From 77f1b3f1f37199553a20f53917638b7e0e68ded5 Mon Sep 17 00:00:00 2001 From: AngelOnFira <14791619+AngelOnFira@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:10:52 +0000 Subject: [PATCH] feat(svc): add servers create endpoint (#740) Will close RVTEE-87, RVTEE-88, RVTEE-89 --- .vscode/rivet.code-workspace | 3 + .../servers/servers_multiple_port_routers.md | 9 + errors/servers/servers_multiple_runtimes.md | 9 + .../servers_must_specify_routing_type.md | 10 + errors/servers/servers_no_port_routers.md | 9 + errors/servers/servers_no_runtime.md | 9 + errors/servers/servers_server_not_found.md | 9 + .../servers_unsupported_host_protocol.md | 10 + fern/definition/cloud/common.yml | 3 + fern/definition/cloud/games/tokens.yml | 14 + fern/definition/servers/__package__.yml | 112 ++ fern/definition/servers/builds.yml | 81 + fern/definition/servers/common.yml | 73 + .../dockerfiles/test-ds-echo/.dockerignore | 25 + .../dockerfiles/test-ds-echo/.gitignore | 21 + .../dockerfiles/test-ds-echo/Cargo.toml | 16 + .../dockerfiles/test-ds-echo/Dockerfile | 18 + .../dockerfiles/test-ds-echo/src/main.rs | 178 ++ .../outputs/test-ds-echo-tag.txt | 1 + infra/default-builds/outputs/test-ds-echo.tar | 3 + infra/tf/modules/secrets/main.tf | 2 +- lib/api-helper/macros/src/lib.rs | 10 +- lib/claims/src/lib.rs | 33 + lib/convert/src/impls/ds.rs | 154 ++ lib/convert/src/impls/mod.rs | 1 + lib/operation/core/Cargo.toml | 2 +- proto/backend/build.proto | 1 + proto/backend/cluster.proto | 1 - proto/backend/ds.proto | 306 +++ proto/backend/faker.proto | 1 + proto/claims.proto | 6 + scripts/default_builds/build.sh | 2 +- sdks/full/go/client/client.go | 3 + sdks/full/go/cloud/games/tokens/client.go | 78 + sdks/full/go/cloud/games/types.go | 32 + sdks/full/go/cloud/types.go | 2 + sdks/full/go/servers/builds.go | 115 ++ sdks/full/go/servers/builds/client.go | 277 +++ sdks/full/go/servers/client/client.go | 373 ++++ sdks/full/go/servers/servers.go | 168 ++ sdks/full/go/servers/types.go | 404 ++++ sdks/full/openapi/openapi.yml | 834 +++++++- sdks/full/openapi_compat/openapi.yml | 769 ++++++- sdks/full/rust-cli/.openapi-generator/FILES | 44 + sdks/full/rust-cli/README.md | 28 + sdks/full/rust-cli/docs/CloudBuildSummary.md | 1 + .../CloudGamesCreateServiceTokenResponse.md | 11 + .../full/rust-cli/docs/CloudGamesTokensApi.md | 31 + sdks/full/rust-cli/docs/ServersApi.md | 133 ++ .../rust-cli/docs/ServersBuildCompression.md | 10 + sdks/full/rust-cli/docs/ServersBuildKind.md | 10 + sdks/full/rust-cli/docs/ServersBuildsApi.md | 101 + .../docs/ServersCreateBuildRequest.md | 17 + .../docs/ServersCreateBuildResponse.md | 14 + .../docs/ServersCreateServerNetworkRequest.md | 12 + .../docs/ServersCreateServerPortRequest.md | 13 + .../docs/ServersCreateServerRequest.md | 19 + .../docs/ServersCreateServerResponse.md | 11 + .../docs/ServersDestroyServerResponse.md | 11 + .../rust-cli/docs/ServersGetServerResponse.md | 11 + .../docs/ServersListBuildsResponse.md | 11 + .../docs/ServersListServersResponse.md | 11 + sdks/full/rust-cli/docs/ServersNetwork.md | 12 + sdks/full/rust-cli/docs/ServersNetworkMode.md | 10 + sdks/full/rust-cli/docs/ServersPort.md | 15 + .../full/rust-cli/docs/ServersPortProtocol.md | 10 + sdks/full/rust-cli/docs/ServersPortRouting.md | 12 + sdks/full/rust-cli/docs/ServersResources.md | 12 + sdks/full/rust-cli/docs/ServersServer.md | 23 + .../src/apis/cloud_games_tokens_api.rs | 44 + sdks/full/rust-cli/src/apis/mod.rs | 2 + sdks/full/rust-cli/src/apis/servers_api.rs | 201 ++ .../rust-cli/src/apis/servers_builds_api.rs | 154 ++ .../src/models/cloud_build_summary.rs | 6 +- ...oud_games_create_service_token_response.rs | 29 + sdks/full/rust-cli/src/models/mod.rs | 40 + .../src/models/servers_build_compression.rs | 39 + .../rust-cli/src/models/servers_build_kind.rs | 39 + .../models/servers_create_build_request.rs | 48 + .../models/servers_create_build_response.rs | 37 + .../servers_create_server_network_request.rs | 31 + .../servers_create_server_port_request.rs | 34 + .../models/servers_create_server_request.rs | 55 + .../models/servers_create_server_response.rs | 28 + .../models/servers_destroy_server_response.rs | 28 + .../src/models/servers_get_server_response.rs | 28 + .../models/servers_list_builds_response.rs | 29 + .../models/servers_list_servers_response.rs | 29 + .../rust-cli/src/models/servers_network.rs | 31 + .../src/models/servers_network_mode.rs | 39 + sdks/full/rust-cli/src/models/servers_port.rs | 40 + .../src/models/servers_port_protocol.rs | 48 + .../src/models/servers_port_routing.rs | 31 + .../rust-cli/src/models/servers_resources.rs | 33 + .../rust-cli/src/models/servers_server.rs | 65 + sdks/full/rust/.openapi-generator/FILES | 44 + sdks/full/rust/README.md | 28 + sdks/full/rust/docs/CloudBuildSummary.md | 1 + .../CloudGamesCreateServiceTokenResponse.md | 11 + sdks/full/rust/docs/CloudGamesTokensApi.md | 31 + sdks/full/rust/docs/ServersApi.md | 133 ++ .../full/rust/docs/ServersBuildCompression.md | 10 + sdks/full/rust/docs/ServersBuildKind.md | 10 + sdks/full/rust/docs/ServersBuildsApi.md | 101 + .../rust/docs/ServersCreateBuildRequest.md | 17 + .../rust/docs/ServersCreateBuildResponse.md | 14 + .../docs/ServersCreateServerNetworkRequest.md | 12 + .../docs/ServersCreateServerPortRequest.md | 13 + .../rust/docs/ServersCreateServerRequest.md | 19 + .../rust/docs/ServersCreateServerResponse.md | 11 + .../rust/docs/ServersDestroyServerResponse.md | 11 + .../rust/docs/ServersGetServerResponse.md | 11 + .../rust/docs/ServersListBuildsResponse.md | 11 + .../rust/docs/ServersListServersResponse.md | 11 + sdks/full/rust/docs/ServersNetwork.md | 12 + sdks/full/rust/docs/ServersNetworkMode.md | 10 + sdks/full/rust/docs/ServersPort.md | 15 + sdks/full/rust/docs/ServersPortProtocol.md | 10 + sdks/full/rust/docs/ServersPortRouting.md | 12 + sdks/full/rust/docs/ServersResources.md | 12 + sdks/full/rust/docs/ServersServer.md | 23 + .../rust/src/apis/cloud_games_tokens_api.rs | 44 + sdks/full/rust/src/apis/mod.rs | 2 + sdks/full/rust/src/apis/servers_api.rs | 201 ++ sdks/full/rust/src/apis/servers_builds_api.rs | 154 ++ .../rust/src/models/cloud_build_summary.rs | 6 +- ...oud_games_create_service_token_response.rs | 29 + sdks/full/rust/src/models/mod.rs | 40 + .../src/models/servers_build_compression.rs | 39 + .../rust/src/models/servers_build_kind.rs | 39 + .../models/servers_create_build_request.rs | 48 + .../models/servers_create_build_response.rs | 37 + .../servers_create_server_network_request.rs | 31 + .../servers_create_server_port_request.rs | 34 + .../models/servers_create_server_request.rs | 55 + .../models/servers_create_server_response.rs | 28 + .../models/servers_destroy_server_response.rs | 28 + .../src/models/servers_get_server_response.rs | 28 + .../models/servers_list_builds_response.rs | 29 + .../models/servers_list_servers_response.rs | 29 + sdks/full/rust/src/models/servers_network.rs | 31 + .../rust/src/models/servers_network_mode.rs | 39 + sdks/full/rust/src/models/servers_port.rs | 40 + .../rust/src/models/servers_port_protocol.rs | 48 + .../rust/src/models/servers_port_routing.rs | 31 + .../full/rust/src/models/servers_resources.rs | 33 + sdks/full/rust/src/models/servers_server.rs | 65 + sdks/full/typescript/archive.tgz | 4 +- sdks/full/typescript/src/Client.ts | 7 + .../resources/common/types/BuildSummary.ts | 2 + .../games/resources/tokens/client/Client.ts | 121 ++ .../games/resources/tokens/types/index.ts | 1 + .../typescript/src/api/resources/index.ts | 1 + .../api/resources/servers/client/Client.ts | 545 +++++ .../src/api/resources/servers/client/index.ts | 1 + .../client/requests/DestroyServerRequest.ts | 10 + .../client/requests/GetServersRequest.ts | 7 + .../servers/client/requests/index.ts | 2 + .../src/api/resources/servers/index.ts | 3 + .../servers/resources/builds/client/Client.ts | 400 ++++ .../servers/resources/builds/client/index.ts | 1 + .../client/requests/GetBuildsRequest.ts | 7 + .../resources/builds/client/requests/index.ts | 1 + .../servers/resources/builds/index.ts | 2 + .../servers/resources/common/index.ts | 1 + .../api/resources/servers/resources/index.ts | 5 + .../typescript/src/core/fetcher/Fetcher.ts | 4 +- .../resources/common/types/BuildSummary.ts | 2 + .../games/resources/tokens/types/index.ts | 1 + .../src/serialization/resources/index.ts | 1 + .../serialization/resources/servers/index.ts | 2 + .../servers/resources/builds/index.ts | 1 + .../servers/resources/common/index.ts | 1 + .../resources/servers/resources/index.ts | 4 + sdks/full/typescript/types/Client.d.ts | 3 + .../resources/common/types/BuildSummary.d.ts | 2 + .../games/resources/tokens/client/Client.d.ts | 10 + .../types/CreateServiceTokenResponse.d.ts | 6 +- .../games/resources/tokens/types/index.d.ts | 1 + .../typescript/types/api/resources/index.d.ts | 1 + .../resources/common/types/BuildSummary.d.ts | 1 + .../games/resources/tokens/types/index.d.ts | 1 + .../types/serialization/resources/index.d.ts | 1 + sdks/runtime/typescript/archive.tgz | 4 +- .../typescript/src/core/fetcher/Fetcher.ts | 6 +- svc/Cargo.lock | 1772 +++++++++++------ svc/Cargo.toml | 7 + svc/api/cloud/Cargo.toml | 2 + svc/api/cloud/src/route/games/builds.rs | 2 + svc/api/cloud/src/route/games/tokens.rs | 25 +- svc/api/cloud/src/route/mod.rs | 3 + svc/api/monolith/Cargo.toml | 1 + svc/api/monolith/Service.toml | 7 + svc/api/monolith/src/route/mod.rs | 4 + svc/api/servers/Cargo.toml | 69 + svc/api/servers/Service.toml | 7 + svc/api/servers/src/assert.rs | 63 + svc/api/servers/src/auth.rs | 386 ++++ svc/api/servers/src/lib.rs | 3 + svc/api/servers/src/main.rs | 5 + svc/api/servers/src/route/builds.rs | 178 ++ svc/api/servers/src/route/mod.rs | 54 + svc/api/servers/src/route/servers.rs | 180 ++ svc/api/servers/tests/basic.rs | 225 +++ svc/api/traefik-provider/Cargo.toml | 8 + .../src/route/game_guard/dynamic_servers.rs | 334 ++++ .../{game_guard.rs => game_guard/job.rs} | 49 +- .../src/route/game_guard/mod.rs | 64 + .../20240717023701_add_build_tags.down.sql | 0 .../20240717023701_add_build_tags.up.sql | 2 + svc/pkg/build/ops/create/src/lib.rs | 17 +- svc/pkg/build/ops/get/src/lib.rs | 44 +- svc/pkg/build/ops/list-for-game/src/lib.rs | 12 +- .../ops/list-for-game/tests/integration.rs | 3 + svc/pkg/build/types/create.proto | 1 + svc/pkg/build/types/list-for-game.proto | 1 + ...20240708222624_game_service_token.down.sql | 0 .../20240708222624_game_service_token.up.sql | 5 + .../ops/game-service-token-create/Cargo.toml | 23 + .../ops/game-service-token-create/README.md | 1 + .../game-service-token-create/Service.toml | 10 + .../ops/game-service-token-create/src/lib.rs | 46 + .../tests/integration.rs | 16 + svc/pkg/ds/db/servers/Service.toml | 7 + .../migrations/20240501133910_init.down.sql | 0 .../migrations/20240501133910_init.up.sql | 76 + svc/pkg/ds/ops/server-create/Cargo.toml | 67 + svc/pkg/ds/ops/server-create/Service.toml | 10 + svc/pkg/ds/ops/server-create/src/lib.rs | 1517 ++++++++++++++ svc/pkg/ds/ops/server-create/src/nomad_job.rs | 613 ++++++ .../ds/ops/server-create/src/oci_config.rs | 316 +++ .../ops/server-create/src/scripts/cleanup.sh | 33 + .../ds/ops/server-create/src/scripts/setup.sh | 71 + .../src/scripts/setup_cni_network.sh | 45 + .../src/scripts/setup_job_runner.sh | 13 + .../src/scripts/setup_oci_bundle.sh | 92 + svc/pkg/ds/ops/server-create/src/seccomp.rs | 481 +++++ svc/pkg/ds/ops/server-create/src/util_job.rs | 26 + .../ds/ops/server-create/tests/integration.rs | 152 ++ .../server-create/tests/print_test_data.rs | 212 ++ svc/pkg/ds/ops/server-delete/Cargo.toml | 29 + svc/pkg/ds/ops/server-delete/Service.toml | 10 + svc/pkg/ds/ops/server-delete/src/lib.rs | 351 ++++ .../ds/ops/server-delete/tests/integration.rs | 6 + svc/pkg/ds/ops/server-get/Cargo.toml | 26 + svc/pkg/ds/ops/server-get/Service.toml | 7 + svc/pkg/ds/ops/server-get/src/lib.rs | 290 +++ .../ds/ops/server-get/tests/integration.rs | 79 + .../ds/ops/server-list-for-game/Cargo.toml | 19 + .../ds/ops/server-list-for-game/Service.toml | 7 + .../ds/ops/server-list-for-game/src/lib.rs | 31 + .../server-list-for-game/tests/integration.rs | 4 + .../msg/ds-nomad-monitor-alloc-plan.proto | 13 + svc/pkg/ds/types/server-create.proto | 36 + svc/pkg/ds/types/server-delete.proto | 14 + svc/pkg/ds/types/server-get.proto | 14 + svc/pkg/ds/types/server-list-for-game.proto | 15 + svc/pkg/ds/util/Cargo.toml | 23 + svc/pkg/ds/util/src/consts.rs | 39 + svc/pkg/ds/util/src/lib.rs | 35 + svc/pkg/ds/util/src/test.rs | 1 + svc/pkg/ds/worker/Cargo.toml | 42 + svc/pkg/ds/worker/Service.toml | 8 + svc/pkg/ds/worker/src/lib.rs | 1 + svc/pkg/ds/worker/src/workers/mod.rs | 104 + .../src/workers/nomad_monitor_alloc_plan.rs | 266 +++ .../src/workers/nomad_monitor_alloc_update.rs | 255 +++ .../src/workers/nomad_monitor_eval_update.rs | 174 ++ svc/pkg/ds/worker/tests/common.rs | 265 +++ .../tests/ds_nomad_monitor_alloc_plan.rs | 13 + svc/pkg/ds/worker/tests/lobby_connectivity.rs | 310 +++ svc/pkg/faker/ops/build/src/lib.rs | 1 + svc/pkg/monolith/standalone/worker/Cargo.toml | 1 + svc/pkg/monolith/standalone/worker/src/lib.rs | 1 + 274 files changed, 17985 insertions(+), 787 deletions(-) create mode 100644 errors/servers/servers_multiple_port_routers.md create mode 100644 errors/servers/servers_multiple_runtimes.md create mode 100644 errors/servers/servers_must_specify_routing_type.md create mode 100644 errors/servers/servers_no_port_routers.md create mode 100644 errors/servers/servers_no_runtime.md create mode 100644 errors/servers/servers_server_not_found.md create mode 100644 errors/servers/servers_unsupported_host_protocol.md create mode 100644 fern/definition/servers/__package__.yml create mode 100644 fern/definition/servers/builds.yml create mode 100644 fern/definition/servers/common.yml create mode 100644 infra/default-builds/dockerfiles/test-ds-echo/.dockerignore create mode 100644 infra/default-builds/dockerfiles/test-ds-echo/.gitignore create mode 100644 infra/default-builds/dockerfiles/test-ds-echo/Cargo.toml create mode 100644 infra/default-builds/dockerfiles/test-ds-echo/Dockerfile create mode 100644 infra/default-builds/dockerfiles/test-ds-echo/src/main.rs create mode 100644 infra/default-builds/outputs/test-ds-echo-tag.txt create mode 100644 infra/default-builds/outputs/test-ds-echo.tar create mode 100644 lib/convert/src/impls/ds.rs create mode 100644 proto/backend/ds.proto create mode 100644 sdks/full/go/servers/builds.go create mode 100644 sdks/full/go/servers/builds/client.go create mode 100644 sdks/full/go/servers/client/client.go create mode 100644 sdks/full/go/servers/servers.go create mode 100644 sdks/full/go/servers/types.go create mode 100644 sdks/full/rust-cli/docs/CloudGamesCreateServiceTokenResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersApi.md create mode 100644 sdks/full/rust-cli/docs/ServersBuildCompression.md create mode 100644 sdks/full/rust-cli/docs/ServersBuildKind.md create mode 100644 sdks/full/rust-cli/docs/ServersBuildsApi.md create mode 100644 sdks/full/rust-cli/docs/ServersCreateBuildRequest.md create mode 100644 sdks/full/rust-cli/docs/ServersCreateBuildResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersCreateServerNetworkRequest.md create mode 100644 sdks/full/rust-cli/docs/ServersCreateServerPortRequest.md create mode 100644 sdks/full/rust-cli/docs/ServersCreateServerRequest.md create mode 100644 sdks/full/rust-cli/docs/ServersCreateServerResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersDestroyServerResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersGetServerResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersListBuildsResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersListServersResponse.md create mode 100644 sdks/full/rust-cli/docs/ServersNetwork.md create mode 100644 sdks/full/rust-cli/docs/ServersNetworkMode.md create mode 100644 sdks/full/rust-cli/docs/ServersPort.md create mode 100644 sdks/full/rust-cli/docs/ServersPortProtocol.md create mode 100644 sdks/full/rust-cli/docs/ServersPortRouting.md create mode 100644 sdks/full/rust-cli/docs/ServersResources.md create mode 100644 sdks/full/rust-cli/docs/ServersServer.md create mode 100644 sdks/full/rust-cli/src/apis/servers_api.rs create mode 100644 sdks/full/rust-cli/src/apis/servers_builds_api.rs create mode 100644 sdks/full/rust-cli/src/models/cloud_games_create_service_token_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_build_compression.rs create mode 100644 sdks/full/rust-cli/src/models/servers_build_kind.rs create mode 100644 sdks/full/rust-cli/src/models/servers_create_build_request.rs create mode 100644 sdks/full/rust-cli/src/models/servers_create_build_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_create_server_network_request.rs create mode 100644 sdks/full/rust-cli/src/models/servers_create_server_port_request.rs create mode 100644 sdks/full/rust-cli/src/models/servers_create_server_request.rs create mode 100644 sdks/full/rust-cli/src/models/servers_create_server_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_destroy_server_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_get_server_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_list_builds_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_list_servers_response.rs create mode 100644 sdks/full/rust-cli/src/models/servers_network.rs create mode 100644 sdks/full/rust-cli/src/models/servers_network_mode.rs create mode 100644 sdks/full/rust-cli/src/models/servers_port.rs create mode 100644 sdks/full/rust-cli/src/models/servers_port_protocol.rs create mode 100644 sdks/full/rust-cli/src/models/servers_port_routing.rs create mode 100644 sdks/full/rust-cli/src/models/servers_resources.rs create mode 100644 sdks/full/rust-cli/src/models/servers_server.rs create mode 100644 sdks/full/rust/docs/CloudGamesCreateServiceTokenResponse.md create mode 100644 sdks/full/rust/docs/ServersApi.md create mode 100644 sdks/full/rust/docs/ServersBuildCompression.md create mode 100644 sdks/full/rust/docs/ServersBuildKind.md create mode 100644 sdks/full/rust/docs/ServersBuildsApi.md create mode 100644 sdks/full/rust/docs/ServersCreateBuildRequest.md create mode 100644 sdks/full/rust/docs/ServersCreateBuildResponse.md create mode 100644 sdks/full/rust/docs/ServersCreateServerNetworkRequest.md create mode 100644 sdks/full/rust/docs/ServersCreateServerPortRequest.md create mode 100644 sdks/full/rust/docs/ServersCreateServerRequest.md create mode 100644 sdks/full/rust/docs/ServersCreateServerResponse.md create mode 100644 sdks/full/rust/docs/ServersDestroyServerResponse.md create mode 100644 sdks/full/rust/docs/ServersGetServerResponse.md create mode 100644 sdks/full/rust/docs/ServersListBuildsResponse.md create mode 100644 sdks/full/rust/docs/ServersListServersResponse.md create mode 100644 sdks/full/rust/docs/ServersNetwork.md create mode 100644 sdks/full/rust/docs/ServersNetworkMode.md create mode 100644 sdks/full/rust/docs/ServersPort.md create mode 100644 sdks/full/rust/docs/ServersPortProtocol.md create mode 100644 sdks/full/rust/docs/ServersPortRouting.md create mode 100644 sdks/full/rust/docs/ServersResources.md create mode 100644 sdks/full/rust/docs/ServersServer.md create mode 100644 sdks/full/rust/src/apis/servers_api.rs create mode 100644 sdks/full/rust/src/apis/servers_builds_api.rs create mode 100644 sdks/full/rust/src/models/cloud_games_create_service_token_response.rs create mode 100644 sdks/full/rust/src/models/servers_build_compression.rs create mode 100644 sdks/full/rust/src/models/servers_build_kind.rs create mode 100644 sdks/full/rust/src/models/servers_create_build_request.rs create mode 100644 sdks/full/rust/src/models/servers_create_build_response.rs create mode 100644 sdks/full/rust/src/models/servers_create_server_network_request.rs create mode 100644 sdks/full/rust/src/models/servers_create_server_port_request.rs create mode 100644 sdks/full/rust/src/models/servers_create_server_request.rs create mode 100644 sdks/full/rust/src/models/servers_create_server_response.rs create mode 100644 sdks/full/rust/src/models/servers_destroy_server_response.rs create mode 100644 sdks/full/rust/src/models/servers_get_server_response.rs create mode 100644 sdks/full/rust/src/models/servers_list_builds_response.rs create mode 100644 sdks/full/rust/src/models/servers_list_servers_response.rs create mode 100644 sdks/full/rust/src/models/servers_network.rs create mode 100644 sdks/full/rust/src/models/servers_network_mode.rs create mode 100644 sdks/full/rust/src/models/servers_port.rs create mode 100644 sdks/full/rust/src/models/servers_port_protocol.rs create mode 100644 sdks/full/rust/src/models/servers_port_routing.rs create mode 100644 sdks/full/rust/src/models/servers_resources.rs create mode 100644 sdks/full/rust/src/models/servers_server.rs create mode 100644 sdks/full/typescript/src/api/resources/servers/client/Client.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/client/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/client/requests/DestroyServerRequest.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/client/requests/GetServersRequest.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/client/requests/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/builds/client/Client.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/builds/client/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/GetBuildsRequest.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/builds/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/common/index.ts create mode 100644 sdks/full/typescript/src/api/resources/servers/resources/index.ts create mode 100644 sdks/full/typescript/src/serialization/resources/servers/index.ts create mode 100644 sdks/full/typescript/src/serialization/resources/servers/resources/builds/index.ts create mode 100644 sdks/full/typescript/src/serialization/resources/servers/resources/common/index.ts create mode 100644 sdks/full/typescript/src/serialization/resources/servers/resources/index.ts create mode 100644 svc/api/servers/Cargo.toml create mode 100644 svc/api/servers/Service.toml create mode 100644 svc/api/servers/src/assert.rs create mode 100644 svc/api/servers/src/auth.rs create mode 100644 svc/api/servers/src/lib.rs create mode 100644 svc/api/servers/src/main.rs create mode 100644 svc/api/servers/src/route/builds.rs create mode 100644 svc/api/servers/src/route/mod.rs create mode 100644 svc/api/servers/src/route/servers.rs create mode 100644 svc/api/servers/tests/basic.rs create mode 100644 svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs rename svc/api/traefik-provider/src/route/{game_guard.rs => game_guard/job.rs} (87%) create mode 100644 svc/api/traefik-provider/src/route/game_guard/mod.rs create mode 100644 svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.down.sql create mode 100644 svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.up.sql create mode 100644 svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.down.sql create mode 100644 svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.up.sql create mode 100644 svc/pkg/cloud/ops/game-service-token-create/Cargo.toml create mode 100644 svc/pkg/cloud/ops/game-service-token-create/README.md create mode 100644 svc/pkg/cloud/ops/game-service-token-create/Service.toml create mode 100644 svc/pkg/cloud/ops/game-service-token-create/src/lib.rs create mode 100644 svc/pkg/cloud/ops/game-service-token-create/tests/integration.rs create mode 100644 svc/pkg/ds/db/servers/Service.toml create mode 100644 svc/pkg/ds/db/servers/migrations/20240501133910_init.down.sql create mode 100644 svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql create mode 100644 svc/pkg/ds/ops/server-create/Cargo.toml create mode 100644 svc/pkg/ds/ops/server-create/Service.toml create mode 100644 svc/pkg/ds/ops/server-create/src/lib.rs create mode 100644 svc/pkg/ds/ops/server-create/src/nomad_job.rs create mode 100644 svc/pkg/ds/ops/server-create/src/oci_config.rs create mode 100644 svc/pkg/ds/ops/server-create/src/scripts/cleanup.sh create mode 100644 svc/pkg/ds/ops/server-create/src/scripts/setup.sh create mode 100644 svc/pkg/ds/ops/server-create/src/scripts/setup_cni_network.sh create mode 100644 svc/pkg/ds/ops/server-create/src/scripts/setup_job_runner.sh create mode 100644 svc/pkg/ds/ops/server-create/src/scripts/setup_oci_bundle.sh create mode 100644 svc/pkg/ds/ops/server-create/src/seccomp.rs create mode 100644 svc/pkg/ds/ops/server-create/src/util_job.rs create mode 100644 svc/pkg/ds/ops/server-create/tests/integration.rs create mode 100644 svc/pkg/ds/ops/server-create/tests/print_test_data.rs create mode 100644 svc/pkg/ds/ops/server-delete/Cargo.toml create mode 100644 svc/pkg/ds/ops/server-delete/Service.toml create mode 100644 svc/pkg/ds/ops/server-delete/src/lib.rs create mode 100644 svc/pkg/ds/ops/server-delete/tests/integration.rs create mode 100644 svc/pkg/ds/ops/server-get/Cargo.toml create mode 100644 svc/pkg/ds/ops/server-get/Service.toml create mode 100644 svc/pkg/ds/ops/server-get/src/lib.rs create mode 100644 svc/pkg/ds/ops/server-get/tests/integration.rs create mode 100644 svc/pkg/ds/ops/server-list-for-game/Cargo.toml create mode 100644 svc/pkg/ds/ops/server-list-for-game/Service.toml create mode 100644 svc/pkg/ds/ops/server-list-for-game/src/lib.rs create mode 100644 svc/pkg/ds/ops/server-list-for-game/tests/integration.rs create mode 100644 svc/pkg/ds/types/msg/ds-nomad-monitor-alloc-plan.proto create mode 100644 svc/pkg/ds/types/server-create.proto create mode 100644 svc/pkg/ds/types/server-delete.proto create mode 100644 svc/pkg/ds/types/server-get.proto create mode 100644 svc/pkg/ds/types/server-list-for-game.proto create mode 100644 svc/pkg/ds/util/Cargo.toml create mode 100644 svc/pkg/ds/util/src/consts.rs create mode 100644 svc/pkg/ds/util/src/lib.rs create mode 100644 svc/pkg/ds/util/src/test.rs create mode 100644 svc/pkg/ds/worker/Cargo.toml create mode 100644 svc/pkg/ds/worker/Service.toml create mode 100644 svc/pkg/ds/worker/src/lib.rs create mode 100644 svc/pkg/ds/worker/src/workers/mod.rs create mode 100644 svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_plan.rs create mode 100644 svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_update.rs create mode 100644 svc/pkg/ds/worker/src/workers/nomad_monitor_eval_update.rs create mode 100644 svc/pkg/ds/worker/tests/common.rs create mode 100644 svc/pkg/ds/worker/tests/ds_nomad_monitor_alloc_plan.rs create mode 100644 svc/pkg/ds/worker/tests/lobby_connectivity.rs diff --git a/.vscode/rivet.code-workspace b/.vscode/rivet.code-workspace index 445d8457b4..34801add3c 100644 --- a/.vscode/rivet.code-workspace +++ b/.vscode/rivet.code-workspace @@ -6,5 +6,8 @@ { "path": "../lib/bolt", }, + { + "path": "../svc", + } ], } diff --git a/errors/servers/servers_multiple_port_routers.md b/errors/servers/servers_multiple_port_routers.md new file mode 100644 index 0000000000..9f610f2295 --- /dev/null +++ b/errors/servers/servers_multiple_port_routers.md @@ -0,0 +1,9 @@ +--- +name = "SERVERS_MULTIPLE_PORT_ROUTERS" +description = "Multiple server port routers were provided." +http_status = 400 +--- + +# Multiple Server Port Routers + +Multiple server port routers were provided. diff --git a/errors/servers/servers_multiple_runtimes.md b/errors/servers/servers_multiple_runtimes.md new file mode 100644 index 0000000000..a2a83356b4 --- /dev/null +++ b/errors/servers/servers_multiple_runtimes.md @@ -0,0 +1,9 @@ +--- +name = "SERVERS_MULTIPLE_RUNTIMES" +description = "Multiple server runtimes were provided." +http_status = 400 +--- + +# Multiple Server Runtimes + +Multiple server runtimes were provided. diff --git a/errors/servers/servers_must_specify_routing_type.md b/errors/servers/servers_must_specify_routing_type.md new file mode 100644 index 0000000000..08f32255cd --- /dev/null +++ b/errors/servers/servers_must_specify_routing_type.md @@ -0,0 +1,10 @@ +--- +name = "SERVERS_MUST_SPECIFY_ROUTING_TYPE" +description = "Must specify routing type." +http_status = 400 +--- + +# Must Specify Routing Type + +Must specify either `game_guard` or `host` routing type. + diff --git a/errors/servers/servers_no_port_routers.md b/errors/servers/servers_no_port_routers.md new file mode 100644 index 0000000000..bbca4f0213 --- /dev/null +++ b/errors/servers/servers_no_port_routers.md @@ -0,0 +1,9 @@ +--- +name = "SERVERS_NO_PORT_ROUTERS" +description = "No port routers were provided." +http_status = 400 +--- + +# No Port Routers + +No port routers were provided. diff --git a/errors/servers/servers_no_runtime.md b/errors/servers/servers_no_runtime.md new file mode 100644 index 0000000000..8490f10210 --- /dev/null +++ b/errors/servers/servers_no_runtime.md @@ -0,0 +1,9 @@ +--- +name = "SERVERS_NO_RUNTIME" +description = "No server runtime were provided." +http_status = 400 +--- + +# No Server Runtime + +No server runtime were provided. diff --git a/errors/servers/servers_server_not_found.md b/errors/servers/servers_server_not_found.md new file mode 100644 index 0000000000..8d0c4189cd --- /dev/null +++ b/errors/servers/servers_server_not_found.md @@ -0,0 +1,9 @@ +--- +name = "SERVERS_SERVER_NOT_FOUND" +description = "Server not found." +http_status = 400 +--- + +# Server Not Found + +Server not found for the given ID. diff --git a/errors/servers/servers_unsupported_host_protocol.md b/errors/servers/servers_unsupported_host_protocol.md new file mode 100644 index 0000000000..7defe2b672 --- /dev/null +++ b/errors/servers/servers_unsupported_host_protocol.md @@ -0,0 +1,10 @@ +--- +name = "SERVERS_UNSUPPORTED_HOST_PROTOCOL" +description = "Unsupported host protocol." +http_status = 400 +--- + +# Unsupported Host Protocol + +Host protocol must use the TCP or UDP protocol. + diff --git a/fern/definition/cloud/common.yml b/fern/definition/cloud/common.yml index c6fcaa547f..84c1734a7e 100644 --- a/fern/definition/cloud/common.yml +++ b/fern/definition/cloud/common.yml @@ -202,6 +202,9 @@ types: complete: docs: Whether or not this build has completely been uploaded. type: boolean + tags: + docs: Tags of this build + type: map CdnSiteSummary: docs: A CDN site summary. diff --git a/fern/definition/cloud/games/tokens.yml b/fern/definition/cloud/games/tokens.yml index f5d851babf..ecdef3b306 100644 --- a/fern/definition/cloud/games/tokens.yml +++ b/fern/definition/cloud/games/tokens.yml @@ -12,6 +12,14 @@ service: game_id: type: uuid response: CreateCloudTokenResponse + createServiceToken: + path: /games/{game_id}/tokens/service + method: POST + docs: Creates a new game service token. + path-parameters: + game_id: + type: uuid + response: CreateCloudTokenResponse types: CreateCloudTokenResponse: @@ -22,3 +30,9 @@ types: Slightly modified to include a description prefix and use Protobufs of JSON. type: string + CreateServiceTokenResponse: + properties: + token: + docs: |- + A JSON Web Token. + type: string diff --git a/fern/definition/servers/__package__.yml b/fern/definition/servers/__package__.yml new file mode 100644 index 0000000000..8dd2b6cb81 --- /dev/null +++ b/fern/definition/servers/__package__.yml @@ -0,0 +1,112 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json + +imports: + commons: common.yml + +service: + auth: true + base-path: /servers + endpoints: + get: + path: /{server_id} + method: GET + docs: Gets a dynamic server. + path-parameters: + server_id: + docs: The id of the server to destroy + type: uuid + response: GetServerResponse + + create: + path: "" + method: POST + docs: Create a new dynamic server. + request: + body: CreateServerRequest + response: CreateServerResponse + + destroy: + path: /{server_id} + method: DELETE + docs: Destroy a dynamic server. + path-parameters: + server_id: + docs: The id of the server to destroy + type: uuid + request: + name: DestroyServerRequest + query-parameters: + override_kill_timeout: + docs: >- + The duration to wait for in milliseconds before killing the server. + This should be used to override the default kill timeout if a faster + time is needed, say for ignoring a graceful shutdown. + type: optional + response: DestroyServerResponse + + list: + path: /list + method: GET + docs: >- + Lists all servers associated with the token used. Can be filtered by + tags in the query string. + request: + name: GetServersRequest + query-parameters: + tags: optional + response: ListServersResponse + +types: + GetServerResponse: + properties: + server: commons.Server + + CreateServerRequest: + properties: + datacenter: + docs: The name ID of the datacenter + type: string + tags: unknown + image_id: uuid + arguments: optional> + environment: optional> + network: CreateServerNetworkRequest + resources: commons.Resources + kill_timeout: + docs: >- + The duration to wait for in milliseconds before killing the server. + This should be set to a safe default, and can be overridden during a + DELETE request if needed. + type: optional + webhook_url: + docs: >- + A url to send to which events from the server running will be sent + type: optional + + CreateServerNetworkRequest: + properties: + mode: optional + ports: map + + CreateServerPortRequest: + properties: + protocol: commons.PortProtocol + internal_port: optional + routing: optional + + CreateServerResponse: + properties: + server: + docs: The server that was created + type: commons.Server + + DestroyServerResponse: + properties: + server_id: + type: uuid + + ListServersResponse: + properties: + servers: + docs: A list of servers for the game associated with the token. + type: list \ No newline at end of file diff --git a/fern/definition/servers/builds.yml b/fern/definition/servers/builds.yml new file mode 100644 index 0000000000..5774e3e3a5 --- /dev/null +++ b/fern/definition/servers/builds.yml @@ -0,0 +1,81 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json + +imports: + commons: ../common.yml + uploadCommons: ../upload/common.yml + cloudCommons: ../cloud/common.yml + +service: + auth: true + base-path: /servers + endpoints: + listBuilds: + path: /builds + method: GET + docs: >- + Lists all builds of the game associated with the token used. Can be + filtered by tags in the query string. + request: + name: GetBuildsRequest + query-parameters: + tags: optional + response: ListBuildsResponse + + prepareBuild: + path: /builds + method: POST + docs: Creates a new game build for the given game. + request: + body: CreateBuildRequest + response: CreateBuildResponse + + completeBuild: + path: /uploads/{upload_id}/complete + method: POST + docs: Marks an upload as complete. + path-parameters: + upload_id: + type: uuid + +types: + ListBuildsResponse: + properties: + builds: + docs: A list of builds for the game associated with the token. + type: list + + CreateBuildRequest: + properties: + display_name: commons.DisplayName + tags: unknown + image_tag: + docs: A tag given to the game build. + type: string + image_file: uploadCommons.PrepareFile + multipart_upload: + type: optional + kind: optional + compression: optional + + CreateBuildResponse: + properties: + build_id: + type: uuid + upload_id: + type: uuid + image_presigned_request: optional + image_presigned_requests: optional> + + BuildKind: + enum: + - value: docker_image + docs: Docker image archive generated by `docker save`. + - value: oci_bundle + docs: OCI-compliant bundle. + + BuildCompression: + enum: + - value: none + docs: None compression. + - value: lz4 + docs: LZ4 compression. Use the minimum compression level. diff --git a/fern/definition/servers/common.yml b/fern/definition/servers/common.yml new file mode 100644 index 0000000000..30dc9d3f90 --- /dev/null +++ b/fern/definition/servers/common.yml @@ -0,0 +1,73 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/fern-api/fern/main/fern.schema.json + +types: + Server: + properties: + server_id: uuid + game_id: uuid + datacenter_id: uuid + cluster_id: uuid + tags: unknown + image_id: uuid + arguments: optional> + environment: optional> + network: Network + resources: Resources + kill_timeout: + docs: >- + The duration to wait for in milliseconds before killing the server. + This should be set to a safe default, and can be overridden during a + DELETE request if needed. + type: optional + create_ts: long + destroy_ts: optional + + Resources: + properties: + cpu: + docs: | + The number of CPU cores in millicores, or 1/1000 of a core. For example, + 1/8 of a core would be 125 millicores, and 1 core would be 1000 + millicores. + type: integer + memory: + docs: The amount of memory in megabytes + type: integer + + Network: + properties: + mode: optional + ports: map + + NetworkMode: + enum: + - bridge + - host + + Port: + properties: + protocol: PortProtocol + internal_port: optional + public_hostname: optional + public_port: optional + routing: PortRouting + + PortProtocol: + enum: + - http + - https + - tcp + - tcp_tls + - udp + + PortRouting: + properties: + game_guard: optional + host: optional + + GameGuardRouting: + properties: {} + + HostRouting: + properties: {} + diff --git a/infra/default-builds/dockerfiles/test-ds-echo/.dockerignore b/infra/default-builds/dockerfiles/test-ds-echo/.dockerignore new file mode 100644 index 0000000000..2c61e2934c --- /dev/null +++ b/infra/default-builds/dockerfiles/test-ds-echo/.dockerignore @@ -0,0 +1,25 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust +# Edit at https://www.toptal.com/developers/gitignore?templates=rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# End of https://www.toptal.com/developers/gitignore/api/rust + +.dockerignore +.gitignore +Dockerfile + diff --git a/infra/default-builds/dockerfiles/test-ds-echo/.gitignore b/infra/default-builds/dockerfiles/test-ds-echo/.gitignore new file mode 100644 index 0000000000..4bc139ed7d --- /dev/null +++ b/infra/default-builds/dockerfiles/test-ds-echo/.gitignore @@ -0,0 +1,21 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust +# Edit at https://www.toptal.com/developers/gitignore?templates=rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# End of https://www.toptal.com/developers/gitignore/api/rust + diff --git a/infra/default-builds/dockerfiles/test-ds-echo/Cargo.toml b/infra/default-builds/dockerfiles/test-ds-echo/Cargo.toml new file mode 100644 index 0000000000..8208e11d7c --- /dev/null +++ b/infra/default-builds/dockerfiles/test-ds-echo/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "test-ds-echo" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1.29", features = ["full"] } +reqwest = "0.11" +anyhow = "1.0" +hyper = { version = "0.14", features = ["server"] } + +[profile.release] +opt-level = 'z' +lto = true +codegen-units = 1 + diff --git a/infra/default-builds/dockerfiles/test-ds-echo/Dockerfile b/infra/default-builds/dockerfiles/test-ds-echo/Dockerfile new file mode 100644 index 0000000000..233a4195cb --- /dev/null +++ b/infra/default-builds/dockerfiles/test-ds-echo/Dockerfile @@ -0,0 +1,18 @@ +FROM clux/muslrust:1.77.2-stable AS build +RUN cargo new --bin /app +WORKDIR /app +COPY Cargo.toml ./ +RUN cargo build --release +RUN rm -r src +COPY ./src ./src +RUN touch src/main.rs && \ + cargo build --release && \ + strip target/x86_64-unknown-linux-musl/release/test-ds-echo + +FROM alpine:latest +COPY --from=build /app/target/x86_64-unknown-linux-musl/release/test-ds-echo /usr/local/bin/app +RUN chmod +x /usr/local/bin/app +RUN adduser -D app +USER app +CMD ["/usr/local/bin/app"] + diff --git a/infra/default-builds/dockerfiles/test-ds-echo/src/main.rs b/infra/default-builds/dockerfiles/test-ds-echo/src/main.rs new file mode 100644 index 0000000000..89427e4508 --- /dev/null +++ b/infra/default-builds/dockerfiles/test-ds-echo/src/main.rs @@ -0,0 +1,178 @@ +use std::{convert::Infallible, env, net::SocketAddr, process::Command}; + +use anyhow::{Context, Result}; +use tokio::{ + io::{AsyncBufReadExt, AsyncWriteExt}, + net::{TcpListener, UdpSocket}, +}; + +#[tokio::main] +async fn main() -> Result<()> { + // Env + let envs: Vec<(String, String)> = env::vars().collect(); + println!("Env:\n{:#?}\n", envs); + + // resolv.conf + let output = Command::new("cat") + .arg("/etc/resolv.conf") + .output() + .expect("Failed to execute command"); + println!( + "resolv.conf:\n{}\n", + String::from_utf8_lossy(&output.stdout) + ); + + // TEMP: Expose dev port + tokio::spawn(with_select_term(echo_http_server(28234))); + + // TODO: Add back + // Echo servers (bridge networking) + // if let Ok(http_port) = env::var("PORT_test_http") { + // let http_port: u16 = http_port.parse()?; + // tokio::spawn(with_select_term(echo_http_server(http_port))); + // } + // + // if let Ok(tcp_port) = env::var("PORT_test_tcp") { + // let tcp_port: u16 = tcp_port.parse()?; + // tokio::spawn(with_select_term(echo_tcp_server(tcp_port))); + // } + // + // if let Ok(udp_port) = env::var("PORT_test_udp") { + // let udp_port: u16 = udp_port.parse()?; + // tokio::spawn(with_select_term(echo_udp_server(udp_port))); + // } + // + // // Echo servers (host networking) + // if let Ok(http_port) = env::var("HOST_PORT_HTTP") { + // let http_port: u16 = http_port.parse()?; + // tokio::spawn(with_select_term(echo_http_server(http_port))); + // } + // + // if let Ok(tcp_port) = env::var("HOST_PORT_TCP") { + // let tcp_port: u16 = tcp_port.parse()?; + // tokio::spawn(with_select_term(echo_tcp_server(tcp_port))); + // } + // + // if let Ok(udp_port) = env::var("HOST_PORT_UDP") { + // let udp_port: u16 = udp_port.parse()?; + // tokio::spawn(with_select_term(echo_udp_server(udp_port))); + // } + + // Lobby ready + // lobby_ready().await?; + + // Wait indefinitely + println!("Waiting indefinitely..."); + wait_term().await?; + println!("Ctrl+C pressed. Exiting main..."); + + Ok(()) +} + +// async fn lobby_ready() -> Result<()> { +// let url = format!( +// "{}/matchmaker/lobbies/ready", +// env::var("RIVET_API_ENDPOINT").context("RIVET_API_ENDPOINT")? +// ); +// let token = env::var("RIVET_TOKEN").context("RIVET_TOKEN")?; +// +// let client = reqwest::Client::new(); +// client +// .post(&url) +// .header("Content-Type", "application/json") +// .header("Authorization", format!("Bearer {}", token)) +// .send() +// .await?; +// +// println!("Success, waiting indefinitely"); +// Ok(()) +// } + +/// Waits for the SIGTERM signal. +async fn wait_term() -> Result<()> { + use tokio::signal::unix::{signal, SignalKind}; + + signal(SignalKind::terminate()) + .expect("Failed to set up SIGTERM handler") + .recv() + .await; + + Ok(()) +} + +/// Waits until future exits or term. +async fn with_select_term(future: impl std::future::Future>) -> Result<()> { + tokio::select! { + result = future => result, + _ = wait_term() => { + println!("Ctrl+C pressed. Exiting..."); + Ok(()) + }, + } +} + +async fn echo_http_server(port: u16) -> Result<()> { + use hyper::{ + service::{make_service_fn, service_fn}, + Body, Request, Response, Server, + }; + + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + println!("HTTP: {}", port); + + async fn echo(req: Request) -> Result, Infallible> { + Ok(Response::new(req.into_body())) + } + + let make_service = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(echo)) }); + Server::bind(&addr) + .serve(make_service) + .await + .expect("hyper server"); + + Ok(()) +} + +async fn echo_tcp_server(port: u16) -> Result<()> { + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + println!("TCP: {}", port); + + let listener = TcpListener::bind(&addr).await.context("bind failed")?; + loop { + let (socket, _) = listener.accept().await.context("accept failed")?; + tokio::spawn(async move { + let mut reader = tokio::io::BufReader::new(socket); + let mut line = String::new(); + loop { + let bytes_read = reader.read_line(&mut line).await.expect("read line failed"); + if bytes_read == 0 { + break; + } + + // Echo the line + reader + .get_mut() + .write_all(format!("{line}\n").as_bytes()) + .await + .expect("write failed"); + reader.get_mut().flush().await.expect("flush failed"); + line.clear(); + } + }); + } +} + +async fn echo_udp_server(port: u16) -> Result<()> { + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + println!("UDP: {}", port); + + let socket = UdpSocket::bind(&addr).await?; + let mut buf = vec![0u8; 1024]; + loop { + let (size, src) = socket.recv_from(&mut buf).await?; + let data = String::from_utf8_lossy(&buf[..size]); + println!("Received data: {}", data); + + socket.send_to(&buf[..size], &src).await?; + } +} diff --git a/infra/default-builds/outputs/test-ds-echo-tag.txt b/infra/default-builds/outputs/test-ds-echo-tag.txt new file mode 100644 index 0000000000..e41e022713 --- /dev/null +++ b/infra/default-builds/outputs/test-ds-echo-tag.txt @@ -0,0 +1 @@ +test-ds-echo:1719995033 \ No newline at end of file diff --git a/infra/default-builds/outputs/test-ds-echo.tar b/infra/default-builds/outputs/test-ds-echo.tar new file mode 100644 index 0000000000..fe0434cce4 --- /dev/null +++ b/infra/default-builds/outputs/test-ds-echo.tar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a75331b6eb2cab6edf6fc07ac60683e7f73aa8d989fbe6a2e8e3550806ef237b +size 10483712 diff --git a/infra/tf/modules/secrets/main.tf b/infra/tf/modules/secrets/main.tf index 4124127005..b42ba22d00 100644 --- a/infra/tf/modules/secrets/main.tf +++ b/infra/tf/modules/secrets/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { external = { source = "hashicorp/external" - version = "2.3.1" + version = "2.3.3" } } } diff --git a/lib/api-helper/macros/src/lib.rs b/lib/api-helper/macros/src/lib.rs index a242152305..c456fd794b 100644 --- a/lib/api-helper/macros/src/lib.rs +++ b/lib/api-helper/macros/src/lib.rs @@ -381,6 +381,7 @@ impl Parse for RequestPath { enum RequestPathSegment { LitStr(syn::LitStr), Type(syn::Type), + Empty, } impl Parse for RequestPathSegment { @@ -389,8 +390,11 @@ impl Parse for RequestPathSegment { if let Ok(lit) = fork.parse::() { input.advance_to(&fork); - - Ok(RequestPathSegment::LitStr(lit)) + if lit.value().is_empty() { + Ok(RequestPathSegment::Empty) + } else { + Ok(RequestPathSegment::LitStr(lit)) + } } else { Ok(RequestPathSegment::Type(input.parse()?)) } @@ -429,6 +433,7 @@ impl RequestPathSegment { }; } } + RequestPathSegment::Empty => quote! {}, // Handle empty path segment } } } @@ -492,6 +497,7 @@ impl Endpoint { .map(|segment| match segment { RequestPathSegment::LitStr(lit) => lit.value(), RequestPathSegment::Type(_) => "{}".to_string(), + RequestPathSegment::Empty => "".to_string(), // Handle empty path }) .collect::>() .join("/"); diff --git a/lib/claims/src/lib.rs b/lib/claims/src/lib.rs index b8885ab314..1f48f511c3 100644 --- a/lib/claims/src/lib.rs +++ b/lib/claims/src/lib.rs @@ -363,6 +363,21 @@ pub mod ent { }) } } + + #[derive(Clone, Debug)] + pub struct GameService { + pub game_id: Uuid, + } + + impl TryFrom<&schema::entitlement::GameService> for GameService { + type Error = GlobalError; + + fn try_from(value: &schema::entitlement::GameService) -> GlobalResult { + Ok(GameService { + game_id: unwrap!(value.game_id).as_uuid(), + }) + } + } } pub trait ClaimsDecode { @@ -388,6 +403,7 @@ pub trait ClaimsDecode { fn as_access_token(&self) -> GlobalResult; fn as_provisioned_server(&self) -> GlobalResult; fn as_opengb_db(&self) -> GlobalResult; + fn as_game_service(&self) -> GlobalResult; } impl ClaimsDecode for schema::Claims { @@ -685,6 +701,22 @@ impl ClaimsDecode for schema::Claims { )) .and_then(std::convert::identity) } + + fn as_game_service(&self) -> GlobalResult { + self.entitlements + .iter() + .find_map(|ent| match &ent.kind { + Some(schema::entitlement::Kind::GameService(ent)) => { + Some(ent::GameService::try_from(ent)) + } + _ => None, + }) + .ok_or(err_code!( + CLAIMS_MISSING_ENTITLEMENT, + entitlements = "GameService" + )) + .and_then(std::convert::identity) + } } pub trait EntitlementTag { @@ -713,6 +745,7 @@ impl EntitlementTag for schema::Entitlement { schema::entitlement::Kind::AccessToken(_) => 16, schema::entitlement::Kind::ProvisionedServer(_) => 17, schema::entitlement::Kind::OpengbDb(_) => 18, + schema::entitlement::Kind::GameService(_) => 19, }) } } diff --git a/lib/convert/src/impls/ds.rs b/lib/convert/src/impls/ds.rs new file mode 100644 index 0000000000..86535fc0bc --- /dev/null +++ b/lib/convert/src/impls/ds.rs @@ -0,0 +1,154 @@ +use std::collections::HashMap; + +use proto::backend; +use rivet_api::models; +use rivet_operation::prelude::*; + +use crate::{ApiFrom, ApiInto, ApiTryFrom, ApiTryInto}; +use serde_json::{json, to_value}; + +impl ApiTryFrom for models::ServersServer { + type Error = GlobalError; + fn api_try_from(value: backend::ds::Server) -> GlobalResult { + Ok(models::ServersServer { + cluster_id: unwrap!(value.cluster_id).as_uuid(), + create_ts: value.create_ts, + datacenter_id: unwrap!(value.datacenter_id).as_uuid(), + destroy_ts: value.destroy_ts, + game_id: unwrap!(value.game_id).as_uuid(), + kill_timeout: Some(value.kill_timeout_ms), + tags: Some(to_value(value.tags).unwrap()), + resources: Box::new(unwrap!(value.resources).api_into()), + server_id: unwrap!(value.server_id).as_uuid(), + arguments: Some(value.args), + environment: Some(value.environment), + image_id: unwrap!(value.image_id).as_uuid(), + network: Box::new(models::ServersNetwork { + mode: Some( + unwrap!(backend::ds::NetworkMode::from_i32(value.network_mode)).api_into(), + ), + ports: value + .network_ports + .into_iter() + .map(|(s, p)| Ok((s, p.api_try_into()?))) + .collect::>>()?, + }), + }) + } +} + +impl ApiFrom for backend::ds::ServerResources { + fn api_from(value: models::ServersResources) -> backend::ds::ServerResources { + backend::ds::ServerResources { + cpu_millicores: value.cpu, + memory_mib: value.memory, + } + } +} + +impl ApiFrom for models::ServersResources { + fn api_from(value: backend::ds::ServerResources) -> models::ServersResources { + models::ServersResources { + cpu: value.cpu_millicores, + memory: value.memory_mib, + } + } +} + +impl ApiFrom for backend::ds::NetworkMode { + fn api_from(value: models::ServersNetworkMode) -> backend::ds::NetworkMode { + match value { + models::ServersNetworkMode::Bridge => backend::ds::NetworkMode::Bridge, + models::ServersNetworkMode::Host => backend::ds::NetworkMode::Host, + } + } +} + +impl ApiFrom for models::ServersNetworkMode { + fn api_from(value: backend::ds::NetworkMode) -> models::ServersNetworkMode { + match value { + backend::ds::NetworkMode::Bridge => models::ServersNetworkMode::Bridge, + backend::ds::NetworkMode::Host => models::ServersNetworkMode::Host, + } + } +} + +impl ApiTryFrom for models::ServersPort { + type Error = GlobalError; + + fn api_try_from(value: backend::ds::Port) -> GlobalResult { + let protocol = match unwrap!(&value.routing) { + backend::ds::port::Routing::GameGuard(x) => { + unwrap!(backend::ds::GameGuardProtocol::from_i32(x.protocol)).api_into() + } + backend::ds::port::Routing::Host(x) => { + unwrap!(backend::ds::HostProtocol::from_i32(x.protocol)).api_into() + } + }; + + let routing = models::ServersPortRouting { + game_guard: if let Some(backend::ds::port::Routing::GameGuard(_)) = &value.routing { + Some(json!({})) + } else { + None + }, + host: if let Some(backend::ds::port::Routing::Host(_)) = &value.routing { + Some(json!({})) + } else { + None + }, + }; + + Ok(models::ServersPort { + protocol, + internal_port: value.internal_port, + public_hostname: value.public_hostname, + public_port: value.public_port, + routing: Box::new(routing), + }) + } +} + +impl ApiFrom for backend::ds::GameGuardProtocol { + fn api_from(value: models::ServersPortProtocol) -> backend::ds::GameGuardProtocol { + match value { + models::ServersPortProtocol::Udp => backend::ds::GameGuardProtocol::Udp, + models::ServersPortProtocol::Tcp => backend::ds::GameGuardProtocol::Tcp, + models::ServersPortProtocol::Http => backend::ds::GameGuardProtocol::Http, + models::ServersPortProtocol::Https => backend::ds::GameGuardProtocol::Https, + models::ServersPortProtocol::TcpTls => backend::ds::GameGuardProtocol::TcpTls, + } + } +} + +impl ApiFrom for models::ServersPortProtocol { + fn api_from(value: backend::ds::GameGuardProtocol) -> models::ServersPortProtocol { + match value { + backend::ds::GameGuardProtocol::Udp => models::ServersPortProtocol::Udp, + backend::ds::GameGuardProtocol::Tcp => models::ServersPortProtocol::Tcp, + backend::ds::GameGuardProtocol::Http => models::ServersPortProtocol::Http, + backend::ds::GameGuardProtocol::Https => models::ServersPortProtocol::Https, + backend::ds::GameGuardProtocol::TcpTls => models::ServersPortProtocol::TcpTls, + } + } +} + +impl ApiTryFrom for backend::ds::HostProtocol { + type Error = GlobalError; + fn api_try_from(value: models::ServersPortProtocol) -> GlobalResult { + Ok(match value { + models::ServersPortProtocol::Udp => backend::ds::HostProtocol::HostUdp, + models::ServersPortProtocol::Tcp => backend::ds::HostProtocol::HostTcp, + _ => bail_with!(SERVERS_UNSUPPORTED_HOST_PROTOCOL), + }) + } +} + +impl ApiFrom for models::ServersPortProtocol { + fn api_from(value: backend::ds::HostProtocol) -> models::ServersPortProtocol { + match value { + backend::ds::HostProtocol::HostUdp => models::ServersPortProtocol::Udp, + backend::ds::HostProtocol::HostTcp => models::ServersPortProtocol::Tcp, + } + } +} diff --git a/lib/convert/src/impls/mod.rs b/lib/convert/src/impls/mod.rs index 9c981bf62c..f2fc7172c6 100644 --- a/lib/convert/src/impls/mod.rs +++ b/lib/convert/src/impls/mod.rs @@ -12,6 +12,7 @@ pub mod group; pub mod identity; pub mod kv; pub mod portal; +pub mod ds; pub mod user; impl ApiFrom for new_models::ValidationError { diff --git a/lib/operation/core/Cargo.toml b/lib/operation/core/Cargo.toml index 8af04240d3..aa2c3eb1eb 100644 --- a/lib/operation/core/Cargo.toml +++ b/lib/operation/core/Cargo.toml @@ -21,7 +21,7 @@ rivet-connection = { path = "../../connection" } rivet-operation-macros = { path = "../macros" } rivet-pools = { path = "../../pools" } rivet-util = { path = "../../util/core" } -serde_json = "1.0" +serde_json = { version = "1.0", features = ["raw_value"] } thiserror = "1.0" tokio = { version = "1.29", features = ["full", "tracing"] } tracing = "0.1" diff --git a/proto/backend/build.proto b/proto/backend/build.proto index e9938093c7..30097028e0 100644 --- a/proto/backend/build.proto +++ b/proto/backend/build.proto @@ -23,5 +23,6 @@ message Build { int64 create_ts = 5; BuildKind kind = 7; BuildCompression compression = 8; + map tags = 9; } diff --git a/proto/backend/cluster.proto b/proto/backend/cluster.proto index 8c901f555a..a6c28f6e5a 100644 --- a/proto/backend/cluster.proto +++ b/proto/backend/cluster.proto @@ -3,7 +3,6 @@ syntax = "proto3"; package rivet.backend.cluster; import "proto/common.proto"; -import "proto/backend/net.proto"; message Cluster { rivet.common.Uuid cluster_id = 1; diff --git a/proto/backend/ds.proto b/proto/backend/ds.proto new file mode 100644 index 0000000000..fa0dba6592 --- /dev/null +++ b/proto/backend/ds.proto @@ -0,0 +1,306 @@ +syntax = "proto3"; + +package rivet.backend.ds; + +import "proto/common.proto"; +import "proto/backend/captcha.proto"; +import "proto/backend/region.proto"; + +message Server { + rivet.common.Uuid server_id = 1; + rivet.common.Uuid game_id = 2; + rivet.common.Uuid datacenter_id = 3; + rivet.common.Uuid cluster_id = 4; + map tags = 5; + rivet.backend.ds.ServerResources resources = 6; + int64 kill_timeout_ms = 7; + optional string webhook_url = 8; + int64 create_ts = 9; + optional int64 destroy_ts = 10; + rivet.common.Uuid image_id = 11; + repeated string args = 12; + NetworkMode network_mode = 13; + map environment = 14; + map network_ports = 15; + +} + +message ServerResources { + int32 cpu_millicores = 1; + int32 memory_mib = 2; +} + +enum NetworkMode { + BRIDGE = 0; + HOST = 1; +} + +message Port { + // Null when using host networking since one is automatially assigned + optional int32 internal_port = 1; + optional string public_hostname = 2; + optional int32 public_port = 3; + + oneof routing { + GameGuardRouting game_guard = 101; + HostRouting host = 102; + } +} + +message GameGuardRouting { + GameGuardProtocol protocol = 1; +} + +enum GameGuardProtocol { + HTTP = 0; + HTTPS = 1; + TCP = 2; + TCP_TLS = 3; + UDP = 4; +} + +message HostRouting { + HostProtocol protocol = 1; +} + +// TODO: Rename to TCP & UDP +enum HostProtocol { + HOST_TCP = 0; + HOST_UDP = 1; +} + + +// +// +// +// // MARK: Game Config +// message GameConfig { +// bool host_networking_enabled = 1; +// bool root_user_enabled = 2; +// } +// +// // MARK: Game Namespace Config +// message NamespaceConfig { +// uint32 lobby_count_max = 1; +// uint32 max_players_per_client = 2; +// uint32 max_players_per_client_vpn = 3; +// uint32 max_players_per_client_proxy = 4; +// uint32 max_players_per_client_tor = 5; +// uint32 max_players_per_client_hosting = 6; +// } +// +// // MARK: Game Version Config +// message VersionConfig { +// repeated LobbyGroup lobby_groups = 1; +// +// optional rivet.backend.captcha.CaptchaConfig captcha = 2; +// } +// +// message LobbyGroup { +// message Region { +// rivet.common.Uuid region_id = 1; +// string tier_name_id = 2; +// IdleLobbies idle_lobbies = 3; +// } +// +// message IdleLobbies { +// uint32 min_idle_lobbies = 1; +// uint32 max_idle_lobbies = 2; +// } +// +// message Actions { +// optional FindConfig find = 1; +// optional JoinConfig join = 2; +// optional CreateConfig create = 3; +// } +// +// string name_id = 1; +// +// repeated Region regions = 101; +// uint32 max_players_normal = 102; +// uint32 max_players_direct = 103; +// uint32 max_players_party = 104; +// bool listable = 105; +// bool taggable = 106; +// bool allow_dynamic_max_players = 107; +// +// LobbyRuntime runtime = 201; +// +// optional Actions actions = 301; +// } +// +// message LobbyRuntime { +// enum NetworkMode { +// BRIDGE = 0; +// HOST = 1; +// } +// +// // Should be named "PortProtocol" +// enum ProxyProtocol { +// HTTP = 0; +// HTTPS = 1; +// TCP = 3; +// TCP_TLS = 4; +// UDP = 2; +// } +// +// enum ProxyKind { +// GAME_GUARD = 0; +// NONE = 1; +// } +// +// message PortRange { +// uint32 min = 1; +// uint32 max = 2; +// } +// +// message Port { +// string label = 1; +// +// // Only applicable to `ProxyProtocol::HTTP` and `ProxyProtocol::HTTP`. +// optional uint32 target_port = 2; +// +// // Only applicable to `ProxyProtocol::UDP` and `ProxyProtocol::TCP` when `proxy_kind` is `ProxyKind::GameGuard`. +// optional PortRange port_range = 4; +// +// ProxyProtocol proxy_protocol = 3; +// +// ProxyKind proxy_kind = 5; +// } +// +// message EnvVar { +// string key = 1; +// string value = 2; +// } +// +// message Docker { +// rivet.common.Uuid build_id = 1; +// repeated string args = 2; +// repeated EnvVar env_vars = 4; +// NetworkMode network_mode = 5; +// repeated Port ports = 3; +// } +// +// oneof runtime { +// Docker docker = 201; +// }; +// } +// +// enum IdentityRequirement { +// NONE = 0; +// GUEST = 1; +// REGISTERED = 2; +// } +// +// message VerificationConfig { +// string url = 1; +// map headers = 2; +// } +// +// message FindConfig { +// bool enabled = 1; +// IdentityRequirement identity_requirement = 2; +// optional VerificationConfig verification = 3; +// } +// +// message JoinConfig { +// bool enabled = 1; +// IdentityRequirement identity_requirement = 2; +// optional VerificationConfig verification = 3; +// } +// +// message CreateConfig { +// bool enabled = 1; +// IdentityRequirement identity_requirement = 2; +// optional VerificationConfig verification = 3; +// +// bool enable_public = 4; +// bool enable_private = 5; +// +// optional uint64 max_lobbies_per_identity = 6; +// } +// +// // MARK: Game Version Config Context +// // Context required to publish a new version. +// message VersionConfigCtx { +// repeated LobbyGroupCtx lobby_groups = 1; +// } +// +// message LobbyGroupCtx { +// LobbyRuntimeCtx runtime = 101; +// } +// +// message LobbyRuntimeCtx { +// message Docker { +// optional rivet.common.Uuid job_template_id = 1 [deprecated = true]; +// } +// +// oneof runtime { +// Docker docker = 1; +// }; +// } +// +// // MARK: Game Version Config Meta +// // Metadata about a given configuration generated after publishing. +// message VersionConfigMeta { +// repeated LobbyGroupMeta lobby_groups = 1; +// } +// +// message LobbyGroupMeta { +// // The indexes of `LobbyGroupMeta` and `LobbyGroupConfig` returned by `game-version-get` line up, so +// // fetching lobby group config via `lobby_group_id` is done via zipping. +// rivet.common.Uuid lobby_group_id = 1; +// +// LobbyRuntimeMeta runtime = 101; +// } +// +// message LobbyRuntimeMeta { +// message Docker { +// optional rivet.common.Uuid job_template_id = 1 [deprecated = true]; +// } +// +// oneof runtime { +// Docker docker = 201; +// }; +// } +// +// // MARK: Lobby State +// message Lobby { +// enum Publicity { +// PUBLIC = 0; +// PRIVATE = 1; +// } +// +// reserved 10; +// +// rivet.common.Uuid lobby_id = 1; +// rivet.common.Uuid lobby_group_id = 2; +// rivet.common.Uuid region_id = 3; +// rivet.common.Uuid token_session_id = 4; +// int64 create_ts = 5; +// optional int64 ready_ts = 14; +// optional int64 stop_ts = 13; +// optional rivet.common.Uuid run_id = 6; +// bool is_closed = 11; +// rivet.common.Uuid namespace_id = 9; +// optional rivet.common.Uuid create_ray_id = 12; +// optional rivet.common.Uuid creator_user_id = 15; +// bool is_custom = 16; +// Publicity publicity = 17; +// +// uint32 max_players_normal = 101; +// uint32 max_players_direct = 102; +// uint32 max_players_party = 103; +// } +// +// // MARK: Player State +// message Player { +// rivet.common.Uuid player_id = 1; +// rivet.common.Uuid lobby_id = 2; +// int64 create_ts = 3; +// optional int64 register_ts = 4; +// optional int64 remove_ts = 5; +// rivet.common.Uuid token_session_id = 6; +// rivet.common.Uuid create_ray_id = 7; +// } +// diff --git a/proto/backend/faker.proto b/proto/backend/faker.proto index 78c32902d6..770bf3997c 100644 --- a/proto/backend/faker.proto +++ b/proto/backend/faker.proto @@ -11,5 +11,6 @@ enum Image { MM_LOBBY_AUTO_READY = 2; MM_LOBBY_ECHO = 3; MM_PLAYER_CONNECT = 4; + DS_ECHO = 5; } diff --git a/proto/claims.proto b/proto/claims.proto index 854a27bffd..684311a610 100644 --- a/proto/claims.proto +++ b/proto/claims.proto @@ -119,6 +119,11 @@ message Entitlement { string db_name = 2; } + message GameService { + // The game ID that the server will be running. + rivet.common.Uuid game_id = 1; + } + oneof kind { Refresh refresh = 1; User user = 2; @@ -137,6 +142,7 @@ message Entitlement { AccessToken access_token = 16; ProvisionedServer provisioned_server = 17; OpenGbDb opengb_db = 18; + GameService game_service = 19; } reserved 13; diff --git a/scripts/default_builds/build.sh b/scripts/default_builds/build.sh index 3da69f0cba..01e8d5bad8 100755 --- a/scripts/default_builds/build.sh +++ b/scripts/default_builds/build.sh @@ -17,7 +17,7 @@ for build in $builds; do echo echo "> $build" echo " * Building" - docker build -t "$image" "$build_path" + docker build --platform linux/amd64 -t "$image" "$build_path" echo " * Saving" docker image save --output "infra/default-builds/outputs/${build}.tar" "$image" diff --git a/sdks/full/go/client/client.go b/sdks/full/go/client/client.go index 7288b5569f..b02b1e5203 100644 --- a/sdks/full/go/client/client.go +++ b/sdks/full/go/client/client.go @@ -15,6 +15,7 @@ import ( matchmakerclient "sdk/matchmaker/client" portalclient "sdk/portal/client" provisionclient "sdk/provision/client" + serversclient "sdk/servers/client" ) type Client struct { @@ -28,6 +29,7 @@ type Client struct { Identity *identityclient.Client Kv *kvclient.Client Provision *provisionclient.Client + Servers *serversclient.Client Auth *authclient.Client Job *jobclient.Client Matchmaker *matchmakerclient.Client @@ -49,6 +51,7 @@ func NewClient(opts ...core.ClientOption) *Client { Identity: identityclient.NewClient(opts...), Kv: kvclient.NewClient(opts...), Provision: provisionclient.NewClient(opts...), + Servers: serversclient.NewClient(opts...), Auth: authclient.NewClient(opts...), Job: jobclient.NewClient(opts...), Matchmaker: matchmakerclient.NewClient(opts...), diff --git a/sdks/full/go/cloud/games/tokens/client.go b/sdks/full/go/cloud/games/tokens/client.go index 79ae853bd0..73865dcc1a 100644 --- a/sdks/full/go/cloud/games/tokens/client.go +++ b/sdks/full/go/cloud/games/tokens/client.go @@ -111,3 +111,81 @@ func (c *Client) CreateCloudToken(ctx context.Context, gameId uuid.UUID) (*games } return response, nil } + +// Creates a new game service token. +func (c *Client) CreateServiceToken(ctx context.Context, gameId uuid.UUID) (*games.CreateCloudTokenResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := fmt.Sprintf(baseURL+"/"+"cloud/games/%v/tokens/service", gameId) + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *games.CreateCloudTokenResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} diff --git a/sdks/full/go/cloud/games/types.go b/sdks/full/go/cloud/games/types.go index 7943c6ed4b..729a89c395 100644 --- a/sdks/full/go/cloud/games/types.go +++ b/sdks/full/go/cloud/games/types.go @@ -3,7 +3,9 @@ package games import ( + json "encoding/json" fmt "fmt" + core "sdk/core" ) type GetGameByIdRequest struct { @@ -63,3 +65,33 @@ func NewBuildKindFromString(s string) (BuildKind, error) { func (b BuildKind) Ptr() *BuildKind { return &b } + +type CreateServiceTokenResponse struct { + // A JSON Web Token. + Token string `json:"token"` + + _rawJSON json.RawMessage +} + +func (c *CreateServiceTokenResponse) UnmarshalJSON(data []byte) error { + type unmarshaler CreateServiceTokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateServiceTokenResponse(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateServiceTokenResponse) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} diff --git a/sdks/full/go/cloud/types.go b/sdks/full/go/cloud/types.go index 6af5695e19..8e99c58772 100644 --- a/sdks/full/go/cloud/types.go +++ b/sdks/full/go/cloud/types.go @@ -114,6 +114,8 @@ type BuildSummary struct { ContentLength int64 `json:"content_length"` // Whether or not this build has completely been uploaded. Complete bool `json:"complete"` + // Tags of this build + Tags map[string]string `json:"tags,omitempty"` _rawJSON json.RawMessage } diff --git a/sdks/full/go/servers/builds.go b/sdks/full/go/servers/builds.go new file mode 100644 index 0000000000..ab0cb6f321 --- /dev/null +++ b/sdks/full/go/servers/builds.go @@ -0,0 +1,115 @@ +// This file was auto-generated by Fern from our API Definition. + +package servers + +import ( + json "encoding/json" + fmt "fmt" + uuid "github.com/google/uuid" + sdk "sdk" + cloud "sdk/cloud" + core "sdk/core" + upload "sdk/upload" +) + +type GetBuildsRequest struct { + Tags *string `json:"-"` +} + +type CreateBuildRequest struct { + DisplayName sdk.DisplayName `json:"display_name"` + Tags interface{} `json:"tags,omitempty"` + // A tag given to the game build. + ImageTag string `json:"image_tag"` + ImageFile *upload.PrepareFile `json:"image_file,omitempty"` + MultipartUpload *bool `json:"multipart_upload,omitempty"` + Kind *BuildKind `json:"kind,omitempty"` + Compression *BuildCompression `json:"compression,omitempty"` + + _rawJSON json.RawMessage +} + +func (c *CreateBuildRequest) UnmarshalJSON(data []byte) error { + type unmarshaler CreateBuildRequest + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateBuildRequest(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateBuildRequest) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} + +type CreateBuildResponse struct { + BuildId uuid.UUID `json:"build_id"` + UploadId uuid.UUID `json:"upload_id"` + ImagePresignedRequest *upload.PresignedRequest `json:"image_presigned_request,omitempty"` + ImagePresignedRequests []*upload.PresignedRequest `json:"image_presigned_requests,omitempty"` + + _rawJSON json.RawMessage +} + +func (c *CreateBuildResponse) UnmarshalJSON(data []byte) error { + type unmarshaler CreateBuildResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateBuildResponse(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateBuildResponse) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} + +type ListBuildsResponse struct { + // A list of builds for the game associated with the token. + Builds []*cloud.BuildSummary `json:"builds,omitempty"` + + _rawJSON json.RawMessage +} + +func (l *ListBuildsResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListBuildsResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListBuildsResponse(value) + l._rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListBuildsResponse) String() string { + if len(l._rawJSON) > 0 { + if value, err := core.StringifyJSON(l._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} diff --git a/sdks/full/go/servers/builds/client.go b/sdks/full/go/servers/builds/client.go new file mode 100644 index 0000000000..84427f8ef1 --- /dev/null +++ b/sdks/full/go/servers/builds/client.go @@ -0,0 +1,277 @@ +// This file was auto-generated by Fern from our API Definition. + +package builds + +import ( + bytes "bytes" + context "context" + json "encoding/json" + errors "errors" + fmt "fmt" + uuid "github.com/google/uuid" + io "io" + http "net/http" + url "net/url" + sdk "sdk" + core "sdk/core" + servers "sdk/servers" +) + +type Client struct { + baseURL string + caller *core.Caller + header http.Header +} + +func NewClient(opts ...core.ClientOption) *Client { + options := core.NewClientOptions() + for _, opt := range opts { + opt(options) + } + return &Client{ + baseURL: options.BaseURL, + caller: core.NewCaller(options.HTTPClient), + header: options.ToHeader(), + } +} + +// Lists all builds of the game associated with the token used. Can be filtered by tags in the query string. +func (c *Client) ListBuilds(ctx context.Context, request *servers.GetBuildsRequest) (*servers.ListBuildsResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := baseURL + "/" + "servers/builds" + + queryParams := make(url.Values) + if request.Tags != nil { + queryParams.Add("tags", fmt.Sprintf("%v", *request.Tags)) + } + if len(queryParams) > 0 { + endpointURL += "?" + queryParams.Encode() + } + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.ListBuildsResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} + +// Creates a new game build for the given game. +func (c *Client) PrepareBuild(ctx context.Context, request *servers.CreateBuildRequest) (*servers.CreateBuildResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := baseURL + "/" + "servers/builds" + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.CreateBuildResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: c.header, + Request: request, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} + +// Marks an upload as complete. +func (c *Client) CompleteBuild(ctx context.Context, uploadId uuid.UUID) error { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := fmt.Sprintf(baseURL+"/"+"servers/uploads/%v/complete", uploadId) + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: c.header, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return err + } + return nil +} diff --git a/sdks/full/go/servers/client/client.go b/sdks/full/go/servers/client/client.go new file mode 100644 index 0000000000..da5b21a21f --- /dev/null +++ b/sdks/full/go/servers/client/client.go @@ -0,0 +1,373 @@ +// This file was auto-generated by Fern from our API Definition. + +package client + +import ( + bytes "bytes" + context "context" + json "encoding/json" + errors "errors" + fmt "fmt" + uuid "github.com/google/uuid" + io "io" + http "net/http" + url "net/url" + sdk "sdk" + core "sdk/core" + servers "sdk/servers" + builds "sdk/servers/builds" +) + +type Client struct { + baseURL string + caller *core.Caller + header http.Header + + Builds *builds.Client +} + +func NewClient(opts ...core.ClientOption) *Client { + options := core.NewClientOptions() + for _, opt := range opts { + opt(options) + } + return &Client{ + baseURL: options.BaseURL, + caller: core.NewCaller(options.HTTPClient), + header: options.ToHeader(), + Builds: builds.NewClient(opts...), + } +} + +// Gets a dynamic server. +// +// The id of the server to destroy +func (c *Client) Get(ctx context.Context, serverId uuid.UUID) (*servers.GetServerResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := fmt.Sprintf(baseURL+"/"+"servers/%v", serverId) + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.GetServerResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} + +// Create a new dynamic server. +func (c *Client) Create(ctx context.Context, request *servers.CreateServerRequest) (*servers.CreateServerResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := baseURL + "/" + "servers" + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.CreateServerResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: c.header, + Request: request, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} + +// Destroy a dynamic server. +// +// The id of the server to destroy +func (c *Client) Destroy(ctx context.Context, serverId uuid.UUID, request *servers.DestroyServerRequest) (*servers.DestroyServerResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := fmt.Sprintf(baseURL+"/"+"servers/%v", serverId) + + queryParams := make(url.Values) + if request.OverrideKillTimeout != nil { + queryParams.Add("override_kill_timeout", fmt.Sprintf("%v", *request.OverrideKillTimeout)) + } + if len(queryParams) > 0 { + endpointURL += "?" + queryParams.Encode() + } + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.DestroyServerResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodDelete, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} + +// Lists all servers associated with the token used. Can be filtered by tags in the query string. +func (c *Client) List(ctx context.Context, request *servers.GetServersRequest) (*servers.ListServersResponse, error) { + baseURL := "https://api.rivet.gg" + if c.baseURL != "" { + baseURL = c.baseURL + } + endpointURL := baseURL + "/" + "servers/list" + + queryParams := make(url.Values) + if request.Tags != nil { + queryParams.Add("tags", fmt.Sprintf("%v", *request.Tags)) + } + if len(queryParams) > 0 { + endpointURL += "?" + queryParams.Encode() + } + + errorDecoder := func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return err + } + apiError := core.NewAPIError(statusCode, errors.New(string(raw))) + decoder := json.NewDecoder(bytes.NewReader(raw)) + switch statusCode { + case 500: + value := new(sdk.InternalError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 429: + value := new(sdk.RateLimitError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 403: + value := new(sdk.ForbiddenError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 408: + value := new(sdk.UnauthorizedError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 404: + value := new(sdk.NotFoundError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + case 400: + value := new(sdk.BadRequestError) + value.APIError = apiError + if err := decoder.Decode(value); err != nil { + return apiError + } + return value + } + return apiError + } + + var response *servers.ListServersResponse + if err := c.caller.Call( + ctx, + &core.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: c.header, + Response: &response, + ErrorDecoder: errorDecoder, + }, + ); err != nil { + return nil, err + } + return response, nil +} diff --git a/sdks/full/go/servers/servers.go b/sdks/full/go/servers/servers.go new file mode 100644 index 0000000000..4c840d3c86 --- /dev/null +++ b/sdks/full/go/servers/servers.go @@ -0,0 +1,168 @@ +// This file was auto-generated by Fern from our API Definition. + +package servers + +import ( + json "encoding/json" + fmt "fmt" + uuid "github.com/google/uuid" + core "sdk/core" +) + +type CreateServerRequest struct { + // The name ID of the datacenter + Datacenter string `json:"datacenter"` + Tags interface{} `json:"tags,omitempty"` + ImageId uuid.UUID `json:"image_id"` + Arguments []string `json:"arguments,omitempty"` + Environment map[string]string `json:"environment,omitempty"` + Network *CreateServerNetworkRequest `json:"network,omitempty"` + Resources *Resources `json:"resources,omitempty"` + // The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. + KillTimeout *int64 `json:"kill_timeout,omitempty"` + // A url to send to which events from the server running will be sent + WebhookUrl *string `json:"webhook_url,omitempty"` + + _rawJSON json.RawMessage +} + +func (c *CreateServerRequest) UnmarshalJSON(data []byte) error { + type unmarshaler CreateServerRequest + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateServerRequest(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateServerRequest) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} + +type CreateServerResponse struct { + // The server that was created + Server *Server `json:"server,omitempty"` + + _rawJSON json.RawMessage +} + +func (c *CreateServerResponse) UnmarshalJSON(data []byte) error { + type unmarshaler CreateServerResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateServerResponse(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateServerResponse) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} + +type DestroyServerResponse struct { + ServerId uuid.UUID `json:"server_id"` + + _rawJSON json.RawMessage +} + +func (d *DestroyServerResponse) UnmarshalJSON(data []byte) error { + type unmarshaler DestroyServerResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *d = DestroyServerResponse(value) + d._rawJSON = json.RawMessage(data) + return nil +} + +func (d *DestroyServerResponse) String() string { + if len(d._rawJSON) > 0 { + if value, err := core.StringifyJSON(d._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(d); err == nil { + return value + } + return fmt.Sprintf("%#v", d) +} + +type GetServerResponse struct { + Server *Server `json:"server,omitempty"` + + _rawJSON json.RawMessage +} + +func (g *GetServerResponse) UnmarshalJSON(data []byte) error { + type unmarshaler GetServerResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *g = GetServerResponse(value) + g._rawJSON = json.RawMessage(data) + return nil +} + +func (g *GetServerResponse) String() string { + if len(g._rawJSON) > 0 { + if value, err := core.StringifyJSON(g._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} + +type ListServersResponse struct { + // A list of servers for the game associated with the token. + Servers []*Server `json:"servers,omitempty"` + + _rawJSON json.RawMessage +} + +func (l *ListServersResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListServersResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListServersResponse(value) + l._rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListServersResponse) String() string { + if len(l._rawJSON) > 0 { + if value, err := core.StringifyJSON(l._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} diff --git a/sdks/full/go/servers/types.go b/sdks/full/go/servers/types.go new file mode 100644 index 0000000000..c646521218 --- /dev/null +++ b/sdks/full/go/servers/types.go @@ -0,0 +1,404 @@ +// This file was auto-generated by Fern from our API Definition. + +package servers + +import ( + json "encoding/json" + fmt "fmt" + uuid "github.com/google/uuid" + core "sdk/core" +) + +type DestroyServerRequest struct { + // The duration to wait for in milliseconds before killing the server. This should be used to override the default kill timeout if a faster time is needed, say for ignoring a graceful shutdown. + OverrideKillTimeout *int64 `json:"-"` +} + +type GetServersRequest struct { + Tags *string `json:"-"` +} + +type BuildCompression string + +const ( + // None compression. + BuildCompressionNone BuildCompression = "none" + // LZ4 compression. Use the minimum compression level. + BuildCompressionLz4 BuildCompression = "lz4" +) + +func NewBuildCompressionFromString(s string) (BuildCompression, error) { + switch s { + case "none": + return BuildCompressionNone, nil + case "lz4": + return BuildCompressionLz4, nil + } + var t BuildCompression + return "", fmt.Errorf("%s is not a valid %T", s, t) +} + +func (b BuildCompression) Ptr() *BuildCompression { + return &b +} + +type BuildKind string + +const ( + // Docker image archive generated by `docker save`. + BuildKindDockerImage BuildKind = "docker_image" + // OCI-compliant bundle. + BuildKindOciBundle BuildKind = "oci_bundle" +) + +func NewBuildKindFromString(s string) (BuildKind, error) { + switch s { + case "docker_image": + return BuildKindDockerImage, nil + case "oci_bundle": + return BuildKindOciBundle, nil + } + var t BuildKind + return "", fmt.Errorf("%s is not a valid %T", s, t) +} + +func (b BuildKind) Ptr() *BuildKind { + return &b +} + +type GameGuardRouting struct { + _rawJSON json.RawMessage +} + +func (g *GameGuardRouting) UnmarshalJSON(data []byte) error { + type unmarshaler GameGuardRouting + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *g = GameGuardRouting(value) + g._rawJSON = json.RawMessage(data) + return nil +} + +func (g *GameGuardRouting) String() string { + if len(g._rawJSON) > 0 { + if value, err := core.StringifyJSON(g._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} + +type HostRouting struct { + _rawJSON json.RawMessage +} + +func (h *HostRouting) UnmarshalJSON(data []byte) error { + type unmarshaler HostRouting + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *h = HostRouting(value) + h._rawJSON = json.RawMessage(data) + return nil +} + +func (h *HostRouting) String() string { + if len(h._rawJSON) > 0 { + if value, err := core.StringifyJSON(h._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(h); err == nil { + return value + } + return fmt.Sprintf("%#v", h) +} + +type Network struct { + Mode *NetworkMode `json:"mode,omitempty"` + Ports map[string]*Port `json:"ports,omitempty"` + + _rawJSON json.RawMessage +} + +func (n *Network) UnmarshalJSON(data []byte) error { + type unmarshaler Network + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *n = Network(value) + n._rawJSON = json.RawMessage(data) + return nil +} + +func (n *Network) String() string { + if len(n._rawJSON) > 0 { + if value, err := core.StringifyJSON(n._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(n); err == nil { + return value + } + return fmt.Sprintf("%#v", n) +} + +type NetworkMode string + +const ( + NetworkModeBridge NetworkMode = "bridge" + NetworkModeHost NetworkMode = "host" +) + +func NewNetworkModeFromString(s string) (NetworkMode, error) { + switch s { + case "bridge": + return NetworkModeBridge, nil + case "host": + return NetworkModeHost, nil + } + var t NetworkMode + return "", fmt.Errorf("%s is not a valid %T", s, t) +} + +func (n NetworkMode) Ptr() *NetworkMode { + return &n +} + +type Port struct { + Protocol PortProtocol `json:"protocol,omitempty"` + InternalPort *int `json:"internal_port,omitempty"` + PublicHostname *string `json:"public_hostname,omitempty"` + PublicPort *int `json:"public_port,omitempty"` + Routing *PortRouting `json:"routing,omitempty"` + + _rawJSON json.RawMessage +} + +func (p *Port) UnmarshalJSON(data []byte) error { + type unmarshaler Port + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *p = Port(value) + p._rawJSON = json.RawMessage(data) + return nil +} + +func (p *Port) String() string { + if len(p._rawJSON) > 0 { + if value, err := core.StringifyJSON(p._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(p); err == nil { + return value + } + return fmt.Sprintf("%#v", p) +} + +type PortProtocol string + +const ( + PortProtocolHttp PortProtocol = "http" + PortProtocolHttps PortProtocol = "https" + PortProtocolTcp PortProtocol = "tcp" + PortProtocolTcpTls PortProtocol = "tcp_tls" + PortProtocolUdp PortProtocol = "udp" +) + +func NewPortProtocolFromString(s string) (PortProtocol, error) { + switch s { + case "http": + return PortProtocolHttp, nil + case "https": + return PortProtocolHttps, nil + case "tcp": + return PortProtocolTcp, nil + case "tcp_tls": + return PortProtocolTcpTls, nil + case "udp": + return PortProtocolUdp, nil + } + var t PortProtocol + return "", fmt.Errorf("%s is not a valid %T", s, t) +} + +func (p PortProtocol) Ptr() *PortProtocol { + return &p +} + +type PortRouting struct { + GameGuard *GameGuardRouting `json:"game_guard,omitempty"` + Host *HostRouting `json:"host,omitempty"` + + _rawJSON json.RawMessage +} + +func (p *PortRouting) UnmarshalJSON(data []byte) error { + type unmarshaler PortRouting + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *p = PortRouting(value) + p._rawJSON = json.RawMessage(data) + return nil +} + +func (p *PortRouting) String() string { + if len(p._rawJSON) > 0 { + if value, err := core.StringifyJSON(p._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(p); err == nil { + return value + } + return fmt.Sprintf("%#v", p) +} + +type Resources struct { + // The number of CPU cores in millicores, or 1/1000 of a core. For example, + // 1/8 of a core would be 125 millicores, and 1 core would be 1000 + // millicores. + Cpu int `json:"cpu"` + // The amount of memory in megabytes + Memory int `json:"memory"` + + _rawJSON json.RawMessage +} + +func (r *Resources) UnmarshalJSON(data []byte) error { + type unmarshaler Resources + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *r = Resources(value) + r._rawJSON = json.RawMessage(data) + return nil +} + +func (r *Resources) String() string { + if len(r._rawJSON) > 0 { + if value, err := core.StringifyJSON(r._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(r); err == nil { + return value + } + return fmt.Sprintf("%#v", r) +} + +type Server struct { + ServerId uuid.UUID `json:"server_id"` + GameId uuid.UUID `json:"game_id"` + DatacenterId uuid.UUID `json:"datacenter_id"` + ClusterId uuid.UUID `json:"cluster_id"` + Tags interface{} `json:"tags,omitempty"` + ImageId uuid.UUID `json:"image_id"` + Arguments []string `json:"arguments,omitempty"` + Environment map[string]string `json:"environment,omitempty"` + Network *Network `json:"network,omitempty"` + Resources *Resources `json:"resources,omitempty"` + // The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. + KillTimeout *int64 `json:"kill_timeout,omitempty"` + CreateTs int64 `json:"create_ts"` + DestroyTs *int64 `json:"destroy_ts,omitempty"` + + _rawJSON json.RawMessage +} + +func (s *Server) UnmarshalJSON(data []byte) error { + type unmarshaler Server + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *s = Server(value) + s._rawJSON = json.RawMessage(data) + return nil +} + +func (s *Server) String() string { + if len(s._rawJSON) > 0 { + if value, err := core.StringifyJSON(s._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(s); err == nil { + return value + } + return fmt.Sprintf("%#v", s) +} + +type CreateServerNetworkRequest struct { + Mode *NetworkMode `json:"mode,omitempty"` + Ports map[string]*CreateServerPortRequest `json:"ports,omitempty"` + + _rawJSON json.RawMessage +} + +func (c *CreateServerNetworkRequest) UnmarshalJSON(data []byte) error { + type unmarshaler CreateServerNetworkRequest + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateServerNetworkRequest(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateServerNetworkRequest) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} + +type CreateServerPortRequest struct { + Protocol PortProtocol `json:"protocol,omitempty"` + InternalPort *int `json:"internal_port,omitempty"` + Routing *PortRouting `json:"routing,omitempty"` + + _rawJSON json.RawMessage +} + +func (c *CreateServerPortRequest) UnmarshalJSON(data []byte) error { + type unmarshaler CreateServerPortRequest + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *c = CreateServerPortRequest(value) + c._rawJSON = json.RawMessage(data) + return nil +} + +func (c *CreateServerPortRequest) String() string { + if len(c._rawJSON) > 0 { + if value, err := core.StringifyJSON(c._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(c); err == nil { + return value + } + return fmt.Sprintf("%#v", c) +} diff --git a/sdks/full/openapi/openapi.yml b/sdks/full/openapi/openapi.yml index 90495ff226..d7e999eacf 100644 --- a/sdks/full/openapi/openapi.yml +++ b/sdks/full/openapi/openapi.yml @@ -4874,6 +4874,246 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 + /servers/{server_id}: + get: + description: Gets a dynamic server. + operationId: servers_get + tags: + - Servers + parameters: + - name: server_id + in: path + description: The id of the server to destroy + required: true + schema: + type: string + format: uuid + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ServersGetServerResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + delete: + description: Destroy a dynamic server. + operationId: servers_destroy + tags: + - Servers + parameters: + - name: server_id + in: path + description: The id of the server to destroy + required: true + schema: + type: string + format: uuid + - name: override_kill_timeout + in: query + description: >- + The duration to wait for in milliseconds before killing the server. + This should be used to override the default kill timeout if a faster + time is needed, say for ignoring a graceful shutdown. + required: false + schema: + type: integer + format: int64 + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ServersDestroyServerResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + /servers: + post: + description: Create a new dynamic server. + operationId: servers_create + tags: + - Servers + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateServerResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateServerRequest' + /servers/list: + get: + description: >- + Lists all servers associated with the token used. Can be filtered by + tags in the query string. + operationId: servers_list + tags: + - Servers + parameters: + - name: tags + in: query + required: false + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ServersListServersResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 /admin/clusters/{cluster_id}/datacenters: get: description: Get datacenters of a cluster @@ -6667,6 +6907,63 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 + /cloud/games/{game_id}/tokens/service: + post: + description: Creates a new game service token. + operationId: cloud_games_tokens_createServiceToken + tags: + - CloudGamesTokens + parameters: + - name: game_id + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CloudGamesCreateCloudTokenResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 /cloud/games/{game_id}/versions: post: description: Creates a new game version. @@ -8675,18 +8972,200 @@ paths: - player_token /matchmaker/players/statistics: get: - description: Gives matchmaker statistics about the players in game. - operationId: matchmaker_players_getStatistics + description: Gives matchmaker statistics about the players in game. + operationId: matchmaker_players_getStatistics + tags: + - MatchmakerPlayers + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/MatchmakerGetStatisticsResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + /matchmaker/regions: + get: + description: >- + Returns a list of regions available to this namespace. + + Regions are sorted by most optimal to least optimal. The player's IP + address + + is used to calculate the regions' optimality. + operationId: matchmaker_regions_list + tags: + - MatchmakerRegions + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/MatchmakerListRegionsResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + /portal/games/{game_name_id}/profile: + get: + description: Returns a game profile. + operationId: portal_games_getGameProfile + tags: + - PortalGames + parameters: + - name: game_name_id + in: path + description: >- + A human readable short identifier used to references resources. + Different than a `rivet.common#Uuid` because this is intended to be + human readable. Different than `rivet.common#DisplayName` because + this should not include special characters and be short. + required: true + schema: + type: string + - name: watch_index + in: query + description: A query parameter denoting the requests watch index. + required: false + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/PortalGetGameProfileResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '404': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '408': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '429': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + security: *ref_0 + /servers/builds: + get: + description: >- + Lists all builds of the game associated with the token used. Can be + filtered by tags in the query string. + operationId: servers_builds_listBuilds tags: - - MatchmakerPlayers - parameters: [] + - ServersBuilds + parameters: + - name: tags + in: query + required: false + schema: + type: string responses: '200': description: '' content: application/json: schema: - $ref: '#/components/schemas/MatchmakerGetStatisticsResponse' + $ref: '#/components/schemas/ServersListBuildsResponse' '400': description: '' content: @@ -8724,18 +9203,11 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 - /matchmaker/regions: - get: - description: >- - Returns a list of regions available to this namespace. - - Regions are sorted by most optimal to least optimal. The player's IP - address - - is used to calculate the regions' optimality. - operationId: matchmaker_regions_list + post: + description: Creates a new game build for the given game. + operationId: servers_builds_prepareBuild tags: - - MatchmakerRegions + - ServersBuilds parameters: [] responses: '200': @@ -8743,7 +9215,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/MatchmakerListRegionsResponse' + $ref: '#/components/schemas/ServersCreateBuildResponse' '400': description: '' content: @@ -8781,36 +9253,28 @@ paths: schema: $ref: '#/components/schemas/ErrorBody' security: *ref_0 - /portal/games/{game_name_id}/profile: - get: - description: Returns a game profile. - operationId: portal_games_getGameProfile + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateBuildRequest' + /servers/uploads/{upload_id}/complete: + post: + description: Marks an upload as complete. + operationId: servers_builds_completeBuild tags: - - PortalGames + - ServersBuilds parameters: - - name: game_name_id + - name: upload_id in: path - description: >- - A human readable short identifier used to references resources. - Different than a `rivet.common#Uuid` because this is intended to be - human readable. Different than `rivet.common#DisplayName` because - this should not include special characters and be short. required: true schema: type: string - - name: watch_index - in: query - description: A query parameter denoting the requests watch index. - required: false - schema: - type: string + format: uuid responses: - '200': + '204': description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PortalGetGameProfileResponse' '400': description: '' content: @@ -9975,6 +10439,99 @@ components: - datacenter_id - cluster_id - vlan_ip + ServersGetServerResponse: + type: object + properties: + server: + $ref: '#/components/schemas/ServersServer' + required: + - server + ServersCreateServerRequest: + type: object + properties: + datacenter: + type: string + description: The name ID of the datacenter + tags: {} + image_id: + type: string + format: uuid + arguments: + type: array + items: + type: string + environment: + type: object + additionalProperties: + type: string + network: + $ref: '#/components/schemas/ServersCreateServerNetworkRequest' + resources: + $ref: '#/components/schemas/ServersResources' + kill_timeout: + type: integer + format: int64 + description: >- + The duration to wait for in milliseconds before killing the server. + This should be set to a safe default, and can be overridden during a + DELETE request if needed. + webhook_url: + type: string + description: A url to send to which events from the server running will be sent + required: + - datacenter + - tags + - image_id + - network + - resources + ServersCreateServerNetworkRequest: + type: object + properties: + mode: + $ref: '#/components/schemas/ServersNetworkMode' + ports: + type: object + additionalProperties: + $ref: '#/components/schemas/ServersCreateServerPortRequest' + required: + - ports + ServersCreateServerPortRequest: + type: object + properties: + protocol: + $ref: '#/components/schemas/ServersPortProtocol' + internal_port: + type: integer + routing: + $ref: '#/components/schemas/ServersPortRouting' + required: + - protocol + ServersCreateServerResponse: + type: object + properties: + server: + $ref: '#/components/schemas/ServersServer' + description: The server that was created + required: + - server + ServersDestroyServerResponse: + type: object + properties: + server_id: + type: string + format: uuid + required: + - server_id + ServersListServersResponse: + type: object + properties: + servers: + type: array + items: + $ref: '#/components/schemas/ServersServer' + description: A list of servers for the game associated with the token. + required: + - servers AdminClustersPoolType: type: string enum: @@ -10577,6 +11134,11 @@ components: complete: type: boolean description: Whether or not this build has completely been uploaded. + tags: + type: object + additionalProperties: + type: string + description: Tags of this build required: - build_id - upload_id @@ -10584,6 +11146,7 @@ components: - create_ts - content_length - complete + - tags CloudCdnSiteSummary: type: object description: A CDN site summary. @@ -11386,6 +11949,14 @@ components: JSON. required: - token + CloudGamesCreateServiceTokenResponse: + type: object + properties: + token: + type: string + description: A JSON Web Token. + required: + - token CloudGamesCreateGameVersionRequest: type: object properties: @@ -13363,6 +13934,191 @@ components: required: - game - watch + ServersListBuildsResponse: + type: object + properties: + builds: + type: array + items: + $ref: '#/components/schemas/CloudBuildSummary' + description: A list of builds for the game associated with the token. + required: + - builds + ServersCreateBuildRequest: + type: object + properties: + display_name: + $ref: '#/components/schemas/DisplayName' + tags: {} + image_tag: + type: string + description: A tag given to the game build. + image_file: + $ref: '#/components/schemas/UploadPrepareFile' + multipart_upload: + type: boolean + kind: + $ref: '#/components/schemas/ServersBuildKind' + compression: + $ref: '#/components/schemas/ServersBuildCompression' + required: + - display_name + - tags + - image_tag + - image_file + ServersCreateBuildResponse: + type: object + properties: + build_id: + type: string + format: uuid + upload_id: + type: string + format: uuid + image_presigned_request: + $ref: '#/components/schemas/UploadPresignedRequest' + image_presigned_requests: + type: array + items: + $ref: '#/components/schemas/UploadPresignedRequest' + required: + - build_id + - upload_id + ServersBuildKind: + type: string + enum: + - docker_image + - oci_bundle + ServersBuildCompression: + type: string + enum: + - none + - lz4 + ServersServer: + type: object + properties: + server_id: + type: string + format: uuid + game_id: + type: string + format: uuid + datacenter_id: + type: string + format: uuid + cluster_id: + type: string + format: uuid + tags: {} + image_id: + type: string + format: uuid + arguments: + type: array + items: + type: string + environment: + type: object + additionalProperties: + type: string + network: + $ref: '#/components/schemas/ServersNetwork' + resources: + $ref: '#/components/schemas/ServersResources' + kill_timeout: + type: integer + format: int64 + description: >- + The duration to wait for in milliseconds before killing the server. + This should be set to a safe default, and can be overridden during a + DELETE request if needed. + create_ts: + type: integer + format: int64 + destroy_ts: + type: integer + format: int64 + required: + - server_id + - game_id + - datacenter_id + - cluster_id + - tags + - image_id + - network + - resources + - create_ts + ServersResources: + type: object + properties: + cpu: + type: integer + description: >- + The number of CPU cores in millicores, or 1/1000 of a core. For + example, + + 1/8 of a core would be 125 millicores, and 1 core would be 1000 + + millicores. + memory: + type: integer + description: The amount of memory in megabytes + required: + - cpu + - memory + ServersNetwork: + type: object + properties: + mode: + $ref: '#/components/schemas/ServersNetworkMode' + ports: + type: object + additionalProperties: + $ref: '#/components/schemas/ServersPort' + required: + - ports + ServersNetworkMode: + type: string + enum: + - bridge + - host + ServersPort: + type: object + properties: + protocol: + $ref: '#/components/schemas/ServersPortProtocol' + internal_port: + type: integer + public_hostname: + type: string + public_port: + type: integer + routing: + $ref: '#/components/schemas/ServersPortRouting' + required: + - protocol + - routing + ServersPortProtocol: + type: string + enum: + - http + - https + - tcp + - tcp_tls + - udp + ServersPortRouting: + type: object + properties: + game_guard: + $ref: '#/components/schemas/ServersGameGuardRouting' + host: + $ref: '#/components/schemas/ServersHostRouting' + ServersGameGuardRouting: + type: object + properties: {} + ServersHostRouting: + type: object + properties: {} UploadPresignedRequest: type: object description: >- diff --git a/sdks/full/openapi_compat/openapi.yml b/sdks/full/openapi_compat/openapi.yml index 4534bbfa22..8a6c940e02 100644 --- a/sdks/full/openapi_compat/openapi.yml +++ b/sdks/full/openapi_compat/openapi.yml @@ -464,6 +464,11 @@ components: $ref: '#/components/schemas/Timestamp' display_name: $ref: '#/components/schemas/DisplayName' + tags: + additionalProperties: + type: string + description: Tags of this build + type: object upload_id: format: uuid type: string @@ -474,6 +479,7 @@ components: - create_ts - content_length - complete + - tags type: object CloudCdnAuthType: description: A value denoting what type of authentication to use for a game @@ -856,6 +862,14 @@ components: required: - version_id type: object + CloudGamesCreateServiceTokenResponse: + properties: + token: + description: A JSON Web Token. + type: string + required: + - token + type: object CloudGamesDeleteMatchmakerLobbyResponse: properties: did_remove: @@ -4389,6 +4403,282 @@ components: - cluster_id - vlan_ip type: object + ServersBuildCompression: + enum: + - none + - lz4 + type: string + ServersBuildKind: + enum: + - docker_image + - oci_bundle + type: string + ServersCreateBuildRequest: + properties: + compression: + $ref: '#/components/schemas/ServersBuildCompression' + display_name: + $ref: '#/components/schemas/DisplayName' + image_file: + $ref: '#/components/schemas/UploadPrepareFile' + image_tag: + description: A tag given to the game build. + type: string + kind: + $ref: '#/components/schemas/ServersBuildKind' + multipart_upload: + type: boolean + tags: {} + required: + - display_name + - tags + - image_tag + - image_file + type: object + ServersCreateBuildResponse: + properties: + build_id: + format: uuid + type: string + image_presigned_request: + $ref: '#/components/schemas/UploadPresignedRequest' + image_presigned_requests: + items: + $ref: '#/components/schemas/UploadPresignedRequest' + type: array + upload_id: + format: uuid + type: string + required: + - build_id + - upload_id + type: object + ServersCreateServerNetworkRequest: + properties: + mode: + $ref: '#/components/schemas/ServersNetworkMode' + ports: + additionalProperties: + $ref: '#/components/schemas/ServersCreateServerPortRequest' + type: object + required: + - ports + type: object + ServersCreateServerPortRequest: + properties: + internal_port: + type: integer + protocol: + $ref: '#/components/schemas/ServersPortProtocol' + routing: + $ref: '#/components/schemas/ServersPortRouting' + required: + - protocol + type: object + ServersCreateServerRequest: + properties: + arguments: + items: + type: string + type: array + datacenter: + description: The name ID of the datacenter + type: string + environment: + additionalProperties: + type: string + type: object + image_id: + format: uuid + type: string + kill_timeout: + description: The duration to wait for in milliseconds before killing the + server. This should be set to a safe default, and can be overridden during + a DELETE request if needed. + format: int64 + type: integer + network: + $ref: '#/components/schemas/ServersCreateServerNetworkRequest' + resources: + $ref: '#/components/schemas/ServersResources' + tags: {} + webhook_url: + description: A url to send to which events from the server running will + be sent + type: string + required: + - datacenter + - tags + - image_id + - network + - resources + type: object + ServersCreateServerResponse: + properties: + server: + $ref: '#/components/schemas/ServersServer' + description: The server that was created + required: + - server + type: object + ServersDestroyServerResponse: + properties: + server_id: + format: uuid + type: string + required: + - server_id + type: object + ServersGameGuardRouting: + properties: {} + type: object + ServersGetServerResponse: + properties: + server: + $ref: '#/components/schemas/ServersServer' + required: + - server + type: object + ServersHostRouting: + properties: {} + type: object + ServersListBuildsResponse: + properties: + builds: + description: A list of builds for the game associated with the token. + items: + $ref: '#/components/schemas/CloudBuildSummary' + type: array + required: + - builds + type: object + ServersListServersResponse: + properties: + servers: + description: A list of servers for the game associated with the token. + items: + $ref: '#/components/schemas/ServersServer' + type: array + required: + - servers + type: object + ServersNetwork: + properties: + mode: + $ref: '#/components/schemas/ServersNetworkMode' + ports: + additionalProperties: + $ref: '#/components/schemas/ServersPort' + type: object + required: + - ports + type: object + ServersNetworkMode: + enum: + - bridge + - host + type: string + ServersPort: + properties: + internal_port: + type: integer + protocol: + $ref: '#/components/schemas/ServersPortProtocol' + public_hostname: + type: string + public_port: + type: integer + routing: + $ref: '#/components/schemas/ServersPortRouting' + required: + - protocol + - routing + type: object + ServersPortProtocol: + enum: + - http + - https + - tcp + - tcp_tls + - udp + type: string + ServersPortRouting: + properties: + game_guard: + $ref: '#/components/schemas/ServersGameGuardRouting' + host: + $ref: '#/components/schemas/ServersHostRouting' + type: object + ServersResources: + properties: + cpu: + description: 'The number of CPU cores in millicores, or 1/1000 of a core. + For example, + + 1/8 of a core would be 125 millicores, and 1 core would be 1000 + + millicores.' + type: integer + memory: + description: The amount of memory in megabytes + type: integer + required: + - cpu + - memory + type: object + ServersServer: + properties: + arguments: + items: + type: string + type: array + cluster_id: + format: uuid + type: string + create_ts: + format: int64 + type: integer + datacenter_id: + format: uuid + type: string + destroy_ts: + format: int64 + type: integer + environment: + additionalProperties: + type: string + type: object + game_id: + format: uuid + type: string + image_id: + format: uuid + type: string + kill_timeout: + description: The duration to wait for in milliseconds before killing the + server. This should be set to a safe default, and can be overridden during + a DELETE request if needed. + format: int64 + type: integer + network: + $ref: '#/components/schemas/ServersNetwork' + resources: + $ref: '#/components/schemas/ServersResources' + server_id: + format: uuid + type: string + tags: {} + required: + - server_id + - game_id + - datacenter_id + - cluster_id + - tags + - image_id + - network + - resources + - create_ts + type: object Timestamp: description: RFC3339 timestamp format: date-time @@ -8006,6 +8296,63 @@ paths: security: *id001 tags: - CloudGamesTokens + /cloud/games/{game_id}/tokens/service: + post: + description: Creates a new game service token. + operationId: cloud_games_tokens_createServiceToken + parameters: + - in: path + name: game_id + required: true + schema: + format: uuid + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CloudGamesCreateCloudTokenResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - CloudGamesTokens /cloud/games/{game_id}/versions: post: description: Creates a new game version. @@ -13159,15 +13506,295 @@ paths: security: *id001 tags: - PortalGames - /servers/{ip}: - get: - operationId: provision_servers_getInfo - parameters: - - in: path - name: ip - required: true - schema: - type: string + /servers: + post: + description: Create a new dynamic server. + operationId: servers_create + parameters: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateServerRequest' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateServerResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - Servers + /servers/builds: + get: + description: Lists all builds of the game associated with the token used. Can + be filtered by tags in the query string. + operationId: servers_builds_listBuilds + parameters: + - in: query + name: tags + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersListBuildsResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - ServersBuilds + post: + description: Creates a new game build for the given game. + operationId: servers_builds_prepareBuild + parameters: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateBuildRequest' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersCreateBuildResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - ServersBuilds + /servers/list: + get: + description: Lists all servers associated with the token used. Can be filtered + by tags in the query string. + operationId: servers_list + parameters: + - in: query + name: tags + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersListServersResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - Servers + /servers/uploads/{upload_id}/complete: + post: + description: Marks an upload as complete. + operationId: servers_builds_completeBuild + parameters: + - in: path + name: upload_id + required: true + schema: + format: uuid + type: string + responses: + '204': + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - ServersBuilds + /servers/{ip}: + get: + operationId: provision_servers_getInfo + parameters: + - in: path + name: ip + required: true + schema: + type: string responses: '200': content: @@ -13214,6 +13841,130 @@ paths: security: *id001 tags: - ProvisionServers + /servers/{server_id}: + delete: + description: Destroy a dynamic server. + operationId: servers_destroy + parameters: + - description: The id of the server to destroy + in: path + name: server_id + required: true + schema: + format: uuid + type: string + - description: The duration to wait for in milliseconds before killing the server. + This should be used to override the default kill timeout if a faster time + is needed, say for ignoring a graceful shutdown. + in: query + name: override_kill_timeout + required: false + schema: + format: int64 + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersDestroyServerResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - Servers + get: + description: Gets a dynamic server. + operationId: servers_get + parameters: + - description: The id of the server to destroy + in: path + name: server_id + required: true + schema: + format: uuid + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ServersGetServerResponse' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '408': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '429': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBody' + description: '' + security: *id001 + tags: + - Servers servers: - description: Production url: https://api.rivet.gg diff --git a/sdks/full/rust-cli/.openapi-generator/FILES b/sdks/full/rust-cli/.openapi-generator/FILES index dda235bb7e..0a29db9a8c 100644 --- a/sdks/full/rust-cli/.openapi-generator/FILES +++ b/sdks/full/rust-cli/.openapi-generator/FILES @@ -84,6 +84,7 @@ docs/CloudGamesCreateGameRequest.md docs/CloudGamesCreateGameResponse.md docs/CloudGamesCreateGameVersionRequest.md docs/CloudGamesCreateGameVersionResponse.md +docs/CloudGamesCreateServiceTokenResponse.md docs/CloudGamesDeleteMatchmakerLobbyResponse.md docs/CloudGamesExportLobbyLogsRequest.md docs/CloudGamesExportLobbyLogsResponse.md @@ -345,6 +346,27 @@ docs/ProvisionDatacentersApi.md docs/ProvisionDatacentersGetTlsResponse.md docs/ProvisionServersApi.md docs/ProvisionServersGetInfoResponse.md +docs/ServersApi.md +docs/ServersBuildCompression.md +docs/ServersBuildKind.md +docs/ServersBuildsApi.md +docs/ServersCreateBuildRequest.md +docs/ServersCreateBuildResponse.md +docs/ServersCreateServerNetworkRequest.md +docs/ServersCreateServerPortRequest.md +docs/ServersCreateServerRequest.md +docs/ServersCreateServerResponse.md +docs/ServersDestroyServerResponse.md +docs/ServersGetServerResponse.md +docs/ServersListBuildsResponse.md +docs/ServersListServersResponse.md +docs/ServersNetwork.md +docs/ServersNetworkMode.md +docs/ServersPort.md +docs/ServersPortProtocol.md +docs/ServersPortRouting.md +docs/ServersResources.md +docs/ServersServer.md docs/UploadPrepareFile.md docs/UploadPresignedRequest.md docs/ValidationError.md @@ -391,6 +413,8 @@ src/apis/mod.rs src/apis/portal_games_api.rs src/apis/provision_datacenters_api.rs src/apis/provision_servers_api.rs +src/apis/servers_api.rs +src/apis/servers_builds_api.rs src/lib.rs src/models/admin_clusters_build_delivery_method.rs src/models/admin_clusters_cluster.rs @@ -459,6 +483,7 @@ src/models/cloud_games_create_game_request.rs src/models/cloud_games_create_game_response.rs src/models/cloud_games_create_game_version_request.rs src/models/cloud_games_create_game_version_response.rs +src/models/cloud_games_create_service_token_response.rs src/models/cloud_games_delete_matchmaker_lobby_response.rs src/models/cloud_games_export_lobby_logs_request.rs src/models/cloud_games_export_lobby_logs_response.rs @@ -696,6 +721,25 @@ src/models/portal_notification_register_service.rs src/models/portal_notification_unregister_service.rs src/models/provision_datacenters_get_tls_response.rs src/models/provision_servers_get_info_response.rs +src/models/servers_build_compression.rs +src/models/servers_build_kind.rs +src/models/servers_create_build_request.rs +src/models/servers_create_build_response.rs +src/models/servers_create_server_network_request.rs +src/models/servers_create_server_port_request.rs +src/models/servers_create_server_request.rs +src/models/servers_create_server_response.rs +src/models/servers_destroy_server_response.rs +src/models/servers_get_server_response.rs +src/models/servers_list_builds_response.rs +src/models/servers_list_servers_response.rs +src/models/servers_network.rs +src/models/servers_network_mode.rs +src/models/servers_port.rs +src/models/servers_port_protocol.rs +src/models/servers_port_routing.rs +src/models/servers_resources.rs +src/models/servers_server.rs src/models/upload_prepare_file.rs src/models/upload_presigned_request.rs src/models/validation_error.rs diff --git a/sdks/full/rust-cli/README.md b/sdks/full/rust-cli/README.md index c7806fcdf6..4fecaf0a6e 100644 --- a/sdks/full/rust-cli/README.md +++ b/sdks/full/rust-cli/README.md @@ -82,6 +82,7 @@ Class | Method | HTTP request | Description *CloudGamesNamespacesLogsApi* | [**cloud_games_namespaces_logs_get_namespace_lobby**](docs/CloudGamesNamespacesLogsApi.md#cloud_games_namespaces_logs_get_namespace_lobby) | **GET** /cloud/games/{game_id}/namespaces/{namespace_id}/logs/lobbies/{lobby_id} | *CloudGamesNamespacesLogsApi* | [**cloud_games_namespaces_logs_list_namespace_lobbies**](docs/CloudGamesNamespacesLogsApi.md#cloud_games_namespaces_logs_list_namespace_lobbies) | **GET** /cloud/games/{game_id}/namespaces/{namespace_id}/logs/lobbies | *CloudGamesTokensApi* | [**cloud_games_tokens_create_cloud_token**](docs/CloudGamesTokensApi.md#cloud_games_tokens_create_cloud_token) | **POST** /cloud/games/{game_id}/tokens/cloud | +*CloudGamesTokensApi* | [**cloud_games_tokens_create_service_token**](docs/CloudGamesTokensApi.md#cloud_games_tokens_create_service_token) | **POST** /cloud/games/{game_id}/tokens/service | *CloudGamesVersionsApi* | [**cloud_games_versions_create_game_version**](docs/CloudGamesVersionsApi.md#cloud_games_versions_create_game_version) | **POST** /cloud/games/{game_id}/versions | *CloudGamesVersionsApi* | [**cloud_games_versions_get_game_version_by_id**](docs/CloudGamesVersionsApi.md#cloud_games_versions_get_game_version_by_id) | **GET** /cloud/games/{game_id}/versions/{version_id} | *CloudGamesVersionsApi* | [**cloud_games_versions_reserve_version_name**](docs/CloudGamesVersionsApi.md#cloud_games_versions_reserve_version_name) | **POST** /cloud/games/{game_id}/versions/reserve-name | @@ -166,6 +167,13 @@ Class | Method | HTTP request | Description *PortalGamesApi* | [**portal_games_get_game_profile**](docs/PortalGamesApi.md#portal_games_get_game_profile) | **GET** /portal/games/{game_name_id}/profile | *ProvisionDatacentersApi* | [**provision_datacenters_get_tls**](docs/ProvisionDatacentersApi.md#provision_datacenters_get_tls) | **GET** /datacenters/{datacenter_id}/tls | *ProvisionServersApi* | [**provision_servers_get_info**](docs/ProvisionServersApi.md#provision_servers_get_info) | **GET** /servers/{ip} | +*ServersApi* | [**servers_create**](docs/ServersApi.md#servers_create) | **POST** /servers | +*ServersApi* | [**servers_destroy**](docs/ServersApi.md#servers_destroy) | **DELETE** /servers/{server_id} | +*ServersApi* | [**servers_get**](docs/ServersApi.md#servers_get) | **GET** /servers/{server_id} | +*ServersApi* | [**servers_list**](docs/ServersApi.md#servers_list) | **GET** /servers/list | +*ServersBuildsApi* | [**servers_builds_complete_build**](docs/ServersBuildsApi.md#servers_builds_complete_build) | **POST** /servers/uploads/{upload_id}/complete | +*ServersBuildsApi* | [**servers_builds_list_builds**](docs/ServersBuildsApi.md#servers_builds_list_builds) | **GET** /servers/builds | +*ServersBuildsApi* | [**servers_builds_prepare_build**](docs/ServersBuildsApi.md#servers_builds_prepare_build) | **POST** /servers/builds | ## Documentation For Models @@ -237,6 +245,7 @@ Class | Method | HTTP request | Description - [CloudGamesCreateGameResponse](docs/CloudGamesCreateGameResponse.md) - [CloudGamesCreateGameVersionRequest](docs/CloudGamesCreateGameVersionRequest.md) - [CloudGamesCreateGameVersionResponse](docs/CloudGamesCreateGameVersionResponse.md) + - [CloudGamesCreateServiceTokenResponse](docs/CloudGamesCreateServiceTokenResponse.md) - [CloudGamesDeleteMatchmakerLobbyResponse](docs/CloudGamesDeleteMatchmakerLobbyResponse.md) - [CloudGamesExportLobbyLogsRequest](docs/CloudGamesExportLobbyLogsRequest.md) - [CloudGamesExportLobbyLogsResponse](docs/CloudGamesExportLobbyLogsResponse.md) @@ -473,6 +482,25 @@ Class | Method | HTTP request | Description - [PortalNotificationUnregisterService](docs/PortalNotificationUnregisterService.md) - [ProvisionDatacentersGetTlsResponse](docs/ProvisionDatacentersGetTlsResponse.md) - [ProvisionServersGetInfoResponse](docs/ProvisionServersGetInfoResponse.md) + - [ServersBuildCompression](docs/ServersBuildCompression.md) + - [ServersBuildKind](docs/ServersBuildKind.md) + - [ServersCreateBuildRequest](docs/ServersCreateBuildRequest.md) + - [ServersCreateBuildResponse](docs/ServersCreateBuildResponse.md) + - [ServersCreateServerNetworkRequest](docs/ServersCreateServerNetworkRequest.md) + - [ServersCreateServerPortRequest](docs/ServersCreateServerPortRequest.md) + - [ServersCreateServerRequest](docs/ServersCreateServerRequest.md) + - [ServersCreateServerResponse](docs/ServersCreateServerResponse.md) + - [ServersDestroyServerResponse](docs/ServersDestroyServerResponse.md) + - [ServersGetServerResponse](docs/ServersGetServerResponse.md) + - [ServersListBuildsResponse](docs/ServersListBuildsResponse.md) + - [ServersListServersResponse](docs/ServersListServersResponse.md) + - [ServersNetwork](docs/ServersNetwork.md) + - [ServersNetworkMode](docs/ServersNetworkMode.md) + - [ServersPort](docs/ServersPort.md) + - [ServersPortProtocol](docs/ServersPortProtocol.md) + - [ServersPortRouting](docs/ServersPortRouting.md) + - [ServersResources](docs/ServersResources.md) + - [ServersServer](docs/ServersServer.md) - [UploadPrepareFile](docs/UploadPrepareFile.md) - [UploadPresignedRequest](docs/UploadPresignedRequest.md) - [ValidationError](docs/ValidationError.md) diff --git a/sdks/full/rust-cli/docs/CloudBuildSummary.md b/sdks/full/rust-cli/docs/CloudBuildSummary.md index 6d91dd4ead..f3b76d934d 100644 --- a/sdks/full/rust-cli/docs/CloudBuildSummary.md +++ b/sdks/full/rust-cli/docs/CloudBuildSummary.md @@ -9,6 +9,7 @@ Name | Type | Description | Notes **content_length** | **i64** | Unsigned 64 bit integer. | **create_ts** | **String** | RFC3339 timestamp | **display_name** | **String** | Represent a resource's readable display name. | +**tags** | **::std::collections::HashMap** | Tags of this build | **upload_id** | [**uuid::Uuid**](uuid::Uuid.md) | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/sdks/full/rust-cli/docs/CloudGamesCreateServiceTokenResponse.md b/sdks/full/rust-cli/docs/CloudGamesCreateServiceTokenResponse.md new file mode 100644 index 0000000000..c8d021b0f2 --- /dev/null +++ b/sdks/full/rust-cli/docs/CloudGamesCreateServiceTokenResponse.md @@ -0,0 +1,11 @@ +# CloudGamesCreateServiceTokenResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**token** | **String** | A JSON Web Token. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/CloudGamesTokensApi.md b/sdks/full/rust-cli/docs/CloudGamesTokensApi.md index c0ee6276fc..8c64a98652 100644 --- a/sdks/full/rust-cli/docs/CloudGamesTokensApi.md +++ b/sdks/full/rust-cli/docs/CloudGamesTokensApi.md @@ -5,6 +5,7 @@ All URIs are relative to *https://api.rivet.gg* Method | HTTP request | Description ------------- | ------------- | ------------- [**cloud_games_tokens_create_cloud_token**](CloudGamesTokensApi.md#cloud_games_tokens_create_cloud_token) | **POST** /cloud/games/{game_id}/tokens/cloud | +[**cloud_games_tokens_create_service_token**](CloudGamesTokensApi.md#cloud_games_tokens_create_service_token) | **POST** /cloud/games/{game_id}/tokens/service | @@ -37,3 +38,33 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +## cloud_games_tokens_create_service_token + +> crate::models::CloudGamesCreateCloudTokenResponse cloud_games_tokens_create_service_token(game_id) + + +Creates a new game service token. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**game_id** | **uuid::Uuid** | | [required] | + +### Return type + +[**crate::models::CloudGamesCreateCloudTokenResponse**](CloudGamesCreateCloudTokenResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust-cli/docs/ServersApi.md b/sdks/full/rust-cli/docs/ServersApi.md new file mode 100644 index 0000000000..40ca6fcb9d --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersApi.md @@ -0,0 +1,133 @@ +# \ServersApi + +All URIs are relative to *https://api.rivet.gg* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**servers_create**](ServersApi.md#servers_create) | **POST** /servers | +[**servers_destroy**](ServersApi.md#servers_destroy) | **DELETE** /servers/{server_id} | +[**servers_get**](ServersApi.md#servers_get) | **GET** /servers/{server_id} | +[**servers_list**](ServersApi.md#servers_list) | **GET** /servers/list | + + + +## servers_create + +> crate::models::ServersCreateServerResponse servers_create(servers_create_server_request) + + +Create a new dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**servers_create_server_request** | [**ServersCreateServerRequest**](ServersCreateServerRequest.md) | | [required] | + +### Return type + +[**crate::models::ServersCreateServerResponse**](ServersCreateServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_destroy + +> crate::models::ServersDestroyServerResponse servers_destroy(server_id, override_kill_timeout) + + +Destroy a dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**server_id** | **uuid::Uuid** | The id of the server to destroy | [required] | +**override_kill_timeout** | Option<**i64**> | The duration to wait for in milliseconds before killing the server. This should be used to override the default kill timeout if a faster time is needed, say for ignoring a graceful shutdown. | | + +### Return type + +[**crate::models::ServersDestroyServerResponse**](ServersDestroyServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_get + +> crate::models::ServersGetServerResponse servers_get(server_id) + + +Gets a dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**server_id** | **uuid::Uuid** | The id of the server to destroy | [required] | + +### Return type + +[**crate::models::ServersGetServerResponse**](ServersGetServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_list + +> crate::models::ServersListServersResponse servers_list(tags) + + +Lists all servers associated with the token used. Can be filtered by tags in the query string. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**tags** | Option<**String**> | | | + +### Return type + +[**crate::models::ServersListServersResponse**](ServersListServersResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust-cli/docs/ServersBuildCompression.md b/sdks/full/rust-cli/docs/ServersBuildCompression.md new file mode 100644 index 0000000000..3e1b76a053 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersBuildCompression.md @@ -0,0 +1,10 @@ +# ServersBuildCompression + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersBuildKind.md b/sdks/full/rust-cli/docs/ServersBuildKind.md new file mode 100644 index 0000000000..6fc399d0e0 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersBuildKind.md @@ -0,0 +1,10 @@ +# ServersBuildKind + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersBuildsApi.md b/sdks/full/rust-cli/docs/ServersBuildsApi.md new file mode 100644 index 0000000000..e86d50fc71 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersBuildsApi.md @@ -0,0 +1,101 @@ +# \ServersBuildsApi + +All URIs are relative to *https://api.rivet.gg* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**servers_builds_complete_build**](ServersBuildsApi.md#servers_builds_complete_build) | **POST** /servers/uploads/{upload_id}/complete | +[**servers_builds_list_builds**](ServersBuildsApi.md#servers_builds_list_builds) | **GET** /servers/builds | +[**servers_builds_prepare_build**](ServersBuildsApi.md#servers_builds_prepare_build) | **POST** /servers/builds | + + + +## servers_builds_complete_build + +> servers_builds_complete_build(upload_id) + + +Marks an upload as complete. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**upload_id** | **uuid::Uuid** | | [required] | + +### Return type + + (empty response body) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_builds_list_builds + +> crate::models::ServersListBuildsResponse servers_builds_list_builds(tags) + + +Lists all builds of the game associated with the token used. Can be filtered by tags in the query string. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**tags** | Option<**String**> | | | + +### Return type + +[**crate::models::ServersListBuildsResponse**](ServersListBuildsResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_builds_prepare_build + +> crate::models::ServersCreateBuildResponse servers_builds_prepare_build(servers_create_build_request) + + +Creates a new game build for the given game. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**servers_create_build_request** | [**ServersCreateBuildRequest**](ServersCreateBuildRequest.md) | | [required] | + +### Return type + +[**crate::models::ServersCreateBuildResponse**](ServersCreateBuildResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust-cli/docs/ServersCreateBuildRequest.md b/sdks/full/rust-cli/docs/ServersCreateBuildRequest.md new file mode 100644 index 0000000000..1c96d10698 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersCreateBuildRequest.md @@ -0,0 +1,17 @@ +# ServersCreateBuildRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**compression** | Option<[**crate::models::ServersBuildCompression**](ServersBuildCompression.md)> | | [optional] +**display_name** | **String** | Represent a resource's readable display name. | +**image_file** | [**crate::models::UploadPrepareFile**](UploadPrepareFile.md) | | +**image_tag** | **String** | A tag given to the game build. | +**kind** | Option<[**crate::models::ServersBuildKind**](ServersBuildKind.md)> | | [optional] +**multipart_upload** | Option<**bool**> | | [optional] +**tags** | Option<[**serde_json::Value**](.md)> | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersCreateBuildResponse.md b/sdks/full/rust-cli/docs/ServersCreateBuildResponse.md new file mode 100644 index 0000000000..e5c3a19f8c --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersCreateBuildResponse.md @@ -0,0 +1,14 @@ +# ServersCreateBuildResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**build_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**image_presigned_request** | Option<[**crate::models::UploadPresignedRequest**](UploadPresignedRequest.md)> | | [optional] +**image_presigned_requests** | Option<[**Vec**](UploadPresignedRequest.md)> | | [optional] +**upload_id** | [**uuid::Uuid**](uuid::Uuid.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersCreateServerNetworkRequest.md b/sdks/full/rust-cli/docs/ServersCreateServerNetworkRequest.md new file mode 100644 index 0000000000..633ac595fa --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersCreateServerNetworkRequest.md @@ -0,0 +1,12 @@ +# ServersCreateServerNetworkRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**mode** | Option<[**crate::models::ServersNetworkMode**](ServersNetworkMode.md)> | | [optional] +**ports** | [**::std::collections::HashMap**](ServersCreateServerPortRequest.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersCreateServerPortRequest.md b/sdks/full/rust-cli/docs/ServersCreateServerPortRequest.md new file mode 100644 index 0000000000..97c550c60d --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersCreateServerPortRequest.md @@ -0,0 +1,13 @@ +# ServersCreateServerPortRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**internal_port** | Option<**i32**> | | [optional] +**protocol** | [**crate::models::ServersPortProtocol**](ServersPortProtocol.md) | | +**routing** | Option<[**crate::models::ServersPortRouting**](ServersPortRouting.md)> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersCreateServerRequest.md b/sdks/full/rust-cli/docs/ServersCreateServerRequest.md new file mode 100644 index 0000000000..382da90515 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersCreateServerRequest.md @@ -0,0 +1,19 @@ +# ServersCreateServerRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**arguments** | Option<**Vec**> | | [optional] +**datacenter** | **String** | The name ID of the datacenter | +**environment** | Option<**::std::collections::HashMap**> | | [optional] +**image_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**kill_timeout** | Option<**i64**> | The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. | [optional] +**network** | [**crate::models::ServersCreateServerNetworkRequest**](ServersCreateServerNetworkRequest.md) | | +**resources** | [**crate::models::ServersResources**](ServersResources.md) | | +**tags** | Option<[**serde_json::Value**](.md)> | | +**webhook_url** | Option<**String**> | A url to send to which events from the server running will be sent | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersCreateServerResponse.md b/sdks/full/rust-cli/docs/ServersCreateServerResponse.md new file mode 100644 index 0000000000..29bae37bf7 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersCreateServerResponse.md @@ -0,0 +1,11 @@ +# ServersCreateServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**server** | [**crate::models::ServersServer**](ServersServer.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersDestroyServerResponse.md b/sdks/full/rust-cli/docs/ServersDestroyServerResponse.md new file mode 100644 index 0000000000..0cf193ed7a --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersDestroyServerResponse.md @@ -0,0 +1,11 @@ +# ServersDestroyServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**server_id** | [**uuid::Uuid**](uuid::Uuid.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersGetServerResponse.md b/sdks/full/rust-cli/docs/ServersGetServerResponse.md new file mode 100644 index 0000000000..f5e5e42f86 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersGetServerResponse.md @@ -0,0 +1,11 @@ +# ServersGetServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**server** | [**crate::models::ServersServer**](ServersServer.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersListBuildsResponse.md b/sdks/full/rust-cli/docs/ServersListBuildsResponse.md new file mode 100644 index 0000000000..2135664842 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersListBuildsResponse.md @@ -0,0 +1,11 @@ +# ServersListBuildsResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**builds** | [**Vec**](CloudBuildSummary.md) | A list of builds for the game associated with the token. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersListServersResponse.md b/sdks/full/rust-cli/docs/ServersListServersResponse.md new file mode 100644 index 0000000000..b1c8e2d66a --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersListServersResponse.md @@ -0,0 +1,11 @@ +# ServersListServersResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**servers** | [**Vec**](ServersServer.md) | A list of servers for the game associated with the token. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersNetwork.md b/sdks/full/rust-cli/docs/ServersNetwork.md new file mode 100644 index 0000000000..e1b819556d --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersNetwork.md @@ -0,0 +1,12 @@ +# ServersNetwork + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**mode** | Option<[**crate::models::ServersNetworkMode**](ServersNetworkMode.md)> | | [optional] +**ports** | [**::std::collections::HashMap**](ServersPort.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersNetworkMode.md b/sdks/full/rust-cli/docs/ServersNetworkMode.md new file mode 100644 index 0000000000..9fdd3e28c1 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersNetworkMode.md @@ -0,0 +1,10 @@ +# ServersNetworkMode + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersPort.md b/sdks/full/rust-cli/docs/ServersPort.md new file mode 100644 index 0000000000..5ade4ddd58 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersPort.md @@ -0,0 +1,15 @@ +# ServersPort + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**internal_port** | Option<**i32**> | | [optional] +**protocol** | [**crate::models::ServersPortProtocol**](ServersPortProtocol.md) | | +**public_hostname** | Option<**String**> | | [optional] +**public_port** | Option<**i32**> | | [optional] +**routing** | [**crate::models::ServersPortRouting**](ServersPortRouting.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersPortProtocol.md b/sdks/full/rust-cli/docs/ServersPortProtocol.md new file mode 100644 index 0000000000..595d11df8f --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersPortProtocol.md @@ -0,0 +1,10 @@ +# ServersPortProtocol + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersPortRouting.md b/sdks/full/rust-cli/docs/ServersPortRouting.md new file mode 100644 index 0000000000..1ac474c230 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersPortRouting.md @@ -0,0 +1,12 @@ +# ServersPortRouting + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**game_guard** | Option<[**serde_json::Value**](.md)> | | [optional] +**host** | Option<[**serde_json::Value**](.md)> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersResources.md b/sdks/full/rust-cli/docs/ServersResources.md new file mode 100644 index 0000000000..3b3f9d6882 --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersResources.md @@ -0,0 +1,12 @@ +# ServersResources + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**cpu** | **i32** | The number of CPU cores in millicores, or 1/1000 of a core. For example, 1/8 of a core would be 125 millicores, and 1 core would be 1000 millicores. | +**memory** | **i32** | The amount of memory in megabytes | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/docs/ServersServer.md b/sdks/full/rust-cli/docs/ServersServer.md new file mode 100644 index 0000000000..f8c79759cf --- /dev/null +++ b/sdks/full/rust-cli/docs/ServersServer.md @@ -0,0 +1,23 @@ +# ServersServer + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**arguments** | Option<**Vec**> | | [optional] +**cluster_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**create_ts** | **i64** | | +**datacenter_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**destroy_ts** | Option<**i64**> | | [optional] +**environment** | Option<**::std::collections::HashMap**> | | [optional] +**game_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**image_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**kill_timeout** | Option<**i64**> | The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. | [optional] +**network** | [**crate::models::ServersNetwork**](ServersNetwork.md) | | +**resources** | [**crate::models::ServersResources**](ServersResources.md) | | +**server_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**tags** | Option<[**serde_json::Value**](.md)> | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust-cli/src/apis/cloud_games_tokens_api.rs b/sdks/full/rust-cli/src/apis/cloud_games_tokens_api.rs index 42318b5460..5541d05823 100644 --- a/sdks/full/rust-cli/src/apis/cloud_games_tokens_api.rs +++ b/sdks/full/rust-cli/src/apis/cloud_games_tokens_api.rs @@ -28,6 +28,19 @@ pub enum CloudGamesTokensCreateCloudTokenError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`cloud_games_tokens_create_service_token`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CloudGamesTokensCreateServiceTokenError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + /// Creates a new game cloud token. pub async fn cloud_games_tokens_create_cloud_token(configuration: &configuration::Configuration, game_id: &str) -> Result> { @@ -60,3 +73,34 @@ pub async fn cloud_games_tokens_create_cloud_token(configuration: &configuration } } +/// Creates a new game service token. +pub async fn cloud_games_tokens_create_service_token(configuration: &configuration::Configuration, game_id: &str) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/cloud/games/{game_id}/tokens/service", local_var_configuration.base_path, game_id=crate::apis::urlencode(game_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust-cli/src/apis/mod.rs b/sdks/full/rust-cli/src/apis/mod.rs index e6401fd8f2..d267ce82f4 100644 --- a/sdks/full/rust-cli/src/apis/mod.rs +++ b/sdks/full/rust-cli/src/apis/mod.rs @@ -129,5 +129,7 @@ pub mod matchmaker_regions_api; pub mod portal_games_api; pub mod provision_datacenters_api; pub mod provision_servers_api; +pub mod servers_api; +pub mod servers_builds_api; pub mod configuration; diff --git a/sdks/full/rust-cli/src/apis/servers_api.rs b/sdks/full/rust-cli/src/apis/servers_api.rs new file mode 100644 index 0000000000..aa98e6ee7e --- /dev/null +++ b/sdks/full/rust-cli/src/apis/servers_api.rs @@ -0,0 +1,201 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +use reqwest; + +use crate::apis::ResponseContent; +use super::{Error, configuration}; + + +/// struct for typed errors of method [`servers_create`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersCreateError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_destroy`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersDestroyError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_get`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersGetError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_list`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersListError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + + +/// Create a new dynamic server. +pub async fn servers_create(configuration: &configuration::Configuration, servers_create_server_request: crate::models::ServersCreateServerRequest) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.json(&servers_create_server_request); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Destroy a dynamic server. +pub async fn servers_destroy(configuration: &configuration::Configuration, server_id: &str, override_kill_timeout: Option) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/{server_id}", local_var_configuration.base_path, server_id=crate::apis::urlencode(server_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::DELETE, local_var_uri_str.as_str()); + + if let Some(ref local_var_str) = override_kill_timeout { + local_var_req_builder = local_var_req_builder.query(&[("override_kill_timeout", &local_var_str.to_string())]); + } + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Gets a dynamic server. +pub async fn servers_get(configuration: &configuration::Configuration, server_id: &str) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/{server_id}", local_var_configuration.base_path, server_id=crate::apis::urlencode(server_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Lists all servers associated with the token used. Can be filtered by tags in the query string. +pub async fn servers_list(configuration: &configuration::Configuration, tags: Option<&str>) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/list", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_str) = tags { + local_var_req_builder = local_var_req_builder.query(&[("tags", &local_var_str.to_string())]); + } + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust-cli/src/apis/servers_builds_api.rs b/sdks/full/rust-cli/src/apis/servers_builds_api.rs new file mode 100644 index 0000000000..1b487540c6 --- /dev/null +++ b/sdks/full/rust-cli/src/apis/servers_builds_api.rs @@ -0,0 +1,154 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +use reqwest; + +use crate::apis::ResponseContent; +use super::{Error, configuration}; + + +/// struct for typed errors of method [`servers_builds_complete_build`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersBuildsCompleteBuildError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_builds_list_builds`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersBuildsListBuildsError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_builds_prepare_build`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersBuildsPrepareBuildError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + + +/// Marks an upload as complete. +pub async fn servers_builds_complete_build(configuration: &configuration::Configuration, upload_id: &str) -> Result<(), Error> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/uploads/{upload_id}/complete", local_var_configuration.base_path, upload_id=crate::apis::urlencode(upload_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + Ok(()) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Lists all builds of the game associated with the token used. Can be filtered by tags in the query string. +pub async fn servers_builds_list_builds(configuration: &configuration::Configuration, tags: Option<&str>) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/builds", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_str) = tags { + local_var_req_builder = local_var_req_builder.query(&[("tags", &local_var_str.to_string())]); + } + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Creates a new game build for the given game. +pub async fn servers_builds_prepare_build(configuration: &configuration::Configuration, servers_create_build_request: crate::models::ServersCreateBuildRequest) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/builds", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.json(&servers_create_build_request); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust-cli/src/models/cloud_build_summary.rs b/sdks/full/rust-cli/src/models/cloud_build_summary.rs index e11928b5f4..2191748cf3 100644 --- a/sdks/full/rust-cli/src/models/cloud_build_summary.rs +++ b/sdks/full/rust-cli/src/models/cloud_build_summary.rs @@ -28,19 +28,23 @@ pub struct CloudBuildSummary { /// Represent a resource's readable display name. #[serde(rename = "display_name")] pub display_name: String, + /// Tags of this build + #[serde(rename = "tags")] + pub tags: ::std::collections::HashMap, #[serde(rename = "upload_id")] pub upload_id: uuid::Uuid, } impl CloudBuildSummary { /// A build summary. - pub fn new(build_id: uuid::Uuid, complete: bool, content_length: i64, create_ts: String, display_name: String, upload_id: uuid::Uuid) -> CloudBuildSummary { + pub fn new(build_id: uuid::Uuid, complete: bool, content_length: i64, create_ts: String, display_name: String, tags: ::std::collections::HashMap, upload_id: uuid::Uuid) -> CloudBuildSummary { CloudBuildSummary { build_id, complete, content_length, create_ts, display_name, + tags, upload_id, } } diff --git a/sdks/full/rust-cli/src/models/cloud_games_create_service_token_response.rs b/sdks/full/rust-cli/src/models/cloud_games_create_service_token_response.rs new file mode 100644 index 0000000000..7221b71310 --- /dev/null +++ b/sdks/full/rust-cli/src/models/cloud_games_create_service_token_response.rs @@ -0,0 +1,29 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct CloudGamesCreateServiceTokenResponse { + /// A JSON Web Token. + #[serde(rename = "token")] + pub token: String, +} + +impl CloudGamesCreateServiceTokenResponse { + pub fn new(token: String) -> CloudGamesCreateServiceTokenResponse { + CloudGamesCreateServiceTokenResponse { + token, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/mod.rs b/sdks/full/rust-cli/src/models/mod.rs index cca9c150d4..a04299340c 100644 --- a/sdks/full/rust-cli/src/models/mod.rs +++ b/sdks/full/rust-cli/src/models/mod.rs @@ -132,6 +132,8 @@ pub mod cloud_games_create_game_version_request; pub use self::cloud_games_create_game_version_request::CloudGamesCreateGameVersionRequest; pub mod cloud_games_create_game_version_response; pub use self::cloud_games_create_game_version_response::CloudGamesCreateGameVersionResponse; +pub mod cloud_games_create_service_token_response; +pub use self::cloud_games_create_service_token_response::CloudGamesCreateServiceTokenResponse; pub mod cloud_games_delete_matchmaker_lobby_response; pub use self::cloud_games_delete_matchmaker_lobby_response::CloudGamesDeleteMatchmakerLobbyResponse; pub mod cloud_games_export_lobby_logs_request; @@ -604,6 +606,44 @@ pub mod provision_datacenters_get_tls_response; pub use self::provision_datacenters_get_tls_response::ProvisionDatacentersGetTlsResponse; pub mod provision_servers_get_info_response; pub use self::provision_servers_get_info_response::ProvisionServersGetInfoResponse; +pub mod servers_build_compression; +pub use self::servers_build_compression::ServersBuildCompression; +pub mod servers_build_kind; +pub use self::servers_build_kind::ServersBuildKind; +pub mod servers_create_build_request; +pub use self::servers_create_build_request::ServersCreateBuildRequest; +pub mod servers_create_build_response; +pub use self::servers_create_build_response::ServersCreateBuildResponse; +pub mod servers_create_server_network_request; +pub use self::servers_create_server_network_request::ServersCreateServerNetworkRequest; +pub mod servers_create_server_port_request; +pub use self::servers_create_server_port_request::ServersCreateServerPortRequest; +pub mod servers_create_server_request; +pub use self::servers_create_server_request::ServersCreateServerRequest; +pub mod servers_create_server_response; +pub use self::servers_create_server_response::ServersCreateServerResponse; +pub mod servers_destroy_server_response; +pub use self::servers_destroy_server_response::ServersDestroyServerResponse; +pub mod servers_get_server_response; +pub use self::servers_get_server_response::ServersGetServerResponse; +pub mod servers_list_builds_response; +pub use self::servers_list_builds_response::ServersListBuildsResponse; +pub mod servers_list_servers_response; +pub use self::servers_list_servers_response::ServersListServersResponse; +pub mod servers_network; +pub use self::servers_network::ServersNetwork; +pub mod servers_network_mode; +pub use self::servers_network_mode::ServersNetworkMode; +pub mod servers_port; +pub use self::servers_port::ServersPort; +pub mod servers_port_protocol; +pub use self::servers_port_protocol::ServersPortProtocol; +pub mod servers_port_routing; +pub use self::servers_port_routing::ServersPortRouting; +pub mod servers_resources; +pub use self::servers_resources::ServersResources; +pub mod servers_server; +pub use self::servers_server::ServersServer; pub mod upload_prepare_file; pub use self::upload_prepare_file::UploadPrepareFile; pub mod upload_presigned_request; diff --git a/sdks/full/rust-cli/src/models/servers_build_compression.rs b/sdks/full/rust-cli/src/models/servers_build_compression.rs new file mode 100644 index 0000000000..fd549d6279 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_build_compression.rs @@ -0,0 +1,39 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersBuildCompression { + #[serde(rename = "none")] + None, + #[serde(rename = "lz4")] + Lz4, + +} + +impl ToString for ServersBuildCompression { + fn to_string(&self) -> String { + match self { + Self::None => String::from("none"), + Self::Lz4 => String::from("lz4"), + } + } +} + +impl Default for ServersBuildCompression { + fn default() -> ServersBuildCompression { + Self::None + } +} + + + + diff --git a/sdks/full/rust-cli/src/models/servers_build_kind.rs b/sdks/full/rust-cli/src/models/servers_build_kind.rs new file mode 100644 index 0000000000..853abbb0e7 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_build_kind.rs @@ -0,0 +1,39 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersBuildKind { + #[serde(rename = "docker_image")] + DockerImage, + #[serde(rename = "oci_bundle")] + OciBundle, + +} + +impl ToString for ServersBuildKind { + fn to_string(&self) -> String { + match self { + Self::DockerImage => String::from("docker_image"), + Self::OciBundle => String::from("oci_bundle"), + } + } +} + +impl Default for ServersBuildKind { + fn default() -> ServersBuildKind { + Self::DockerImage + } +} + + + + diff --git a/sdks/full/rust-cli/src/models/servers_create_build_request.rs b/sdks/full/rust-cli/src/models/servers_create_build_request.rs new file mode 100644 index 0000000000..395fe9aeb6 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_create_build_request.rs @@ -0,0 +1,48 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateBuildRequest { + #[serde(rename = "compression", skip_serializing_if = "Option::is_none")] + pub compression: Option, + /// Represent a resource's readable display name. + #[serde(rename = "display_name")] + pub display_name: String, + #[serde(rename = "image_file")] + pub image_file: Box, + /// A tag given to the game build. + #[serde(rename = "image_tag")] + pub image_tag: String, + #[serde(rename = "kind", skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(rename = "multipart_upload", skip_serializing_if = "Option::is_none")] + pub multipart_upload: Option, + #[serde(rename = "tags", deserialize_with = "Option::deserialize")] + pub tags: Option, +} + +impl ServersCreateBuildRequest { + pub fn new(display_name: String, image_file: crate::models::UploadPrepareFile, image_tag: String, tags: Option) -> ServersCreateBuildRequest { + ServersCreateBuildRequest { + compression: None, + display_name, + image_file: Box::new(image_file), + image_tag, + kind: None, + multipart_upload: None, + tags, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_create_build_response.rs b/sdks/full/rust-cli/src/models/servers_create_build_response.rs new file mode 100644 index 0000000000..551e3030bf --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_create_build_response.rs @@ -0,0 +1,37 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateBuildResponse { + #[serde(rename = "build_id")] + pub build_id: uuid::Uuid, + #[serde(rename = "image_presigned_request", skip_serializing_if = "Option::is_none")] + pub image_presigned_request: Option>, + #[serde(rename = "image_presigned_requests", skip_serializing_if = "Option::is_none")] + pub image_presigned_requests: Option>, + #[serde(rename = "upload_id")] + pub upload_id: uuid::Uuid, +} + +impl ServersCreateBuildResponse { + pub fn new(build_id: uuid::Uuid, upload_id: uuid::Uuid) -> ServersCreateBuildResponse { + ServersCreateBuildResponse { + build_id, + image_presigned_request: None, + image_presigned_requests: None, + upload_id, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_create_server_network_request.rs b/sdks/full/rust-cli/src/models/servers_create_server_network_request.rs new file mode 100644 index 0000000000..d469cc181d --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_create_server_network_request.rs @@ -0,0 +1,31 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerNetworkRequest { + #[serde(rename = "mode", skip_serializing_if = "Option::is_none")] + pub mode: Option, + #[serde(rename = "ports")] + pub ports: ::std::collections::HashMap, +} + +impl ServersCreateServerNetworkRequest { + pub fn new(ports: ::std::collections::HashMap) -> ServersCreateServerNetworkRequest { + ServersCreateServerNetworkRequest { + mode: None, + ports, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_create_server_port_request.rs b/sdks/full/rust-cli/src/models/servers_create_server_port_request.rs new file mode 100644 index 0000000000..d818685f71 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_create_server_port_request.rs @@ -0,0 +1,34 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerPortRequest { + #[serde(rename = "internal_port", skip_serializing_if = "Option::is_none")] + pub internal_port: Option, + #[serde(rename = "protocol")] + pub protocol: crate::models::ServersPortProtocol, + #[serde(rename = "routing", skip_serializing_if = "Option::is_none")] + pub routing: Option>, +} + +impl ServersCreateServerPortRequest { + pub fn new(protocol: crate::models::ServersPortProtocol) -> ServersCreateServerPortRequest { + ServersCreateServerPortRequest { + internal_port: None, + protocol, + routing: None, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_create_server_request.rs b/sdks/full/rust-cli/src/models/servers_create_server_request.rs new file mode 100644 index 0000000000..e50b4b9541 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_create_server_request.rs @@ -0,0 +1,55 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerRequest { + #[serde(rename = "arguments", skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + /// The name ID of the datacenter + #[serde(rename = "datacenter")] + pub datacenter: String, + #[serde(rename = "environment", skip_serializing_if = "Option::is_none")] + pub environment: Option<::std::collections::HashMap>, + #[serde(rename = "image_id")] + pub image_id: uuid::Uuid, + /// The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. + #[serde(rename = "kill_timeout", skip_serializing_if = "Option::is_none")] + pub kill_timeout: Option, + #[serde(rename = "network")] + pub network: Box, + #[serde(rename = "resources")] + pub resources: Box, + #[serde(rename = "tags", deserialize_with = "Option::deserialize")] + pub tags: Option, + /// A url to send to which events from the server running will be sent + #[serde(rename = "webhook_url", skip_serializing_if = "Option::is_none")] + pub webhook_url: Option, +} + +impl ServersCreateServerRequest { + pub fn new(datacenter: String, image_id: uuid::Uuid, network: crate::models::ServersCreateServerNetworkRequest, resources: crate::models::ServersResources, tags: Option) -> ServersCreateServerRequest { + ServersCreateServerRequest { + arguments: None, + datacenter, + environment: None, + image_id, + kill_timeout: None, + network: Box::new(network), + resources: Box::new(resources), + tags, + webhook_url: None, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_create_server_response.rs b/sdks/full/rust-cli/src/models/servers_create_server_response.rs new file mode 100644 index 0000000000..3c32cbd896 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_create_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerResponse { + #[serde(rename = "server")] + pub server: Box, +} + +impl ServersCreateServerResponse { + pub fn new(server: crate::models::ServersServer) -> ServersCreateServerResponse { + ServersCreateServerResponse { + server: Box::new(server), + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_destroy_server_response.rs b/sdks/full/rust-cli/src/models/servers_destroy_server_response.rs new file mode 100644 index 0000000000..f993e53a94 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_destroy_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersDestroyServerResponse { + #[serde(rename = "server_id")] + pub server_id: uuid::Uuid, +} + +impl ServersDestroyServerResponse { + pub fn new(server_id: uuid::Uuid) -> ServersDestroyServerResponse { + ServersDestroyServerResponse { + server_id, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_get_server_response.rs b/sdks/full/rust-cli/src/models/servers_get_server_response.rs new file mode 100644 index 0000000000..2f017da813 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_get_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersGetServerResponse { + #[serde(rename = "server")] + pub server: Box, +} + +impl ServersGetServerResponse { + pub fn new(server: crate::models::ServersServer) -> ServersGetServerResponse { + ServersGetServerResponse { + server: Box::new(server), + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_list_builds_response.rs b/sdks/full/rust-cli/src/models/servers_list_builds_response.rs new file mode 100644 index 0000000000..3bfdec824f --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_list_builds_response.rs @@ -0,0 +1,29 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersListBuildsResponse { + /// A list of builds for the game associated with the token. + #[serde(rename = "builds")] + pub builds: Vec, +} + +impl ServersListBuildsResponse { + pub fn new(builds: Vec) -> ServersListBuildsResponse { + ServersListBuildsResponse { + builds, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_list_servers_response.rs b/sdks/full/rust-cli/src/models/servers_list_servers_response.rs new file mode 100644 index 0000000000..202010aa26 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_list_servers_response.rs @@ -0,0 +1,29 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersListServersResponse { + /// A list of servers for the game associated with the token. + #[serde(rename = "servers")] + pub servers: Vec, +} + +impl ServersListServersResponse { + pub fn new(servers: Vec) -> ServersListServersResponse { + ServersListServersResponse { + servers, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_network.rs b/sdks/full/rust-cli/src/models/servers_network.rs new file mode 100644 index 0000000000..97bade5673 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_network.rs @@ -0,0 +1,31 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersNetwork { + #[serde(rename = "mode", skip_serializing_if = "Option::is_none")] + pub mode: Option, + #[serde(rename = "ports")] + pub ports: ::std::collections::HashMap, +} + +impl ServersNetwork { + pub fn new(ports: ::std::collections::HashMap) -> ServersNetwork { + ServersNetwork { + mode: None, + ports, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_network_mode.rs b/sdks/full/rust-cli/src/models/servers_network_mode.rs new file mode 100644 index 0000000000..bbb9bd9a5b --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_network_mode.rs @@ -0,0 +1,39 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersNetworkMode { + #[serde(rename = "bridge")] + Bridge, + #[serde(rename = "host")] + Host, + +} + +impl ToString for ServersNetworkMode { + fn to_string(&self) -> String { + match self { + Self::Bridge => String::from("bridge"), + Self::Host => String::from("host"), + } + } +} + +impl Default for ServersNetworkMode { + fn default() -> ServersNetworkMode { + Self::Bridge + } +} + + + + diff --git a/sdks/full/rust-cli/src/models/servers_port.rs b/sdks/full/rust-cli/src/models/servers_port.rs new file mode 100644 index 0000000000..ab305ac1e4 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_port.rs @@ -0,0 +1,40 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersPort { + #[serde(rename = "internal_port", skip_serializing_if = "Option::is_none")] + pub internal_port: Option, + #[serde(rename = "protocol")] + pub protocol: crate::models::ServersPortProtocol, + #[serde(rename = "public_hostname", skip_serializing_if = "Option::is_none")] + pub public_hostname: Option, + #[serde(rename = "public_port", skip_serializing_if = "Option::is_none")] + pub public_port: Option, + #[serde(rename = "routing")] + pub routing: Box, +} + +impl ServersPort { + pub fn new(protocol: crate::models::ServersPortProtocol, routing: crate::models::ServersPortRouting) -> ServersPort { + ServersPort { + internal_port: None, + protocol, + public_hostname: None, + public_port: None, + routing: Box::new(routing), + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_port_protocol.rs b/sdks/full/rust-cli/src/models/servers_port_protocol.rs new file mode 100644 index 0000000000..e2e8c326c9 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_port_protocol.rs @@ -0,0 +1,48 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersPortProtocol { + #[serde(rename = "http")] + Http, + #[serde(rename = "https")] + Https, + #[serde(rename = "tcp")] + Tcp, + #[serde(rename = "tcp_tls")] + TcpTls, + #[serde(rename = "udp")] + Udp, + +} + +impl ToString for ServersPortProtocol { + fn to_string(&self) -> String { + match self { + Self::Http => String::from("http"), + Self::Https => String::from("https"), + Self::Tcp => String::from("tcp"), + Self::TcpTls => String::from("tcp_tls"), + Self::Udp => String::from("udp"), + } + } +} + +impl Default for ServersPortProtocol { + fn default() -> ServersPortProtocol { + Self::Http + } +} + + + + diff --git a/sdks/full/rust-cli/src/models/servers_port_routing.rs b/sdks/full/rust-cli/src/models/servers_port_routing.rs new file mode 100644 index 0000000000..83f0846fc4 --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_port_routing.rs @@ -0,0 +1,31 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersPortRouting { + #[serde(rename = "game_guard", skip_serializing_if = "Option::is_none")] + pub game_guard: Option, + #[serde(rename = "host", skip_serializing_if = "Option::is_none")] + pub host: Option, +} + +impl ServersPortRouting { + pub fn new() -> ServersPortRouting { + ServersPortRouting { + game_guard: None, + host: None, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_resources.rs b/sdks/full/rust-cli/src/models/servers_resources.rs new file mode 100644 index 0000000000..d29a3cb0ac --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_resources.rs @@ -0,0 +1,33 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersResources { + /// The number of CPU cores in millicores, or 1/1000 of a core. For example, 1/8 of a core would be 125 millicores, and 1 core would be 1000 millicores. + #[serde(rename = "cpu")] + pub cpu: i32, + /// The amount of memory in megabytes + #[serde(rename = "memory")] + pub memory: i32, +} + +impl ServersResources { + pub fn new(cpu: i32, memory: i32) -> ServersResources { + ServersResources { + cpu, + memory, + } + } +} + + diff --git a/sdks/full/rust-cli/src/models/servers_server.rs b/sdks/full/rust-cli/src/models/servers_server.rs new file mode 100644 index 0000000000..cb93decaee --- /dev/null +++ b/sdks/full/rust-cli/src/models/servers_server.rs @@ -0,0 +1,65 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersServer { + #[serde(rename = "arguments", skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + #[serde(rename = "cluster_id")] + pub cluster_id: uuid::Uuid, + #[serde(rename = "create_ts")] + pub create_ts: i64, + #[serde(rename = "datacenter_id")] + pub datacenter_id: uuid::Uuid, + #[serde(rename = "destroy_ts", skip_serializing_if = "Option::is_none")] + pub destroy_ts: Option, + #[serde(rename = "environment", skip_serializing_if = "Option::is_none")] + pub environment: Option<::std::collections::HashMap>, + #[serde(rename = "game_id")] + pub game_id: uuid::Uuid, + #[serde(rename = "image_id")] + pub image_id: uuid::Uuid, + /// The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. + #[serde(rename = "kill_timeout", skip_serializing_if = "Option::is_none")] + pub kill_timeout: Option, + #[serde(rename = "network")] + pub network: Box, + #[serde(rename = "resources")] + pub resources: Box, + #[serde(rename = "server_id")] + pub server_id: uuid::Uuid, + #[serde(rename = "tags", deserialize_with = "Option::deserialize")] + pub tags: Option, +} + +impl ServersServer { + pub fn new(cluster_id: uuid::Uuid, create_ts: i64, datacenter_id: uuid::Uuid, game_id: uuid::Uuid, image_id: uuid::Uuid, network: crate::models::ServersNetwork, resources: crate::models::ServersResources, server_id: uuid::Uuid, tags: Option) -> ServersServer { + ServersServer { + arguments: None, + cluster_id, + create_ts, + datacenter_id, + destroy_ts: None, + environment: None, + game_id, + image_id, + kill_timeout: None, + network: Box::new(network), + resources: Box::new(resources), + server_id, + tags, + } + } +} + + diff --git a/sdks/full/rust/.openapi-generator/FILES b/sdks/full/rust/.openapi-generator/FILES index dda235bb7e..0a29db9a8c 100644 --- a/sdks/full/rust/.openapi-generator/FILES +++ b/sdks/full/rust/.openapi-generator/FILES @@ -84,6 +84,7 @@ docs/CloudGamesCreateGameRequest.md docs/CloudGamesCreateGameResponse.md docs/CloudGamesCreateGameVersionRequest.md docs/CloudGamesCreateGameVersionResponse.md +docs/CloudGamesCreateServiceTokenResponse.md docs/CloudGamesDeleteMatchmakerLobbyResponse.md docs/CloudGamesExportLobbyLogsRequest.md docs/CloudGamesExportLobbyLogsResponse.md @@ -345,6 +346,27 @@ docs/ProvisionDatacentersApi.md docs/ProvisionDatacentersGetTlsResponse.md docs/ProvisionServersApi.md docs/ProvisionServersGetInfoResponse.md +docs/ServersApi.md +docs/ServersBuildCompression.md +docs/ServersBuildKind.md +docs/ServersBuildsApi.md +docs/ServersCreateBuildRequest.md +docs/ServersCreateBuildResponse.md +docs/ServersCreateServerNetworkRequest.md +docs/ServersCreateServerPortRequest.md +docs/ServersCreateServerRequest.md +docs/ServersCreateServerResponse.md +docs/ServersDestroyServerResponse.md +docs/ServersGetServerResponse.md +docs/ServersListBuildsResponse.md +docs/ServersListServersResponse.md +docs/ServersNetwork.md +docs/ServersNetworkMode.md +docs/ServersPort.md +docs/ServersPortProtocol.md +docs/ServersPortRouting.md +docs/ServersResources.md +docs/ServersServer.md docs/UploadPrepareFile.md docs/UploadPresignedRequest.md docs/ValidationError.md @@ -391,6 +413,8 @@ src/apis/mod.rs src/apis/portal_games_api.rs src/apis/provision_datacenters_api.rs src/apis/provision_servers_api.rs +src/apis/servers_api.rs +src/apis/servers_builds_api.rs src/lib.rs src/models/admin_clusters_build_delivery_method.rs src/models/admin_clusters_cluster.rs @@ -459,6 +483,7 @@ src/models/cloud_games_create_game_request.rs src/models/cloud_games_create_game_response.rs src/models/cloud_games_create_game_version_request.rs src/models/cloud_games_create_game_version_response.rs +src/models/cloud_games_create_service_token_response.rs src/models/cloud_games_delete_matchmaker_lobby_response.rs src/models/cloud_games_export_lobby_logs_request.rs src/models/cloud_games_export_lobby_logs_response.rs @@ -696,6 +721,25 @@ src/models/portal_notification_register_service.rs src/models/portal_notification_unregister_service.rs src/models/provision_datacenters_get_tls_response.rs src/models/provision_servers_get_info_response.rs +src/models/servers_build_compression.rs +src/models/servers_build_kind.rs +src/models/servers_create_build_request.rs +src/models/servers_create_build_response.rs +src/models/servers_create_server_network_request.rs +src/models/servers_create_server_port_request.rs +src/models/servers_create_server_request.rs +src/models/servers_create_server_response.rs +src/models/servers_destroy_server_response.rs +src/models/servers_get_server_response.rs +src/models/servers_list_builds_response.rs +src/models/servers_list_servers_response.rs +src/models/servers_network.rs +src/models/servers_network_mode.rs +src/models/servers_port.rs +src/models/servers_port_protocol.rs +src/models/servers_port_routing.rs +src/models/servers_resources.rs +src/models/servers_server.rs src/models/upload_prepare_file.rs src/models/upload_presigned_request.rs src/models/validation_error.rs diff --git a/sdks/full/rust/README.md b/sdks/full/rust/README.md index c7806fcdf6..4fecaf0a6e 100644 --- a/sdks/full/rust/README.md +++ b/sdks/full/rust/README.md @@ -82,6 +82,7 @@ Class | Method | HTTP request | Description *CloudGamesNamespacesLogsApi* | [**cloud_games_namespaces_logs_get_namespace_lobby**](docs/CloudGamesNamespacesLogsApi.md#cloud_games_namespaces_logs_get_namespace_lobby) | **GET** /cloud/games/{game_id}/namespaces/{namespace_id}/logs/lobbies/{lobby_id} | *CloudGamesNamespacesLogsApi* | [**cloud_games_namespaces_logs_list_namespace_lobbies**](docs/CloudGamesNamespacesLogsApi.md#cloud_games_namespaces_logs_list_namespace_lobbies) | **GET** /cloud/games/{game_id}/namespaces/{namespace_id}/logs/lobbies | *CloudGamesTokensApi* | [**cloud_games_tokens_create_cloud_token**](docs/CloudGamesTokensApi.md#cloud_games_tokens_create_cloud_token) | **POST** /cloud/games/{game_id}/tokens/cloud | +*CloudGamesTokensApi* | [**cloud_games_tokens_create_service_token**](docs/CloudGamesTokensApi.md#cloud_games_tokens_create_service_token) | **POST** /cloud/games/{game_id}/tokens/service | *CloudGamesVersionsApi* | [**cloud_games_versions_create_game_version**](docs/CloudGamesVersionsApi.md#cloud_games_versions_create_game_version) | **POST** /cloud/games/{game_id}/versions | *CloudGamesVersionsApi* | [**cloud_games_versions_get_game_version_by_id**](docs/CloudGamesVersionsApi.md#cloud_games_versions_get_game_version_by_id) | **GET** /cloud/games/{game_id}/versions/{version_id} | *CloudGamesVersionsApi* | [**cloud_games_versions_reserve_version_name**](docs/CloudGamesVersionsApi.md#cloud_games_versions_reserve_version_name) | **POST** /cloud/games/{game_id}/versions/reserve-name | @@ -166,6 +167,13 @@ Class | Method | HTTP request | Description *PortalGamesApi* | [**portal_games_get_game_profile**](docs/PortalGamesApi.md#portal_games_get_game_profile) | **GET** /portal/games/{game_name_id}/profile | *ProvisionDatacentersApi* | [**provision_datacenters_get_tls**](docs/ProvisionDatacentersApi.md#provision_datacenters_get_tls) | **GET** /datacenters/{datacenter_id}/tls | *ProvisionServersApi* | [**provision_servers_get_info**](docs/ProvisionServersApi.md#provision_servers_get_info) | **GET** /servers/{ip} | +*ServersApi* | [**servers_create**](docs/ServersApi.md#servers_create) | **POST** /servers | +*ServersApi* | [**servers_destroy**](docs/ServersApi.md#servers_destroy) | **DELETE** /servers/{server_id} | +*ServersApi* | [**servers_get**](docs/ServersApi.md#servers_get) | **GET** /servers/{server_id} | +*ServersApi* | [**servers_list**](docs/ServersApi.md#servers_list) | **GET** /servers/list | +*ServersBuildsApi* | [**servers_builds_complete_build**](docs/ServersBuildsApi.md#servers_builds_complete_build) | **POST** /servers/uploads/{upload_id}/complete | +*ServersBuildsApi* | [**servers_builds_list_builds**](docs/ServersBuildsApi.md#servers_builds_list_builds) | **GET** /servers/builds | +*ServersBuildsApi* | [**servers_builds_prepare_build**](docs/ServersBuildsApi.md#servers_builds_prepare_build) | **POST** /servers/builds | ## Documentation For Models @@ -237,6 +245,7 @@ Class | Method | HTTP request | Description - [CloudGamesCreateGameResponse](docs/CloudGamesCreateGameResponse.md) - [CloudGamesCreateGameVersionRequest](docs/CloudGamesCreateGameVersionRequest.md) - [CloudGamesCreateGameVersionResponse](docs/CloudGamesCreateGameVersionResponse.md) + - [CloudGamesCreateServiceTokenResponse](docs/CloudGamesCreateServiceTokenResponse.md) - [CloudGamesDeleteMatchmakerLobbyResponse](docs/CloudGamesDeleteMatchmakerLobbyResponse.md) - [CloudGamesExportLobbyLogsRequest](docs/CloudGamesExportLobbyLogsRequest.md) - [CloudGamesExportLobbyLogsResponse](docs/CloudGamesExportLobbyLogsResponse.md) @@ -473,6 +482,25 @@ Class | Method | HTTP request | Description - [PortalNotificationUnregisterService](docs/PortalNotificationUnregisterService.md) - [ProvisionDatacentersGetTlsResponse](docs/ProvisionDatacentersGetTlsResponse.md) - [ProvisionServersGetInfoResponse](docs/ProvisionServersGetInfoResponse.md) + - [ServersBuildCompression](docs/ServersBuildCompression.md) + - [ServersBuildKind](docs/ServersBuildKind.md) + - [ServersCreateBuildRequest](docs/ServersCreateBuildRequest.md) + - [ServersCreateBuildResponse](docs/ServersCreateBuildResponse.md) + - [ServersCreateServerNetworkRequest](docs/ServersCreateServerNetworkRequest.md) + - [ServersCreateServerPortRequest](docs/ServersCreateServerPortRequest.md) + - [ServersCreateServerRequest](docs/ServersCreateServerRequest.md) + - [ServersCreateServerResponse](docs/ServersCreateServerResponse.md) + - [ServersDestroyServerResponse](docs/ServersDestroyServerResponse.md) + - [ServersGetServerResponse](docs/ServersGetServerResponse.md) + - [ServersListBuildsResponse](docs/ServersListBuildsResponse.md) + - [ServersListServersResponse](docs/ServersListServersResponse.md) + - [ServersNetwork](docs/ServersNetwork.md) + - [ServersNetworkMode](docs/ServersNetworkMode.md) + - [ServersPort](docs/ServersPort.md) + - [ServersPortProtocol](docs/ServersPortProtocol.md) + - [ServersPortRouting](docs/ServersPortRouting.md) + - [ServersResources](docs/ServersResources.md) + - [ServersServer](docs/ServersServer.md) - [UploadPrepareFile](docs/UploadPrepareFile.md) - [UploadPresignedRequest](docs/UploadPresignedRequest.md) - [ValidationError](docs/ValidationError.md) diff --git a/sdks/full/rust/docs/CloudBuildSummary.md b/sdks/full/rust/docs/CloudBuildSummary.md index 6d91dd4ead..f3b76d934d 100644 --- a/sdks/full/rust/docs/CloudBuildSummary.md +++ b/sdks/full/rust/docs/CloudBuildSummary.md @@ -9,6 +9,7 @@ Name | Type | Description | Notes **content_length** | **i64** | Unsigned 64 bit integer. | **create_ts** | **String** | RFC3339 timestamp | **display_name** | **String** | Represent a resource's readable display name. | +**tags** | **::std::collections::HashMap** | Tags of this build | **upload_id** | [**uuid::Uuid**](uuid::Uuid.md) | | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/sdks/full/rust/docs/CloudGamesCreateServiceTokenResponse.md b/sdks/full/rust/docs/CloudGamesCreateServiceTokenResponse.md new file mode 100644 index 0000000000..c8d021b0f2 --- /dev/null +++ b/sdks/full/rust/docs/CloudGamesCreateServiceTokenResponse.md @@ -0,0 +1,11 @@ +# CloudGamesCreateServiceTokenResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**token** | **String** | A JSON Web Token. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/CloudGamesTokensApi.md b/sdks/full/rust/docs/CloudGamesTokensApi.md index c0ee6276fc..8c64a98652 100644 --- a/sdks/full/rust/docs/CloudGamesTokensApi.md +++ b/sdks/full/rust/docs/CloudGamesTokensApi.md @@ -5,6 +5,7 @@ All URIs are relative to *https://api.rivet.gg* Method | HTTP request | Description ------------- | ------------- | ------------- [**cloud_games_tokens_create_cloud_token**](CloudGamesTokensApi.md#cloud_games_tokens_create_cloud_token) | **POST** /cloud/games/{game_id}/tokens/cloud | +[**cloud_games_tokens_create_service_token**](CloudGamesTokensApi.md#cloud_games_tokens_create_service_token) | **POST** /cloud/games/{game_id}/tokens/service | @@ -37,3 +38,33 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +## cloud_games_tokens_create_service_token + +> crate::models::CloudGamesCreateCloudTokenResponse cloud_games_tokens_create_service_token(game_id) + + +Creates a new game service token. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**game_id** | **uuid::Uuid** | | [required] | + +### Return type + +[**crate::models::CloudGamesCreateCloudTokenResponse**](CloudGamesCreateCloudTokenResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust/docs/ServersApi.md b/sdks/full/rust/docs/ServersApi.md new file mode 100644 index 0000000000..40ca6fcb9d --- /dev/null +++ b/sdks/full/rust/docs/ServersApi.md @@ -0,0 +1,133 @@ +# \ServersApi + +All URIs are relative to *https://api.rivet.gg* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**servers_create**](ServersApi.md#servers_create) | **POST** /servers | +[**servers_destroy**](ServersApi.md#servers_destroy) | **DELETE** /servers/{server_id} | +[**servers_get**](ServersApi.md#servers_get) | **GET** /servers/{server_id} | +[**servers_list**](ServersApi.md#servers_list) | **GET** /servers/list | + + + +## servers_create + +> crate::models::ServersCreateServerResponse servers_create(servers_create_server_request) + + +Create a new dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**servers_create_server_request** | [**ServersCreateServerRequest**](ServersCreateServerRequest.md) | | [required] | + +### Return type + +[**crate::models::ServersCreateServerResponse**](ServersCreateServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_destroy + +> crate::models::ServersDestroyServerResponse servers_destroy(server_id, override_kill_timeout) + + +Destroy a dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**server_id** | **uuid::Uuid** | The id of the server to destroy | [required] | +**override_kill_timeout** | Option<**i64**> | The duration to wait for in milliseconds before killing the server. This should be used to override the default kill timeout if a faster time is needed, say for ignoring a graceful shutdown. | | + +### Return type + +[**crate::models::ServersDestroyServerResponse**](ServersDestroyServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_get + +> crate::models::ServersGetServerResponse servers_get(server_id) + + +Gets a dynamic server. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**server_id** | **uuid::Uuid** | The id of the server to destroy | [required] | + +### Return type + +[**crate::models::ServersGetServerResponse**](ServersGetServerResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_list + +> crate::models::ServersListServersResponse servers_list(tags) + + +Lists all servers associated with the token used. Can be filtered by tags in the query string. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**tags** | Option<**String**> | | | + +### Return type + +[**crate::models::ServersListServersResponse**](ServersListServersResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust/docs/ServersBuildCompression.md b/sdks/full/rust/docs/ServersBuildCompression.md new file mode 100644 index 0000000000..3e1b76a053 --- /dev/null +++ b/sdks/full/rust/docs/ServersBuildCompression.md @@ -0,0 +1,10 @@ +# ServersBuildCompression + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersBuildKind.md b/sdks/full/rust/docs/ServersBuildKind.md new file mode 100644 index 0000000000..6fc399d0e0 --- /dev/null +++ b/sdks/full/rust/docs/ServersBuildKind.md @@ -0,0 +1,10 @@ +# ServersBuildKind + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersBuildsApi.md b/sdks/full/rust/docs/ServersBuildsApi.md new file mode 100644 index 0000000000..e86d50fc71 --- /dev/null +++ b/sdks/full/rust/docs/ServersBuildsApi.md @@ -0,0 +1,101 @@ +# \ServersBuildsApi + +All URIs are relative to *https://api.rivet.gg* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**servers_builds_complete_build**](ServersBuildsApi.md#servers_builds_complete_build) | **POST** /servers/uploads/{upload_id}/complete | +[**servers_builds_list_builds**](ServersBuildsApi.md#servers_builds_list_builds) | **GET** /servers/builds | +[**servers_builds_prepare_build**](ServersBuildsApi.md#servers_builds_prepare_build) | **POST** /servers/builds | + + + +## servers_builds_complete_build + +> servers_builds_complete_build(upload_id) + + +Marks an upload as complete. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**upload_id** | **uuid::Uuid** | | [required] | + +### Return type + + (empty response body) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_builds_list_builds + +> crate::models::ServersListBuildsResponse servers_builds_list_builds(tags) + + +Lists all builds of the game associated with the token used. Can be filtered by tags in the query string. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**tags** | Option<**String**> | | | + +### Return type + +[**crate::models::ServersListBuildsResponse**](ServersListBuildsResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## servers_builds_prepare_build + +> crate::models::ServersCreateBuildResponse servers_builds_prepare_build(servers_create_build_request) + + +Creates a new game build for the given game. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**servers_create_build_request** | [**ServersCreateBuildRequest**](ServersCreateBuildRequest.md) | | [required] | + +### Return type + +[**crate::models::ServersCreateBuildResponse**](ServersCreateBuildResponse.md) + +### Authorization + +[BearerAuth](../README.md#BearerAuth) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/sdks/full/rust/docs/ServersCreateBuildRequest.md b/sdks/full/rust/docs/ServersCreateBuildRequest.md new file mode 100644 index 0000000000..1c96d10698 --- /dev/null +++ b/sdks/full/rust/docs/ServersCreateBuildRequest.md @@ -0,0 +1,17 @@ +# ServersCreateBuildRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**compression** | Option<[**crate::models::ServersBuildCompression**](ServersBuildCompression.md)> | | [optional] +**display_name** | **String** | Represent a resource's readable display name. | +**image_file** | [**crate::models::UploadPrepareFile**](UploadPrepareFile.md) | | +**image_tag** | **String** | A tag given to the game build. | +**kind** | Option<[**crate::models::ServersBuildKind**](ServersBuildKind.md)> | | [optional] +**multipart_upload** | Option<**bool**> | | [optional] +**tags** | Option<[**serde_json::Value**](.md)> | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersCreateBuildResponse.md b/sdks/full/rust/docs/ServersCreateBuildResponse.md new file mode 100644 index 0000000000..e5c3a19f8c --- /dev/null +++ b/sdks/full/rust/docs/ServersCreateBuildResponse.md @@ -0,0 +1,14 @@ +# ServersCreateBuildResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**build_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**image_presigned_request** | Option<[**crate::models::UploadPresignedRequest**](UploadPresignedRequest.md)> | | [optional] +**image_presigned_requests** | Option<[**Vec**](UploadPresignedRequest.md)> | | [optional] +**upload_id** | [**uuid::Uuid**](uuid::Uuid.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersCreateServerNetworkRequest.md b/sdks/full/rust/docs/ServersCreateServerNetworkRequest.md new file mode 100644 index 0000000000..633ac595fa --- /dev/null +++ b/sdks/full/rust/docs/ServersCreateServerNetworkRequest.md @@ -0,0 +1,12 @@ +# ServersCreateServerNetworkRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**mode** | Option<[**crate::models::ServersNetworkMode**](ServersNetworkMode.md)> | | [optional] +**ports** | [**::std::collections::HashMap**](ServersCreateServerPortRequest.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersCreateServerPortRequest.md b/sdks/full/rust/docs/ServersCreateServerPortRequest.md new file mode 100644 index 0000000000..97c550c60d --- /dev/null +++ b/sdks/full/rust/docs/ServersCreateServerPortRequest.md @@ -0,0 +1,13 @@ +# ServersCreateServerPortRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**internal_port** | Option<**i32**> | | [optional] +**protocol** | [**crate::models::ServersPortProtocol**](ServersPortProtocol.md) | | +**routing** | Option<[**crate::models::ServersPortRouting**](ServersPortRouting.md)> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersCreateServerRequest.md b/sdks/full/rust/docs/ServersCreateServerRequest.md new file mode 100644 index 0000000000..382da90515 --- /dev/null +++ b/sdks/full/rust/docs/ServersCreateServerRequest.md @@ -0,0 +1,19 @@ +# ServersCreateServerRequest + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**arguments** | Option<**Vec**> | | [optional] +**datacenter** | **String** | The name ID of the datacenter | +**environment** | Option<**::std::collections::HashMap**> | | [optional] +**image_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**kill_timeout** | Option<**i64**> | The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. | [optional] +**network** | [**crate::models::ServersCreateServerNetworkRequest**](ServersCreateServerNetworkRequest.md) | | +**resources** | [**crate::models::ServersResources**](ServersResources.md) | | +**tags** | Option<[**serde_json::Value**](.md)> | | +**webhook_url** | Option<**String**> | A url to send to which events from the server running will be sent | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersCreateServerResponse.md b/sdks/full/rust/docs/ServersCreateServerResponse.md new file mode 100644 index 0000000000..29bae37bf7 --- /dev/null +++ b/sdks/full/rust/docs/ServersCreateServerResponse.md @@ -0,0 +1,11 @@ +# ServersCreateServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**server** | [**crate::models::ServersServer**](ServersServer.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersDestroyServerResponse.md b/sdks/full/rust/docs/ServersDestroyServerResponse.md new file mode 100644 index 0000000000..0cf193ed7a --- /dev/null +++ b/sdks/full/rust/docs/ServersDestroyServerResponse.md @@ -0,0 +1,11 @@ +# ServersDestroyServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**server_id** | [**uuid::Uuid**](uuid::Uuid.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersGetServerResponse.md b/sdks/full/rust/docs/ServersGetServerResponse.md new file mode 100644 index 0000000000..f5e5e42f86 --- /dev/null +++ b/sdks/full/rust/docs/ServersGetServerResponse.md @@ -0,0 +1,11 @@ +# ServersGetServerResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**server** | [**crate::models::ServersServer**](ServersServer.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersListBuildsResponse.md b/sdks/full/rust/docs/ServersListBuildsResponse.md new file mode 100644 index 0000000000..2135664842 --- /dev/null +++ b/sdks/full/rust/docs/ServersListBuildsResponse.md @@ -0,0 +1,11 @@ +# ServersListBuildsResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**builds** | [**Vec**](CloudBuildSummary.md) | A list of builds for the game associated with the token. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersListServersResponse.md b/sdks/full/rust/docs/ServersListServersResponse.md new file mode 100644 index 0000000000..b1c8e2d66a --- /dev/null +++ b/sdks/full/rust/docs/ServersListServersResponse.md @@ -0,0 +1,11 @@ +# ServersListServersResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**servers** | [**Vec**](ServersServer.md) | A list of servers for the game associated with the token. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersNetwork.md b/sdks/full/rust/docs/ServersNetwork.md new file mode 100644 index 0000000000..e1b819556d --- /dev/null +++ b/sdks/full/rust/docs/ServersNetwork.md @@ -0,0 +1,12 @@ +# ServersNetwork + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**mode** | Option<[**crate::models::ServersNetworkMode**](ServersNetworkMode.md)> | | [optional] +**ports** | [**::std::collections::HashMap**](ServersPort.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersNetworkMode.md b/sdks/full/rust/docs/ServersNetworkMode.md new file mode 100644 index 0000000000..9fdd3e28c1 --- /dev/null +++ b/sdks/full/rust/docs/ServersNetworkMode.md @@ -0,0 +1,10 @@ +# ServersNetworkMode + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersPort.md b/sdks/full/rust/docs/ServersPort.md new file mode 100644 index 0000000000..5ade4ddd58 --- /dev/null +++ b/sdks/full/rust/docs/ServersPort.md @@ -0,0 +1,15 @@ +# ServersPort + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**internal_port** | Option<**i32**> | | [optional] +**protocol** | [**crate::models::ServersPortProtocol**](ServersPortProtocol.md) | | +**public_hostname** | Option<**String**> | | [optional] +**public_port** | Option<**i32**> | | [optional] +**routing** | [**crate::models::ServersPortRouting**](ServersPortRouting.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersPortProtocol.md b/sdks/full/rust/docs/ServersPortProtocol.md new file mode 100644 index 0000000000..595d11df8f --- /dev/null +++ b/sdks/full/rust/docs/ServersPortProtocol.md @@ -0,0 +1,10 @@ +# ServersPortProtocol + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersPortRouting.md b/sdks/full/rust/docs/ServersPortRouting.md new file mode 100644 index 0000000000..1ac474c230 --- /dev/null +++ b/sdks/full/rust/docs/ServersPortRouting.md @@ -0,0 +1,12 @@ +# ServersPortRouting + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**game_guard** | Option<[**serde_json::Value**](.md)> | | [optional] +**host** | Option<[**serde_json::Value**](.md)> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersResources.md b/sdks/full/rust/docs/ServersResources.md new file mode 100644 index 0000000000..3b3f9d6882 --- /dev/null +++ b/sdks/full/rust/docs/ServersResources.md @@ -0,0 +1,12 @@ +# ServersResources + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**cpu** | **i32** | The number of CPU cores in millicores, or 1/1000 of a core. For example, 1/8 of a core would be 125 millicores, and 1 core would be 1000 millicores. | +**memory** | **i32** | The amount of memory in megabytes | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/docs/ServersServer.md b/sdks/full/rust/docs/ServersServer.md new file mode 100644 index 0000000000..f8c79759cf --- /dev/null +++ b/sdks/full/rust/docs/ServersServer.md @@ -0,0 +1,23 @@ +# ServersServer + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**arguments** | Option<**Vec**> | | [optional] +**cluster_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**create_ts** | **i64** | | +**datacenter_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**destroy_ts** | Option<**i64**> | | [optional] +**environment** | Option<**::std::collections::HashMap**> | | [optional] +**game_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**image_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**kill_timeout** | Option<**i64**> | The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. | [optional] +**network** | [**crate::models::ServersNetwork**](ServersNetwork.md) | | +**resources** | [**crate::models::ServersResources**](ServersResources.md) | | +**server_id** | [**uuid::Uuid**](uuid::Uuid.md) | | +**tags** | Option<[**serde_json::Value**](.md)> | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/sdks/full/rust/src/apis/cloud_games_tokens_api.rs b/sdks/full/rust/src/apis/cloud_games_tokens_api.rs index 42318b5460..5541d05823 100644 --- a/sdks/full/rust/src/apis/cloud_games_tokens_api.rs +++ b/sdks/full/rust/src/apis/cloud_games_tokens_api.rs @@ -28,6 +28,19 @@ pub enum CloudGamesTokensCreateCloudTokenError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`cloud_games_tokens_create_service_token`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CloudGamesTokensCreateServiceTokenError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + /// Creates a new game cloud token. pub async fn cloud_games_tokens_create_cloud_token(configuration: &configuration::Configuration, game_id: &str) -> Result> { @@ -60,3 +73,34 @@ pub async fn cloud_games_tokens_create_cloud_token(configuration: &configuration } } +/// Creates a new game service token. +pub async fn cloud_games_tokens_create_service_token(configuration: &configuration::Configuration, game_id: &str) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/cloud/games/{game_id}/tokens/service", local_var_configuration.base_path, game_id=crate::apis::urlencode(game_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust/src/apis/mod.rs b/sdks/full/rust/src/apis/mod.rs index e6401fd8f2..d267ce82f4 100644 --- a/sdks/full/rust/src/apis/mod.rs +++ b/sdks/full/rust/src/apis/mod.rs @@ -129,5 +129,7 @@ pub mod matchmaker_regions_api; pub mod portal_games_api; pub mod provision_datacenters_api; pub mod provision_servers_api; +pub mod servers_api; +pub mod servers_builds_api; pub mod configuration; diff --git a/sdks/full/rust/src/apis/servers_api.rs b/sdks/full/rust/src/apis/servers_api.rs new file mode 100644 index 0000000000..aa98e6ee7e --- /dev/null +++ b/sdks/full/rust/src/apis/servers_api.rs @@ -0,0 +1,201 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +use reqwest; + +use crate::apis::ResponseContent; +use super::{Error, configuration}; + + +/// struct for typed errors of method [`servers_create`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersCreateError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_destroy`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersDestroyError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_get`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersGetError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_list`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersListError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + + +/// Create a new dynamic server. +pub async fn servers_create(configuration: &configuration::Configuration, servers_create_server_request: crate::models::ServersCreateServerRequest) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.json(&servers_create_server_request); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Destroy a dynamic server. +pub async fn servers_destroy(configuration: &configuration::Configuration, server_id: &str, override_kill_timeout: Option) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/{server_id}", local_var_configuration.base_path, server_id=crate::apis::urlencode(server_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::DELETE, local_var_uri_str.as_str()); + + if let Some(ref local_var_str) = override_kill_timeout { + local_var_req_builder = local_var_req_builder.query(&[("override_kill_timeout", &local_var_str.to_string())]); + } + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Gets a dynamic server. +pub async fn servers_get(configuration: &configuration::Configuration, server_id: &str) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/{server_id}", local_var_configuration.base_path, server_id=crate::apis::urlencode(server_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Lists all servers associated with the token used. Can be filtered by tags in the query string. +pub async fn servers_list(configuration: &configuration::Configuration, tags: Option<&str>) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/list", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_str) = tags { + local_var_req_builder = local_var_req_builder.query(&[("tags", &local_var_str.to_string())]); + } + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust/src/apis/servers_builds_api.rs b/sdks/full/rust/src/apis/servers_builds_api.rs new file mode 100644 index 0000000000..1b487540c6 --- /dev/null +++ b/sdks/full/rust/src/apis/servers_builds_api.rs @@ -0,0 +1,154 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +use reqwest; + +use crate::apis::ResponseContent; +use super::{Error, configuration}; + + +/// struct for typed errors of method [`servers_builds_complete_build`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersBuildsCompleteBuildError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_builds_list_builds`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersBuildsListBuildsError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`servers_builds_prepare_build`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ServersBuildsPrepareBuildError { + Status400(crate::models::ErrorBody), + Status403(crate::models::ErrorBody), + Status404(crate::models::ErrorBody), + Status408(crate::models::ErrorBody), + Status429(crate::models::ErrorBody), + Status500(crate::models::ErrorBody), + UnknownValue(serde_json::Value), +} + + +/// Marks an upload as complete. +pub async fn servers_builds_complete_build(configuration: &configuration::Configuration, upload_id: &str) -> Result<(), Error> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/uploads/{upload_id}/complete", local_var_configuration.base_path, upload_id=crate::apis::urlencode(upload_id)); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + Ok(()) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Lists all builds of the game associated with the token used. Can be filtered by tags in the query string. +pub async fn servers_builds_list_builds(configuration: &configuration::Configuration, tags: Option<&str>) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/builds", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_str) = tags { + local_var_req_builder = local_var_req_builder.query(&[("tags", &local_var_str.to_string())]); + } + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + +/// Creates a new game build for the given game. +pub async fn servers_builds_prepare_build(configuration: &configuration::Configuration, servers_create_build_request: crate::models::ServersCreateBuildRequest) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/servers/builds", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.json(&servers_create_build_request); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/sdks/full/rust/src/models/cloud_build_summary.rs b/sdks/full/rust/src/models/cloud_build_summary.rs index e11928b5f4..2191748cf3 100644 --- a/sdks/full/rust/src/models/cloud_build_summary.rs +++ b/sdks/full/rust/src/models/cloud_build_summary.rs @@ -28,19 +28,23 @@ pub struct CloudBuildSummary { /// Represent a resource's readable display name. #[serde(rename = "display_name")] pub display_name: String, + /// Tags of this build + #[serde(rename = "tags")] + pub tags: ::std::collections::HashMap, #[serde(rename = "upload_id")] pub upload_id: uuid::Uuid, } impl CloudBuildSummary { /// A build summary. - pub fn new(build_id: uuid::Uuid, complete: bool, content_length: i64, create_ts: String, display_name: String, upload_id: uuid::Uuid) -> CloudBuildSummary { + pub fn new(build_id: uuid::Uuid, complete: bool, content_length: i64, create_ts: String, display_name: String, tags: ::std::collections::HashMap, upload_id: uuid::Uuid) -> CloudBuildSummary { CloudBuildSummary { build_id, complete, content_length, create_ts, display_name, + tags, upload_id, } } diff --git a/sdks/full/rust/src/models/cloud_games_create_service_token_response.rs b/sdks/full/rust/src/models/cloud_games_create_service_token_response.rs new file mode 100644 index 0000000000..7221b71310 --- /dev/null +++ b/sdks/full/rust/src/models/cloud_games_create_service_token_response.rs @@ -0,0 +1,29 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct CloudGamesCreateServiceTokenResponse { + /// A JSON Web Token. + #[serde(rename = "token")] + pub token: String, +} + +impl CloudGamesCreateServiceTokenResponse { + pub fn new(token: String) -> CloudGamesCreateServiceTokenResponse { + CloudGamesCreateServiceTokenResponse { + token, + } + } +} + + diff --git a/sdks/full/rust/src/models/mod.rs b/sdks/full/rust/src/models/mod.rs index cca9c150d4..a04299340c 100644 --- a/sdks/full/rust/src/models/mod.rs +++ b/sdks/full/rust/src/models/mod.rs @@ -132,6 +132,8 @@ pub mod cloud_games_create_game_version_request; pub use self::cloud_games_create_game_version_request::CloudGamesCreateGameVersionRequest; pub mod cloud_games_create_game_version_response; pub use self::cloud_games_create_game_version_response::CloudGamesCreateGameVersionResponse; +pub mod cloud_games_create_service_token_response; +pub use self::cloud_games_create_service_token_response::CloudGamesCreateServiceTokenResponse; pub mod cloud_games_delete_matchmaker_lobby_response; pub use self::cloud_games_delete_matchmaker_lobby_response::CloudGamesDeleteMatchmakerLobbyResponse; pub mod cloud_games_export_lobby_logs_request; @@ -604,6 +606,44 @@ pub mod provision_datacenters_get_tls_response; pub use self::provision_datacenters_get_tls_response::ProvisionDatacentersGetTlsResponse; pub mod provision_servers_get_info_response; pub use self::provision_servers_get_info_response::ProvisionServersGetInfoResponse; +pub mod servers_build_compression; +pub use self::servers_build_compression::ServersBuildCompression; +pub mod servers_build_kind; +pub use self::servers_build_kind::ServersBuildKind; +pub mod servers_create_build_request; +pub use self::servers_create_build_request::ServersCreateBuildRequest; +pub mod servers_create_build_response; +pub use self::servers_create_build_response::ServersCreateBuildResponse; +pub mod servers_create_server_network_request; +pub use self::servers_create_server_network_request::ServersCreateServerNetworkRequest; +pub mod servers_create_server_port_request; +pub use self::servers_create_server_port_request::ServersCreateServerPortRequest; +pub mod servers_create_server_request; +pub use self::servers_create_server_request::ServersCreateServerRequest; +pub mod servers_create_server_response; +pub use self::servers_create_server_response::ServersCreateServerResponse; +pub mod servers_destroy_server_response; +pub use self::servers_destroy_server_response::ServersDestroyServerResponse; +pub mod servers_get_server_response; +pub use self::servers_get_server_response::ServersGetServerResponse; +pub mod servers_list_builds_response; +pub use self::servers_list_builds_response::ServersListBuildsResponse; +pub mod servers_list_servers_response; +pub use self::servers_list_servers_response::ServersListServersResponse; +pub mod servers_network; +pub use self::servers_network::ServersNetwork; +pub mod servers_network_mode; +pub use self::servers_network_mode::ServersNetworkMode; +pub mod servers_port; +pub use self::servers_port::ServersPort; +pub mod servers_port_protocol; +pub use self::servers_port_protocol::ServersPortProtocol; +pub mod servers_port_routing; +pub use self::servers_port_routing::ServersPortRouting; +pub mod servers_resources; +pub use self::servers_resources::ServersResources; +pub mod servers_server; +pub use self::servers_server::ServersServer; pub mod upload_prepare_file; pub use self::upload_prepare_file::UploadPrepareFile; pub mod upload_presigned_request; diff --git a/sdks/full/rust/src/models/servers_build_compression.rs b/sdks/full/rust/src/models/servers_build_compression.rs new file mode 100644 index 0000000000..fd549d6279 --- /dev/null +++ b/sdks/full/rust/src/models/servers_build_compression.rs @@ -0,0 +1,39 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersBuildCompression { + #[serde(rename = "none")] + None, + #[serde(rename = "lz4")] + Lz4, + +} + +impl ToString for ServersBuildCompression { + fn to_string(&self) -> String { + match self { + Self::None => String::from("none"), + Self::Lz4 => String::from("lz4"), + } + } +} + +impl Default for ServersBuildCompression { + fn default() -> ServersBuildCompression { + Self::None + } +} + + + + diff --git a/sdks/full/rust/src/models/servers_build_kind.rs b/sdks/full/rust/src/models/servers_build_kind.rs new file mode 100644 index 0000000000..853abbb0e7 --- /dev/null +++ b/sdks/full/rust/src/models/servers_build_kind.rs @@ -0,0 +1,39 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersBuildKind { + #[serde(rename = "docker_image")] + DockerImage, + #[serde(rename = "oci_bundle")] + OciBundle, + +} + +impl ToString for ServersBuildKind { + fn to_string(&self) -> String { + match self { + Self::DockerImage => String::from("docker_image"), + Self::OciBundle => String::from("oci_bundle"), + } + } +} + +impl Default for ServersBuildKind { + fn default() -> ServersBuildKind { + Self::DockerImage + } +} + + + + diff --git a/sdks/full/rust/src/models/servers_create_build_request.rs b/sdks/full/rust/src/models/servers_create_build_request.rs new file mode 100644 index 0000000000..395fe9aeb6 --- /dev/null +++ b/sdks/full/rust/src/models/servers_create_build_request.rs @@ -0,0 +1,48 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateBuildRequest { + #[serde(rename = "compression", skip_serializing_if = "Option::is_none")] + pub compression: Option, + /// Represent a resource's readable display name. + #[serde(rename = "display_name")] + pub display_name: String, + #[serde(rename = "image_file")] + pub image_file: Box, + /// A tag given to the game build. + #[serde(rename = "image_tag")] + pub image_tag: String, + #[serde(rename = "kind", skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(rename = "multipart_upload", skip_serializing_if = "Option::is_none")] + pub multipart_upload: Option, + #[serde(rename = "tags", deserialize_with = "Option::deserialize")] + pub tags: Option, +} + +impl ServersCreateBuildRequest { + pub fn new(display_name: String, image_file: crate::models::UploadPrepareFile, image_tag: String, tags: Option) -> ServersCreateBuildRequest { + ServersCreateBuildRequest { + compression: None, + display_name, + image_file: Box::new(image_file), + image_tag, + kind: None, + multipart_upload: None, + tags, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_create_build_response.rs b/sdks/full/rust/src/models/servers_create_build_response.rs new file mode 100644 index 0000000000..551e3030bf --- /dev/null +++ b/sdks/full/rust/src/models/servers_create_build_response.rs @@ -0,0 +1,37 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateBuildResponse { + #[serde(rename = "build_id")] + pub build_id: uuid::Uuid, + #[serde(rename = "image_presigned_request", skip_serializing_if = "Option::is_none")] + pub image_presigned_request: Option>, + #[serde(rename = "image_presigned_requests", skip_serializing_if = "Option::is_none")] + pub image_presigned_requests: Option>, + #[serde(rename = "upload_id")] + pub upload_id: uuid::Uuid, +} + +impl ServersCreateBuildResponse { + pub fn new(build_id: uuid::Uuid, upload_id: uuid::Uuid) -> ServersCreateBuildResponse { + ServersCreateBuildResponse { + build_id, + image_presigned_request: None, + image_presigned_requests: None, + upload_id, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_create_server_network_request.rs b/sdks/full/rust/src/models/servers_create_server_network_request.rs new file mode 100644 index 0000000000..d469cc181d --- /dev/null +++ b/sdks/full/rust/src/models/servers_create_server_network_request.rs @@ -0,0 +1,31 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerNetworkRequest { + #[serde(rename = "mode", skip_serializing_if = "Option::is_none")] + pub mode: Option, + #[serde(rename = "ports")] + pub ports: ::std::collections::HashMap, +} + +impl ServersCreateServerNetworkRequest { + pub fn new(ports: ::std::collections::HashMap) -> ServersCreateServerNetworkRequest { + ServersCreateServerNetworkRequest { + mode: None, + ports, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_create_server_port_request.rs b/sdks/full/rust/src/models/servers_create_server_port_request.rs new file mode 100644 index 0000000000..d818685f71 --- /dev/null +++ b/sdks/full/rust/src/models/servers_create_server_port_request.rs @@ -0,0 +1,34 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerPortRequest { + #[serde(rename = "internal_port", skip_serializing_if = "Option::is_none")] + pub internal_port: Option, + #[serde(rename = "protocol")] + pub protocol: crate::models::ServersPortProtocol, + #[serde(rename = "routing", skip_serializing_if = "Option::is_none")] + pub routing: Option>, +} + +impl ServersCreateServerPortRequest { + pub fn new(protocol: crate::models::ServersPortProtocol) -> ServersCreateServerPortRequest { + ServersCreateServerPortRequest { + internal_port: None, + protocol, + routing: None, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_create_server_request.rs b/sdks/full/rust/src/models/servers_create_server_request.rs new file mode 100644 index 0000000000..e50b4b9541 --- /dev/null +++ b/sdks/full/rust/src/models/servers_create_server_request.rs @@ -0,0 +1,55 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerRequest { + #[serde(rename = "arguments", skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + /// The name ID of the datacenter + #[serde(rename = "datacenter")] + pub datacenter: String, + #[serde(rename = "environment", skip_serializing_if = "Option::is_none")] + pub environment: Option<::std::collections::HashMap>, + #[serde(rename = "image_id")] + pub image_id: uuid::Uuid, + /// The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. + #[serde(rename = "kill_timeout", skip_serializing_if = "Option::is_none")] + pub kill_timeout: Option, + #[serde(rename = "network")] + pub network: Box, + #[serde(rename = "resources")] + pub resources: Box, + #[serde(rename = "tags", deserialize_with = "Option::deserialize")] + pub tags: Option, + /// A url to send to which events from the server running will be sent + #[serde(rename = "webhook_url", skip_serializing_if = "Option::is_none")] + pub webhook_url: Option, +} + +impl ServersCreateServerRequest { + pub fn new(datacenter: String, image_id: uuid::Uuid, network: crate::models::ServersCreateServerNetworkRequest, resources: crate::models::ServersResources, tags: Option) -> ServersCreateServerRequest { + ServersCreateServerRequest { + arguments: None, + datacenter, + environment: None, + image_id, + kill_timeout: None, + network: Box::new(network), + resources: Box::new(resources), + tags, + webhook_url: None, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_create_server_response.rs b/sdks/full/rust/src/models/servers_create_server_response.rs new file mode 100644 index 0000000000..3c32cbd896 --- /dev/null +++ b/sdks/full/rust/src/models/servers_create_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersCreateServerResponse { + #[serde(rename = "server")] + pub server: Box, +} + +impl ServersCreateServerResponse { + pub fn new(server: crate::models::ServersServer) -> ServersCreateServerResponse { + ServersCreateServerResponse { + server: Box::new(server), + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_destroy_server_response.rs b/sdks/full/rust/src/models/servers_destroy_server_response.rs new file mode 100644 index 0000000000..f993e53a94 --- /dev/null +++ b/sdks/full/rust/src/models/servers_destroy_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersDestroyServerResponse { + #[serde(rename = "server_id")] + pub server_id: uuid::Uuid, +} + +impl ServersDestroyServerResponse { + pub fn new(server_id: uuid::Uuid) -> ServersDestroyServerResponse { + ServersDestroyServerResponse { + server_id, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_get_server_response.rs b/sdks/full/rust/src/models/servers_get_server_response.rs new file mode 100644 index 0000000000..2f017da813 --- /dev/null +++ b/sdks/full/rust/src/models/servers_get_server_response.rs @@ -0,0 +1,28 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersGetServerResponse { + #[serde(rename = "server")] + pub server: Box, +} + +impl ServersGetServerResponse { + pub fn new(server: crate::models::ServersServer) -> ServersGetServerResponse { + ServersGetServerResponse { + server: Box::new(server), + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_list_builds_response.rs b/sdks/full/rust/src/models/servers_list_builds_response.rs new file mode 100644 index 0000000000..3bfdec824f --- /dev/null +++ b/sdks/full/rust/src/models/servers_list_builds_response.rs @@ -0,0 +1,29 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersListBuildsResponse { + /// A list of builds for the game associated with the token. + #[serde(rename = "builds")] + pub builds: Vec, +} + +impl ServersListBuildsResponse { + pub fn new(builds: Vec) -> ServersListBuildsResponse { + ServersListBuildsResponse { + builds, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_list_servers_response.rs b/sdks/full/rust/src/models/servers_list_servers_response.rs new file mode 100644 index 0000000000..202010aa26 --- /dev/null +++ b/sdks/full/rust/src/models/servers_list_servers_response.rs @@ -0,0 +1,29 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersListServersResponse { + /// A list of servers for the game associated with the token. + #[serde(rename = "servers")] + pub servers: Vec, +} + +impl ServersListServersResponse { + pub fn new(servers: Vec) -> ServersListServersResponse { + ServersListServersResponse { + servers, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_network.rs b/sdks/full/rust/src/models/servers_network.rs new file mode 100644 index 0000000000..97bade5673 --- /dev/null +++ b/sdks/full/rust/src/models/servers_network.rs @@ -0,0 +1,31 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersNetwork { + #[serde(rename = "mode", skip_serializing_if = "Option::is_none")] + pub mode: Option, + #[serde(rename = "ports")] + pub ports: ::std::collections::HashMap, +} + +impl ServersNetwork { + pub fn new(ports: ::std::collections::HashMap) -> ServersNetwork { + ServersNetwork { + mode: None, + ports, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_network_mode.rs b/sdks/full/rust/src/models/servers_network_mode.rs new file mode 100644 index 0000000000..bbb9bd9a5b --- /dev/null +++ b/sdks/full/rust/src/models/servers_network_mode.rs @@ -0,0 +1,39 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersNetworkMode { + #[serde(rename = "bridge")] + Bridge, + #[serde(rename = "host")] + Host, + +} + +impl ToString for ServersNetworkMode { + fn to_string(&self) -> String { + match self { + Self::Bridge => String::from("bridge"), + Self::Host => String::from("host"), + } + } +} + +impl Default for ServersNetworkMode { + fn default() -> ServersNetworkMode { + Self::Bridge + } +} + + + + diff --git a/sdks/full/rust/src/models/servers_port.rs b/sdks/full/rust/src/models/servers_port.rs new file mode 100644 index 0000000000..ab305ac1e4 --- /dev/null +++ b/sdks/full/rust/src/models/servers_port.rs @@ -0,0 +1,40 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersPort { + #[serde(rename = "internal_port", skip_serializing_if = "Option::is_none")] + pub internal_port: Option, + #[serde(rename = "protocol")] + pub protocol: crate::models::ServersPortProtocol, + #[serde(rename = "public_hostname", skip_serializing_if = "Option::is_none")] + pub public_hostname: Option, + #[serde(rename = "public_port", skip_serializing_if = "Option::is_none")] + pub public_port: Option, + #[serde(rename = "routing")] + pub routing: Box, +} + +impl ServersPort { + pub fn new(protocol: crate::models::ServersPortProtocol, routing: crate::models::ServersPortRouting) -> ServersPort { + ServersPort { + internal_port: None, + protocol, + public_hostname: None, + public_port: None, + routing: Box::new(routing), + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_port_protocol.rs b/sdks/full/rust/src/models/servers_port_protocol.rs new file mode 100644 index 0000000000..e2e8c326c9 --- /dev/null +++ b/sdks/full/rust/src/models/servers_port_protocol.rs @@ -0,0 +1,48 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum ServersPortProtocol { + #[serde(rename = "http")] + Http, + #[serde(rename = "https")] + Https, + #[serde(rename = "tcp")] + Tcp, + #[serde(rename = "tcp_tls")] + TcpTls, + #[serde(rename = "udp")] + Udp, + +} + +impl ToString for ServersPortProtocol { + fn to_string(&self) -> String { + match self { + Self::Http => String::from("http"), + Self::Https => String::from("https"), + Self::Tcp => String::from("tcp"), + Self::TcpTls => String::from("tcp_tls"), + Self::Udp => String::from("udp"), + } + } +} + +impl Default for ServersPortProtocol { + fn default() -> ServersPortProtocol { + Self::Http + } +} + + + + diff --git a/sdks/full/rust/src/models/servers_port_routing.rs b/sdks/full/rust/src/models/servers_port_routing.rs new file mode 100644 index 0000000000..83f0846fc4 --- /dev/null +++ b/sdks/full/rust/src/models/servers_port_routing.rs @@ -0,0 +1,31 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersPortRouting { + #[serde(rename = "game_guard", skip_serializing_if = "Option::is_none")] + pub game_guard: Option, + #[serde(rename = "host", skip_serializing_if = "Option::is_none")] + pub host: Option, +} + +impl ServersPortRouting { + pub fn new() -> ServersPortRouting { + ServersPortRouting { + game_guard: None, + host: None, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_resources.rs b/sdks/full/rust/src/models/servers_resources.rs new file mode 100644 index 0000000000..d29a3cb0ac --- /dev/null +++ b/sdks/full/rust/src/models/servers_resources.rs @@ -0,0 +1,33 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersResources { + /// The number of CPU cores in millicores, or 1/1000 of a core. For example, 1/8 of a core would be 125 millicores, and 1 core would be 1000 millicores. + #[serde(rename = "cpu")] + pub cpu: i32, + /// The amount of memory in megabytes + #[serde(rename = "memory")] + pub memory: i32, +} + +impl ServersResources { + pub fn new(cpu: i32, memory: i32) -> ServersResources { + ServersResources { + cpu, + memory, + } + } +} + + diff --git a/sdks/full/rust/src/models/servers_server.rs b/sdks/full/rust/src/models/servers_server.rs new file mode 100644 index 0000000000..cb93decaee --- /dev/null +++ b/sdks/full/rust/src/models/servers_server.rs @@ -0,0 +1,65 @@ +/* + * Rivet API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * Generated by: https://openapi-generator.tech + */ + + + + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ServersServer { + #[serde(rename = "arguments", skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + #[serde(rename = "cluster_id")] + pub cluster_id: uuid::Uuid, + #[serde(rename = "create_ts")] + pub create_ts: i64, + #[serde(rename = "datacenter_id")] + pub datacenter_id: uuid::Uuid, + #[serde(rename = "destroy_ts", skip_serializing_if = "Option::is_none")] + pub destroy_ts: Option, + #[serde(rename = "environment", skip_serializing_if = "Option::is_none")] + pub environment: Option<::std::collections::HashMap>, + #[serde(rename = "game_id")] + pub game_id: uuid::Uuid, + #[serde(rename = "image_id")] + pub image_id: uuid::Uuid, + /// The duration to wait for in milliseconds before killing the server. This should be set to a safe default, and can be overridden during a DELETE request if needed. + #[serde(rename = "kill_timeout", skip_serializing_if = "Option::is_none")] + pub kill_timeout: Option, + #[serde(rename = "network")] + pub network: Box, + #[serde(rename = "resources")] + pub resources: Box, + #[serde(rename = "server_id")] + pub server_id: uuid::Uuid, + #[serde(rename = "tags", deserialize_with = "Option::deserialize")] + pub tags: Option, +} + +impl ServersServer { + pub fn new(cluster_id: uuid::Uuid, create_ts: i64, datacenter_id: uuid::Uuid, game_id: uuid::Uuid, image_id: uuid::Uuid, network: crate::models::ServersNetwork, resources: crate::models::ServersResources, server_id: uuid::Uuid, tags: Option) -> ServersServer { + ServersServer { + arguments: None, + cluster_id, + create_ts, + datacenter_id, + destroy_ts: None, + environment: None, + game_id, + image_id, + kill_timeout: None, + network: Box::new(network), + resources: Box::new(resources), + server_id, + tags, + } + } +} + + diff --git a/sdks/full/typescript/archive.tgz b/sdks/full/typescript/archive.tgz index 9206d47b95..2728b0d6d8 100644 --- a/sdks/full/typescript/archive.tgz +++ b/sdks/full/typescript/archive.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dd57b0829c24ebac5513e6bec6c92519111cb03619a811f4150d151fb440078 -size 641144 +oid sha256:2c6860019855924ed0c7a1429ed2889e999be87f544afc996b0024c5be787bd7 +size 665221 diff --git a/sdks/full/typescript/src/Client.ts b/sdks/full/typescript/src/Client.ts index 985e3e0e4c..1016c6e32a 100644 --- a/sdks/full/typescript/src/Client.ts +++ b/sdks/full/typescript/src/Client.ts @@ -10,6 +10,7 @@ import { Group } from "./api/resources/group/client/Client"; import { Identity } from "./api/resources/identity/client/Client"; import { Kv } from "./api/resources/kv/client/Client"; import { Provision } from "./api/resources/provision/client/Client"; +import { Servers } from "./api/resources/servers/client/Client"; import { Auth } from "./api/resources/auth/client/Client"; import { Job } from "./api/resources/job/client/Client"; import { Matchmaker } from "./api/resources/matchmaker/client/Client"; @@ -67,6 +68,12 @@ export class RivetClient { return (this._provision ??= new Provision(this._options)); } + protected _servers: Servers | undefined; + + public get servers(): Servers { + return (this._servers ??= new Servers(this._options)); + } + protected _auth: Auth | undefined; public get auth(): Auth { diff --git a/sdks/full/typescript/src/api/resources/cloud/resources/common/types/BuildSummary.ts b/sdks/full/typescript/src/api/resources/cloud/resources/common/types/BuildSummary.ts index 1e23e8558b..e303ece651 100644 --- a/sdks/full/typescript/src/api/resources/cloud/resources/common/types/BuildSummary.ts +++ b/sdks/full/typescript/src/api/resources/cloud/resources/common/types/BuildSummary.ts @@ -16,4 +16,6 @@ export interface BuildSummary { contentLength: number; /** Whether or not this build has completely been uploaded. */ complete: boolean; + /** Tags of this build */ + tags: Record; } diff --git a/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/client/Client.ts b/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/client/Client.ts index 45b9cbd8ed..917470d631 100644 --- a/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/client/Client.ts +++ b/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/client/Client.ts @@ -146,6 +146,127 @@ export class Tokens { } } + /** + * Creates a new game service token. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async createServiceToken( + gameId: string, + requestOptions?: Tokens.RequestOptions + ): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + `/cloud/games/${gameId}/tokens/service` + ), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.cloud.games.CreateCloudTokenResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + protected async _getAuthorizationHeader() { const bearer = await core.Supplier.get(this._options.token); if (bearer != null) { diff --git a/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/types/index.ts b/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/types/index.ts index c6eb67fbbe..3f81f48699 100644 --- a/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/types/index.ts +++ b/sdks/full/typescript/src/api/resources/cloud/resources/games/resources/tokens/types/index.ts @@ -1 +1,2 @@ export * from "./CreateCloudTokenResponse"; +export * from "./CreateServiceTokenResponse"; diff --git a/sdks/full/typescript/src/api/resources/index.ts b/sdks/full/typescript/src/api/resources/index.ts index 13718a2302..3989427651 100644 --- a/sdks/full/typescript/src/api/resources/index.ts +++ b/sdks/full/typescript/src/api/resources/index.ts @@ -4,6 +4,7 @@ export * as group from "./group"; export * as identity from "./identity"; export * as kv from "./kv"; export * as provision from "./provision"; +export * as servers from "./servers"; export * as auth from "./auth"; export * as captcha from "./captcha"; export * as common from "./common"; diff --git a/sdks/full/typescript/src/api/resources/servers/client/Client.ts b/sdks/full/typescript/src/api/resources/servers/client/Client.ts new file mode 100644 index 0000000000..c807f78a16 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/client/Client.ts @@ -0,0 +1,545 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments"; +import * as core from "../../../../core"; +import * as Rivet from "../../.."; +import urlJoin from "url-join"; +import * as serializers from "../../../../serialization"; +import * as errors from "../../../../errors"; +import { Builds } from "../resources/builds/client/Client"; + +export declare namespace Servers { + interface Options { + environment?: core.Supplier; + token?: core.Supplier; + fetcher?: core.FetchFunction; + } + + interface RequestOptions { + timeoutInSeconds?: number; + maxRetries?: number; + } +} + +export class Servers { + constructor(protected readonly _options: Servers.Options = {}) {} + + /** + * Gets a dynamic server. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async get( + serverId: string, + requestOptions?: Servers.RequestOptions + ): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + `/servers/${serverId}` + ), + method: "GET", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.GetServerResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * Create a new dynamic server. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async create( + request: Rivet.servers.CreateServerRequest, + requestOptions?: Servers.RequestOptions + ): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + "/servers" + ), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + body: await serializers.servers.CreateServerRequest.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.CreateServerResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * Destroy a dynamic server. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async destroy( + serverId: string, + request: Rivet.servers.DestroyServerRequest = {}, + requestOptions?: Servers.RequestOptions + ): Promise { + const { overrideKillTimeout } = request; + const _queryParams: Record = {}; + if (overrideKillTimeout != null) { + _queryParams["override_kill_timeout"] = overrideKillTimeout.toString(); + } + + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + `/servers/${serverId}` + ), + method: "DELETE", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + queryParameters: _queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.DestroyServerResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * Lists all servers associated with the token used. Can be filtered by tags in the query string. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async list( + request: Rivet.servers.GetServersRequest = {}, + requestOptions?: Servers.RequestOptions + ): Promise { + const { tags } = request; + const _queryParams: Record = {}; + if (tags != null) { + _queryParams["tags"] = tags; + } + + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + "/servers/list" + ), + method: "GET", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + queryParameters: _queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.ListServersResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + protected _builds: Builds | undefined; + + public get builds(): Builds { + return (this._builds ??= new Builds(this._options)); + } + + protected async _getAuthorizationHeader() { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/sdks/full/typescript/src/api/resources/servers/client/index.ts b/sdks/full/typescript/src/api/resources/servers/client/index.ts new file mode 100644 index 0000000000..415726b7fe --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/sdks/full/typescript/src/api/resources/servers/client/requests/DestroyServerRequest.ts b/sdks/full/typescript/src/api/resources/servers/client/requests/DestroyServerRequest.ts new file mode 100644 index 0000000000..bb50967977 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/client/requests/DestroyServerRequest.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface DestroyServerRequest { + /** + * The duration to wait for in milliseconds before killing the server. This should be used to override the default kill timeout if a faster time is needed, say for ignoring a graceful shutdown. + */ + overrideKillTimeout?: number; +} diff --git a/sdks/full/typescript/src/api/resources/servers/client/requests/GetServersRequest.ts b/sdks/full/typescript/src/api/resources/servers/client/requests/GetServersRequest.ts new file mode 100644 index 0000000000..96fe8dfc0d --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/client/requests/GetServersRequest.ts @@ -0,0 +1,7 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface GetServersRequest { + tags?: string; +} diff --git a/sdks/full/typescript/src/api/resources/servers/client/requests/index.ts b/sdks/full/typescript/src/api/resources/servers/client/requests/index.ts new file mode 100644 index 0000000000..e379aeba70 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/client/requests/index.ts @@ -0,0 +1,2 @@ +export { DestroyServerRequest } from "./DestroyServerRequest"; +export { GetServersRequest } from "./GetServersRequest"; diff --git a/sdks/full/typescript/src/api/resources/servers/index.ts b/sdks/full/typescript/src/api/resources/servers/index.ts new file mode 100644 index 0000000000..a931b36375 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/index.ts @@ -0,0 +1,3 @@ +export * from "./types"; +export * from "./resources"; +export * from "./client"; diff --git a/sdks/full/typescript/src/api/resources/servers/resources/builds/client/Client.ts b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/Client.ts new file mode 100644 index 0000000000..87cc0b1192 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/Client.ts @@ -0,0 +1,400 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments"; +import * as core from "../../../../../../core"; +import * as Rivet from "../../../../.."; +import urlJoin from "url-join"; +import * as serializers from "../../../../../../serialization"; +import * as errors from "../../../../../../errors"; + +export declare namespace Builds { + interface Options { + environment?: core.Supplier; + token?: core.Supplier; + fetcher?: core.FetchFunction; + } + + interface RequestOptions { + timeoutInSeconds?: number; + maxRetries?: number; + } +} + +export class Builds { + constructor(protected readonly _options: Builds.Options = {}) {} + + /** + * Lists all builds of the game associated with the token used. Can be filtered by tags in the query string. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async listBuilds( + request: Rivet.servers.GetBuildsRequest = {}, + requestOptions?: Builds.RequestOptions + ): Promise { + const { tags } = request; + const _queryParams: Record = {}; + if (tags != null) { + _queryParams["tags"] = tags; + } + + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + "/servers/builds" + ), + method: "GET", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + queryParameters: _queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.ListBuildsResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * Creates a new game build for the given game. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async prepareBuild( + request: Rivet.servers.CreateBuildRequest, + requestOptions?: Builds.RequestOptions + ): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + "/servers/builds" + ), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + body: await serializers.servers.CreateBuildRequest.jsonOrThrow(request, { + unrecognizedObjectKeys: "strip", + }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return await serializers.servers.CreateBuildResponse.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }); + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * Marks an upload as complete. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + public async completeBuild(uploadId: string, requestOptions?: Builds.RequestOptions): Promise { + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: urlJoin( + (await core.Supplier.get(this._options.environment)) ?? environments.RivetEnvironment.Production, + `/servers/uploads/${uploadId}/complete` + ), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + }, + contentType: "application/json", + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 180000, + maxRetries: requestOptions?.maxRetries, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 500: + throw new Rivet.InternalError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 429: + throw new Rivet.RateLimitError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 403: + throw new Rivet.ForbiddenError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 408: + throw new Rivet.UnauthorizedError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 404: + throw new Rivet.NotFoundError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + case 400: + throw new Rivet.BadRequestError( + await serializers.ErrorBody.parseOrThrow(_response.error.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }) + ); + default: + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.RivetError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.RivetTimeoutError(); + case "unknown": + throw new errors.RivetError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader() { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/sdks/full/typescript/src/api/resources/servers/resources/builds/client/index.ts b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/index.ts new file mode 100644 index 0000000000..415726b7fe --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/index.ts @@ -0,0 +1 @@ +export * from "./requests"; diff --git a/sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/GetBuildsRequest.ts b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/GetBuildsRequest.ts new file mode 100644 index 0000000000..5c26738a07 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/GetBuildsRequest.ts @@ -0,0 +1,7 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface GetBuildsRequest { + tags?: string; +} diff --git a/sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/index.ts b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/index.ts new file mode 100644 index 0000000000..08db7ff8cb --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/builds/client/requests/index.ts @@ -0,0 +1 @@ +export { GetBuildsRequest } from "./GetBuildsRequest"; diff --git a/sdks/full/typescript/src/api/resources/servers/resources/builds/index.ts b/sdks/full/typescript/src/api/resources/servers/resources/builds/index.ts new file mode 100644 index 0000000000..c9240f83b4 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/builds/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./client"; diff --git a/sdks/full/typescript/src/api/resources/servers/resources/common/index.ts b/sdks/full/typescript/src/api/resources/servers/resources/common/index.ts new file mode 100644 index 0000000000..eea524d655 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/common/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/sdks/full/typescript/src/api/resources/servers/resources/index.ts b/sdks/full/typescript/src/api/resources/servers/resources/index.ts new file mode 100644 index 0000000000..0e22c79059 --- /dev/null +++ b/sdks/full/typescript/src/api/resources/servers/resources/index.ts @@ -0,0 +1,5 @@ +export * as builds from "./builds"; +export * from "./builds/types"; +export * as common from "./common"; +export * from "./common/types"; +export * from "./builds/client/requests"; diff --git a/sdks/full/typescript/src/core/fetcher/Fetcher.ts b/sdks/full/typescript/src/core/fetcher/Fetcher.ts index cb5ee9c1de..19de5d475e 100644 --- a/sdks/full/typescript/src/core/fetcher/Fetcher.ts +++ b/sdks/full/typescript/src/core/fetcher/Fetcher.ts @@ -73,13 +73,15 @@ async function fetcherImpl(args: Fetcher.Args): Promise => { const controller = new AbortController(); let abortId = undefined; if (args.timeoutMs != null) { abortId = setTimeout(() => controller.abort(), args.timeoutMs); } - const response = await fetch(url, { + const response = await fetchFn(url, { method: args.method, headers, body, diff --git a/sdks/full/typescript/src/serialization/resources/cloud/resources/common/types/BuildSummary.ts b/sdks/full/typescript/src/serialization/resources/cloud/resources/common/types/BuildSummary.ts index 9c022b5bb4..72baca0b6e 100644 --- a/sdks/full/typescript/src/serialization/resources/cloud/resources/common/types/BuildSummary.ts +++ b/sdks/full/typescript/src/serialization/resources/cloud/resources/common/types/BuildSummary.ts @@ -22,6 +22,7 @@ export const BuildSummary: core.serialization.ObjectSchema< ), contentLength: core.serialization.property("content_length", core.serialization.number()), complete: core.serialization.boolean(), + tags: core.serialization.record(core.serialization.string(), core.serialization.string()), }); export declare namespace BuildSummary { @@ -32,5 +33,6 @@ export declare namespace BuildSummary { create_ts: serializers.Timestamp.Raw; content_length: number; complete: boolean; + tags: Record; } } diff --git a/sdks/full/typescript/src/serialization/resources/cloud/resources/games/resources/tokens/types/index.ts b/sdks/full/typescript/src/serialization/resources/cloud/resources/games/resources/tokens/types/index.ts index c6eb67fbbe..3f81f48699 100644 --- a/sdks/full/typescript/src/serialization/resources/cloud/resources/games/resources/tokens/types/index.ts +++ b/sdks/full/typescript/src/serialization/resources/cloud/resources/games/resources/tokens/types/index.ts @@ -1 +1,2 @@ export * from "./CreateCloudTokenResponse"; +export * from "./CreateServiceTokenResponse"; diff --git a/sdks/full/typescript/src/serialization/resources/index.ts b/sdks/full/typescript/src/serialization/resources/index.ts index 5cd7449f02..f134c5d8f3 100644 --- a/sdks/full/typescript/src/serialization/resources/index.ts +++ b/sdks/full/typescript/src/serialization/resources/index.ts @@ -4,6 +4,7 @@ export * as group from "./group"; export * as identity from "./identity"; export * as kv from "./kv"; export * as provision from "./provision"; +export * as servers from "./servers"; export * as auth from "./auth"; export * as captcha from "./captcha"; export * as common from "./common"; diff --git a/sdks/full/typescript/src/serialization/resources/servers/index.ts b/sdks/full/typescript/src/serialization/resources/servers/index.ts new file mode 100644 index 0000000000..3ce0a3e38e --- /dev/null +++ b/sdks/full/typescript/src/serialization/resources/servers/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./resources"; diff --git a/sdks/full/typescript/src/serialization/resources/servers/resources/builds/index.ts b/sdks/full/typescript/src/serialization/resources/servers/resources/builds/index.ts new file mode 100644 index 0000000000..eea524d655 --- /dev/null +++ b/sdks/full/typescript/src/serialization/resources/servers/resources/builds/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/sdks/full/typescript/src/serialization/resources/servers/resources/common/index.ts b/sdks/full/typescript/src/serialization/resources/servers/resources/common/index.ts new file mode 100644 index 0000000000..eea524d655 --- /dev/null +++ b/sdks/full/typescript/src/serialization/resources/servers/resources/common/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/sdks/full/typescript/src/serialization/resources/servers/resources/index.ts b/sdks/full/typescript/src/serialization/resources/servers/resources/index.ts new file mode 100644 index 0000000000..5b2d30fd99 --- /dev/null +++ b/sdks/full/typescript/src/serialization/resources/servers/resources/index.ts @@ -0,0 +1,4 @@ +export * as builds from "./builds"; +export * from "./builds/types"; +export * as common from "./common"; +export * from "./common/types"; diff --git a/sdks/full/typescript/types/Client.d.ts b/sdks/full/typescript/types/Client.d.ts index 5de214eeee..86cfc68ef9 100644 --- a/sdks/full/typescript/types/Client.d.ts +++ b/sdks/full/typescript/types/Client.d.ts @@ -9,6 +9,7 @@ import { Group } from "./api/resources/group/client/Client"; import { Identity } from "./api/resources/identity/client/Client"; import { Kv } from "./api/resources/kv/client/Client"; import { Provision } from "./api/resources/provision/client/Client"; +import { Servers } from "./api/resources/servers/client/Client"; import { Auth } from "./api/resources/auth/client/Client"; import { Job } from "./api/resources/job/client/Client"; import { Matchmaker } from "./api/resources/matchmaker/client/Client"; @@ -39,6 +40,8 @@ export declare class RivetClient { get kv(): Kv; protected _provision: Provision | undefined; get provision(): Provision; + protected _servers: Servers | undefined; + get servers(): Servers; protected _auth: Auth | undefined; get auth(): Auth; protected _job: Job | undefined; diff --git a/sdks/full/typescript/types/api/resources/cloud/resources/common/types/BuildSummary.d.ts b/sdks/full/typescript/types/api/resources/cloud/resources/common/types/BuildSummary.d.ts index 63c587d41a..39441cde62 100644 --- a/sdks/full/typescript/types/api/resources/cloud/resources/common/types/BuildSummary.d.ts +++ b/sdks/full/typescript/types/api/resources/cloud/resources/common/types/BuildSummary.d.ts @@ -14,4 +14,6 @@ export interface BuildSummary { contentLength: number; /** Whether or not this build has completely been uploaded. */ complete: boolean; + /** Tags of this build */ + tags: Record; } diff --git a/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/client/Client.d.ts b/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/client/Client.d.ts index 9a28e6ccf5..b707b6f12d 100644 --- a/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/client/Client.d.ts +++ b/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/client/Client.d.ts @@ -28,5 +28,15 @@ export declare class Tokens { * @throws {@link Rivet.BadRequestError} */ createCloudToken(gameId: string, requestOptions?: Tokens.RequestOptions): Promise; + /** + * Creates a new game service token. + * @throws {@link Rivet.InternalError} + * @throws {@link Rivet.RateLimitError} + * @throws {@link Rivet.ForbiddenError} + * @throws {@link Rivet.UnauthorizedError} + * @throws {@link Rivet.NotFoundError} + * @throws {@link Rivet.BadRequestError} + */ + createServiceToken(gameId: string, requestOptions?: Tokens.RequestOptions): Promise; protected _getAuthorizationHeader(): Promise; } diff --git a/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/CreateServiceTokenResponse.d.ts b/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/CreateServiceTokenResponse.d.ts index 113b8f0ff0..c60fec3ccc 100644 --- a/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/CreateServiceTokenResponse.d.ts +++ b/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/CreateServiceTokenResponse.d.ts @@ -2,10 +2,6 @@ * This file was auto-generated by Fern from our API Definition. */ export interface CreateServiceTokenResponse { - /** - * A JSON Web Token. - * Slightly modified to include a description prefix and use Protobufs of - * JSON. - */ + /** A JSON Web Token. */ token: string; } diff --git a/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/index.d.ts b/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/index.d.ts index c6eb67fbbe..3f81f48699 100644 --- a/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/index.d.ts +++ b/sdks/full/typescript/types/api/resources/cloud/resources/games/resources/tokens/types/index.d.ts @@ -1 +1,2 @@ export * from "./CreateCloudTokenResponse"; +export * from "./CreateServiceTokenResponse"; diff --git a/sdks/full/typescript/types/api/resources/index.d.ts b/sdks/full/typescript/types/api/resources/index.d.ts index 13718a2302..3989427651 100644 --- a/sdks/full/typescript/types/api/resources/index.d.ts +++ b/sdks/full/typescript/types/api/resources/index.d.ts @@ -4,6 +4,7 @@ export * as group from "./group"; export * as identity from "./identity"; export * as kv from "./kv"; export * as provision from "./provision"; +export * as servers from "./servers"; export * as auth from "./auth"; export * as captcha from "./captcha"; export * as common from "./common"; diff --git a/sdks/full/typescript/types/serialization/resources/cloud/resources/common/types/BuildSummary.d.ts b/sdks/full/typescript/types/serialization/resources/cloud/resources/common/types/BuildSummary.d.ts index f437b8f662..743b105c54 100644 --- a/sdks/full/typescript/types/serialization/resources/cloud/resources/common/types/BuildSummary.d.ts +++ b/sdks/full/typescript/types/serialization/resources/cloud/resources/common/types/BuildSummary.d.ts @@ -13,5 +13,6 @@ export declare namespace BuildSummary { create_ts: serializers.Timestamp.Raw; content_length: number; complete: boolean; + tags: Record; } } diff --git a/sdks/full/typescript/types/serialization/resources/cloud/resources/games/resources/tokens/types/index.d.ts b/sdks/full/typescript/types/serialization/resources/cloud/resources/games/resources/tokens/types/index.d.ts index c6eb67fbbe..3f81f48699 100644 --- a/sdks/full/typescript/types/serialization/resources/cloud/resources/games/resources/tokens/types/index.d.ts +++ b/sdks/full/typescript/types/serialization/resources/cloud/resources/games/resources/tokens/types/index.d.ts @@ -1 +1,2 @@ export * from "./CreateCloudTokenResponse"; +export * from "./CreateServiceTokenResponse"; diff --git a/sdks/full/typescript/types/serialization/resources/index.d.ts b/sdks/full/typescript/types/serialization/resources/index.d.ts index 5cd7449f02..f134c5d8f3 100644 --- a/sdks/full/typescript/types/serialization/resources/index.d.ts +++ b/sdks/full/typescript/types/serialization/resources/index.d.ts @@ -4,6 +4,7 @@ export * as group from "./group"; export * as identity from "./identity"; export * as kv from "./kv"; export * as provision from "./provision"; +export * as servers from "./servers"; export * as auth from "./auth"; export * as captcha from "./captcha"; export * as common from "./common"; diff --git a/sdks/runtime/typescript/archive.tgz b/sdks/runtime/typescript/archive.tgz index 0b08de48b1..b950751fed 100644 --- a/sdks/runtime/typescript/archive.tgz +++ b/sdks/runtime/typescript/archive.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e87919af89dc342446b6689ebfa0d97010ccf1b65a8f3694cd86cc74876b9ce7 -size 371685 +oid sha256:a1713c5179c9a695ecd99b56196047fb4ff6ecbcef5fd04693d06c7073f6e02f +size 371695 diff --git a/sdks/runtime/typescript/src/core/fetcher/Fetcher.ts b/sdks/runtime/typescript/src/core/fetcher/Fetcher.ts index a43618c45b..19de5d475e 100644 --- a/sdks/runtime/typescript/src/core/fetcher/Fetcher.ts +++ b/sdks/runtime/typescript/src/core/fetcher/Fetcher.ts @@ -73,15 +73,15 @@ async function fetcherImpl(args: Fetcher.Args): Promise => { const controller = new AbortController(); let abortId = undefined; if (args.timeoutMs != null) { abortId = setTimeout(() => controller.abort(), args.timeoutMs); } - - // We assume here that the environment is providing fetch functionality - const response = await fetch(url, { + const response = await fetchFn(url, { method: args.method, headers, body, diff --git a/svc/Cargo.lock b/svc/Cargo.lock index be6652c72e..cab195387d 100644 --- a/svc/Cargo.lock +++ b/svc/Cargo.lock @@ -20,9 +20,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-tzdata" @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "api-admin" @@ -112,10 +112,10 @@ dependencies = [ "cluster-server-get", "cluster-server-list", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-cache", "rivet-claims", @@ -155,7 +155,7 @@ dependencies = [ "faker-user", "headers", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "rivet-api", @@ -199,7 +199,7 @@ dependencies = [ "chrono", "faker-game", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "rivet-cache", @@ -251,6 +251,7 @@ dependencies = [ "cloud-namespace-get", "cloud-namespace-token-development-create", "cloud-namespace-token-public-create", + "cloud-service-game-token-create", "cloud-version-get", "cloud-version-publish", "cluster-datacenter-list", @@ -277,7 +278,7 @@ dependencies = [ "game-version-list", "game-version-validate", "http 0.2.12", - "hyper", + "hyper 0.14.28", "job-log-read", "job-run-get", "job-run-metrics-log", @@ -298,7 +299,7 @@ dependencies = [ "rand", "region-get", "region-list-for-game", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-cache", "rivet-claims", @@ -350,7 +351,7 @@ dependencies = [ "game-get", "game-resolve-namespace-id", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "rand", @@ -408,7 +409,7 @@ dependencies = [ "global-error", "headers", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "regex", @@ -472,7 +473,7 @@ dependencies = [ "game-user-recent-session-list", "game-user-recommend", "http 0.2.12", - "hyper", + "hyper 0.14.28", "identity-config-version-get", "job-run-get", "lazy_static", @@ -531,7 +532,7 @@ dependencies = [ "async-trait", "chirp-client", "http 0.2.12", - "hyper", + "hyper 0.14.28", "rivet-operation", "tokio", "tracing", @@ -549,13 +550,13 @@ dependencies = [ "chrono", "faker-job-run", "http 0.2.12", - "hyper", + "hyper 0.14.28", "job-run-get", "lazy_static", "nomad-client", "nomad-util", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-cache", "rivet-claims", "rivet-connection", @@ -594,7 +595,7 @@ dependencies = [ "game-namespace-get", "game-user-get", "http 0.2.12", - "hyper", + "hyper 0.14.28", "kv-config-version-get", "kv-get", "kv-list", @@ -604,7 +605,7 @@ dependencies = [ "rand", "regex", "region-get", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-cache", "rivet-claims", @@ -660,7 +661,7 @@ dependencies = [ "game-resolve-namespace-id", "game-user-get", "http 0.2.12", - "hyper", + "hyper 0.14.28", "job-run-get", "lazy_static", "mm-config-lobby-group-resolve-version", @@ -674,7 +675,7 @@ dependencies = [ "region-get", "region-recommend", "region-resolve-for-game", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-cache", "rivet-claims", @@ -714,11 +715,12 @@ dependencies = [ "api-kv", "api-matchmaker", "api-portal", + "api-servers", "api-status", "async-trait", "chirp-client", "http 0.2.12", - "hyper", + "hyper 0.14.28", "rivet-operation", "tokio", "tracing", @@ -739,7 +741,7 @@ dependencies = [ "game-get", "game-resolve-name-id", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "rand", @@ -782,7 +784,7 @@ dependencies = [ "cluster-server-get", "cluster-server-resolve-for-ip", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "rivet-api", @@ -802,6 +804,68 @@ dependencies = [ "uuid", ] +[[package]] +name = "api-servers" +version = "0.0.1" +dependencies = [ + "api-helper", + "build-create", + "build-get", + "build-list-for-game", + "cdn-namespace-domain-create", + "chirp-client", + "chrono", + "cloud-namespace-token-development-create", + "cloud-namespace-token-public-create", + "cloud-service-game-token-create", + "cluster-datacenter-get", + "cluster-datacenter-list", + "cluster-datacenter-resolve-for-name-id", + "cluster-get", + "cluster-get-for-game", + "cluster-list", + "cluster-resolve-for-name-id", + "ds-server-create", + "ds-server-delete", + "ds-server-get", + "ds-server-list-for-game", + "faker-build", + "faker-game", + "faker-game-namespace", + "faker-game-version", + "faker-region", + "game-get", + "game-namespace-get", + "game-version-get", + "http 0.2.12", + "hyper 0.14.28", + "region-get", + "reqwest 0.11.27", + "rivet-api", + "rivet-cache", + "rivet-claims", + "rivet-connection", + "rivet-convert", + "rivet-health-checks", + "rivet-operation", + "rivet-pools", + "s3-util", + "serde", + "serde_json", + "token-revoke", + "tokio", + "tracing", + "tracing-futures", + "tracing-subscriber", + "upload-complete", + "upload-get", + "url", + "user-get", + "user-identity-get", + "user-team-list", + "uuid", +] + [[package]] name = "api-status" version = "0.0.1" @@ -816,11 +880,11 @@ dependencies = [ "game-namespace-resolve-name-id", "game-resolve-name-id", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", "regex", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-cache", "rivet-claims", @@ -868,10 +932,10 @@ dependencies = [ "faker-region", "game-get", "http 0.2.12", - "hyper", + "hyper 0.14.28", "lazy_static", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-cache", "rivet-claims", "rivet-connection", @@ -881,10 +945,12 @@ dependencies = [ "rivet-pools", "rivet-route", "rivet-util-cdn", + "rivet-util-ds", "rivet-util-job", "s3-util", "serde", "serde_json", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "thiserror", "tokio", "tracing", @@ -899,12 +965,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "arrayref" version = "0.3.7" @@ -934,16 +994,16 @@ dependencies = [ "rand", "regex", "ring 0.17.8", - "rustls 0.21.12", + "rustls 0.21.10", "rustls-native-certs 0.6.3", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "rustls-webpki 0.101.7", "serde", "serde_json", "serde_nanos", "serde_repr", "thiserror", - "time 0.3.36", + "time 0.3.34", "tokio", "tokio-retry", "tokio-rustls 0.24.1", @@ -957,7 +1017,7 @@ version = "0.2.3" source = "git+https://github.com/rivet-gg/posthog-rs.git?rev=ef4e80e#ef4e80e57747ea7204794bce9a103bfeccefabf1" dependencies = [ "posthog-core", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -972,7 +1032,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -990,11 +1050,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" -version = "1.3.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "aws-endpoint" @@ -1021,7 +1087,7 @@ dependencies = [ "aws-types", "bytes", "http 0.2.12", - "http-body", + "http-body 0.4.6", "lazy_static", "percent-encoding", "pin-project-lite", @@ -1051,7 +1117,7 @@ dependencies = [ "bytes-utils", "fastrand 1.9.0", "http 0.2.12", - "http-body", + "http-body 0.4.6", "tokio-stream", "tower", "tracing", @@ -1088,7 +1154,7 @@ dependencies = [ "percent-encoding", "regex", "sha2", - "time 0.3.36", + "time 0.3.34", "tracing", ] @@ -1129,7 +1195,7 @@ dependencies = [ "crc32fast", "hex", "http 0.2.12", - "http-body", + "http-body 0.4.6", "md-5", "pin-project-lite", "sha1", @@ -1150,8 +1216,8 @@ dependencies = [ "bytes", "fastrand 1.9.0", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-rustls 0.22.1", "lazy_static", "pin-project", @@ -1174,8 +1240,8 @@ dependencies = [ "bytes", "fastrand 1.9.0", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-rustls 0.23.2", "lazy_static", "pin-project-lite", @@ -1206,8 +1272,8 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "once_cell", "percent-encoding", "pin-project", @@ -1228,14 +1294,14 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.10", "tracing", ] @@ -1248,7 +1314,7 @@ dependencies = [ "aws-smithy-http 0.41.0", "bytes", "http 0.2.12", - "http-body", + "http-body 0.4.6", "pin-project", "tower", "tracing", @@ -1264,7 +1330,7 @@ dependencies = [ "aws-smithy-types 0.52.0", "bytes", "http 0.2.12", - "http-body", + "http-body 0.4.6", "pin-project-lite", "tower", "tracing", @@ -1288,7 +1354,7 @@ dependencies = [ "itoa 1.0.11", "num-integer", "ryu", - "time 0.3.36", + "time 0.3.34", ] [[package]] @@ -1301,7 +1367,7 @@ dependencies = [ "itoa 1.0.11", "num-integer", "ryu", - "time 0.3.36", + "time 0.3.34", ] [[package]] @@ -1341,8 +1407,8 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "itoa 1.0.11", "matchit", "memchr", @@ -1351,7 +1417,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 0.1.2", "tower", "tower-layer", "tower-service", @@ -1367,7 +1433,7 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -1376,9 +1442,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -1454,9 +1520,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" dependencies = [ "serde", ] @@ -1526,10 +1592,10 @@ dependencies = [ "faker-user", "game-get", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "rivet-util-build", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -1544,11 +1610,11 @@ dependencies = [ "futures-util", "indoc 1.0.9", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-connection", "rivet-operation", "rivet-pools", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -1568,7 +1634,7 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1581,14 +1647,14 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "byteorder" @@ -1639,7 +1705,7 @@ dependencies = [ "chirp-client", "chirp-worker", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", ] @@ -1654,7 +1720,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-captcha", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1675,7 +1741,7 @@ dependencies = [ "chirp-client", "chirp-worker", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", ] @@ -1692,7 +1758,7 @@ dependencies = [ "chirp-worker", "chrono", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "rivet-util-captcha", "serde_json", @@ -1700,9 +1766,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.104" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cdn-namespace-auth-user-remove" @@ -1714,7 +1780,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1726,7 +1792,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1737,7 +1803,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1751,7 +1817,7 @@ dependencies = [ "game-resolve-namespace-id", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1766,7 +1832,7 @@ dependencies = [ "game-resolve-namespace-id", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1780,7 +1846,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1793,7 +1859,7 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1805,7 +1871,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1817,7 +1883,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1829,9 +1895,9 @@ dependencies = [ "faker-game", "game-get", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -1847,7 +1913,7 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1860,7 +1926,7 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1873,7 +1939,7 @@ dependencies = [ "faker-game-version", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1902,7 +1968,7 @@ dependencies = [ "itertools 0.10.5", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "unzip-n", ] @@ -1941,7 +2007,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1952,7 +2018,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1963,7 +2029,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -1980,7 +2046,7 @@ dependencies = [ "game-list-for-team", "game-namespace-list", "game-resolve-namespace-id", - "reqwest", + "reqwest 0.11.27", "rivet-health-checks", "rivet-metrics", "rivet-runtime", @@ -2011,7 +2077,7 @@ dependencies = [ "serde", "thiserror", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.10", "tracing", "types", "urlencoding", @@ -2088,7 +2154,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -2102,8 +2168,8 @@ dependencies = [ "futures-util", "global-error", "indoc 2.0.5", - "prost 0.12.6", - "prost-types 0.12.6", + "prost 0.12.4", + "prost-types 0.12.4", "rand", "rivet-cache", "rivet-connection", @@ -2114,7 +2180,7 @@ dependencies = [ "rivet-util", "serde", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "thiserror", "tokio", "tracing", @@ -2128,14 +2194,14 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -2143,7 +2209,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -2179,8 +2245,8 @@ dependencies = [ "clickhouse-derive", "clickhouse-rs-cityhash-sys", "futures", - "hyper", - "hyper-tls", + "hyper 0.14.28", + "hyper-tls 0.5.0", "lz4", "sealed", "serde", @@ -2232,7 +2298,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2244,7 +2310,7 @@ dependencies = [ "cloud-game-config-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2256,7 +2322,7 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -2277,7 +2343,7 @@ dependencies = [ "mm-config-namespace-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2296,7 +2362,7 @@ dependencies = [ "mm-config-namespace-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2310,7 +2376,7 @@ dependencies = [ "game-token-development-validate", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -2325,7 +2391,20 @@ dependencies = [ "prost 0.10.4", "rivet-claims", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", + "token-create", +] + +[[package]] +name = "cloud-service-game-token-create" +version = "0.0.1" +dependencies = [ + "chirp-client", + "chirp-worker", + "faker-game", + "prost 0.10.4", + "rivet-operation", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -2346,7 +2425,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2372,7 +2451,7 @@ dependencies = [ "region-list", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2401,7 +2480,7 @@ dependencies = [ "chrono", "http 0.2.12", "percent-encoding", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "serde_qs", @@ -2418,7 +2497,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2429,7 +2508,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2440,7 +2519,7 @@ dependencies = [ "chirp-worker", "ip-info", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2451,7 +2530,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2462,7 +2541,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2477,7 +2556,7 @@ dependencies = [ "rivet-metrics", "rivet-operation", "rivet-runtime", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -2495,7 +2574,7 @@ dependencies = [ "nomad_client", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2508,7 +2587,7 @@ dependencies = [ "cluster-datacenter-list", "cluster-get", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-connection", "rivet-operation", "rivet-pools", @@ -2577,7 +2656,7 @@ dependencies = [ "rivet-operation", "rivet-runtime", "rivet-util-cluster", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -2592,7 +2671,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2604,7 +2683,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-cluster", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2615,7 +2694,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2631,7 +2710,7 @@ dependencies = [ "rivet-operation", "rivet-runtime", "rivet-util-cluster", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-subscriber", @@ -2645,7 +2724,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2656,7 +2735,7 @@ dependencies = [ "chirp-worker", "cluster-server-list", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2667,7 +2746,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2678,7 +2757,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2689,7 +2768,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -2723,7 +2802,7 @@ dependencies = [ "rivet-util-cluster", "s3-util", "serde_yaml", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "ssh2", "thiserror", "token-create", @@ -2732,16 +2811,16 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.7" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ "bytes", "futures-core", "memchr", "pin-project-lite", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.10", ] [[package]] @@ -2829,9 +2908,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] @@ -2844,27 +2923,27 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32c" -version = "0.6.8" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +checksum = "89254598aa9b9fa608de44b3ae54c810f0f06d755e24c50177f1f8f31ff50ce2" dependencies = [ "rustc_version", ] [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -2899,9 +2978,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" @@ -2957,15 +3036,16 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", + "platforms", "rustc_version", "subtle", ] @@ -2978,7 +3058,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -2990,9 +3070,9 @@ dependencies = [ "custom-user-avatar-upload-complete", "faker-game", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-prepare", ] @@ -3003,9 +3083,9 @@ dependencies = [ "chirp-client", "chirp-worker", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -3013,9 +3093,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -3023,27 +3103,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -3053,17 +3133,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.5", + "hashbrown 0.14.3", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core 0.9.9", ] [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "debug-email-res" @@ -3076,7 +3156,7 @@ dependencies = [ "rand", "rivet-operation", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3118,6 +3198,122 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "ds-server-create" +version = "0.0.1" +dependencies = [ + "bit-vec", + "build-get", + "chirp-client", + "chirp-worker", + "cjson", + "cluster-datacenter-list", + "cluster-list", + "faker-build", + "faker-game", + "faker-region", + "faker-user", + "game-get", + "heck 0.3.3", + "hex", + "http 0.2.12", + "ip-info", + "lazy_static", + "mm-lobby-list-for-user-id", + "nomad-util", + "nomad_client", + "rand", + "regex", + "region-get", + "reqwest 0.11.27", + "rivet-api", + "rivet-connection", + "rivet-operation", + "rivet-util", + "rivet-util-build", + "rivet-util-ds", + "s3-util", + "serde", + "serde_json", + "sha2", + "sqlx 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.24.1", + "tier-list", + "token-create", + "tracing-subscriber", + "upload-get", + "user-identity-get", + "uuid", +] + +[[package]] +name = "ds-server-delete" +version = "0.0.1" +dependencies = [ + "chirp-client", + "chirp-worker", + "nomad-util", + "nomad_client", + "region-get", + "reqwest 0.11.27", + "rivet-operation", + "rivet-util-job", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", +] + +[[package]] +name = "ds-server-get" +version = "0.0.1" +dependencies = [ + "chirp-client", + "chirp-worker", + "cluster-list", + "ds-server-create", + "faker-build", + "faker-game", + "faker-region", + "rivet-operation", + "rivet-util-ds", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", +] + +[[package]] +name = "ds-server-list-for-game" +version = "0.0.1" +dependencies = [ + "chirp-client", + "chirp-worker", + "rivet-operation", + "rivet-util-ds", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", +] + +[[package]] +name = "ds-worker" +version = "0.0.1" +dependencies = [ + "chirp-client", + "chirp-worker", + "chrono", + "ds-server-get", + "job-run-get", + "lazy_static", + "mm-config-version-get", + "mm-lobby-get", + "nomad-util", + "nomad_client", + "region-get", + "reqwest 0.12.5", + "rivet-api", + "rivet-convert", + "rivet-health-checks", + "rivet-metrics", + "rivet-runtime", + "rivet-util-job", + "serde", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -3156,9 +3352,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" dependencies = [ "serde", ] @@ -3204,7 +3400,7 @@ dependencies = [ "chirp-worker", "lazy_static", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", "serde_json", @@ -3221,7 +3417,7 @@ dependencies = [ "email-verification-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3238,18 +3434,18 @@ dependencies = [ "game-logo-upload-complete", "prost 0.10.4", "rand", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-prepare", ] [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -3263,7 +3459,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -3274,9 +3470,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3308,11 +3504,11 @@ dependencies = [ "chrono", "http 0.2.12", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3322,7 +3518,7 @@ dependencies = [ "chirp-client", "chirp-worker", "chrono", - "reqwest", + "reqwest 0.11.27", "rivet-health-checks", "rivet-metrics", "rivet-runtime", @@ -3350,7 +3546,7 @@ dependencies = [ "chirp-worker", "faker-game", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "upload-complete", ] @@ -3480,7 +3676,7 @@ dependencies = [ "region-get", "region-list", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3517,9 +3713,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "ff" @@ -3533,9 +3729,15 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] name = "fixedbitset" @@ -3545,9 +3747,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -3671,7 +3873,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.3", + "parking_lot 0.12.1", ] [[package]] @@ -3688,7 +3890,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -3735,9 +3937,9 @@ dependencies = [ "chirp-worker", "faker-game", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -3754,7 +3956,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-team", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "team-get", ] @@ -3768,7 +3970,7 @@ dependencies = [ "game-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-file-list", "upload-get", ] @@ -3781,7 +3983,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3793,7 +3995,7 @@ dependencies = [ "faker-game", "faker-team", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3804,9 +4006,9 @@ dependencies = [ "chirp-worker", "faker-game", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -3823,7 +4025,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3837,7 +4039,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3851,7 +4053,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3865,7 +4067,7 @@ dependencies = [ "game-namespace-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3907,7 +4109,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3924,7 +4126,7 @@ dependencies = [ "prost 0.10.4", "region-list-for-game", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3936,7 +4138,7 @@ dependencies = [ "faker-game", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3949,7 +4151,7 @@ dependencies = [ "game-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3963,7 +4165,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -3987,7 +4189,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-game-user", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -4001,7 +4203,7 @@ dependencies = [ "game-user-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4016,7 +4218,7 @@ dependencies = [ "game-user-link-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -4031,7 +4233,7 @@ dependencies = [ "game-user-link-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4044,7 +4246,7 @@ dependencies = [ "game-user-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4060,7 +4262,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4089,7 +4291,7 @@ dependencies = [ "rivet-metrics", "rivet-runtime", "rivet-util-game-user", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", "token-exchange", "token-revoke", @@ -4117,7 +4319,7 @@ dependencies = [ "game-version-list", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4130,7 +4332,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4143,7 +4345,7 @@ dependencies = [ "game-version-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4183,9 +4385,9 @@ checksum = "9e006f616a407d396ace1d2ebb3f43ed73189db8b098079bd129928d7645dd1e" [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -4194,9 +4396,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "global-error" @@ -4205,7 +4407,7 @@ dependencies = [ "async-trait", "formatted-error", "http 0.2.12", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -4224,7 +4426,7 @@ dependencies = [ "futures-timer", "no-std-compat", "nonzero_ext", - "parking_lot 0.12.3", + "parking_lot 0.12.1", "portable-atomic", "quanta", "rand", @@ -4234,14 +4436,14 @@ dependencies = [ [[package]] name = "gray_matter" -version = "0.2.8" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ee6a6070bad7c953b0c8be9367e9372181fed69f3e026c4eb5160d8b3c0222" +checksum = "1cf2fb99fac0b821a4e61c61abff076324bb0e5c3b4a83815bbc3518a38971ad" dependencies = [ "serde", "serde_json", "toml 0.5.11", - "yaml-rust2", + "yaml-rust", ] [[package]] @@ -4270,7 +4472,26 @@ dependencies = [ "indexmap 2.2.6", "slab", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util 0.7.10", "tracing", ] @@ -4285,9 +4506,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash 0.8.11", "allocator-api2", @@ -4299,7 +4520,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.14.3", ] [[package]] @@ -4440,11 +4661,34 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" -version = "1.9.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -4460,28 +4704,48 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa 1.0.11", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.6", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.22.1" @@ -4490,7 +4754,7 @@ checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" dependencies = [ "ct-logs", "futures-util", - "hyper", + "hyper 0.14.28", "log", "rustls 0.19.1", "rustls-native-certs 0.5.0", @@ -4506,7 +4770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http 0.2.12", - "hyper", + "hyper 0.14.28", "log", "rustls 0.20.9", "rustls-native-certs 0.6.3", @@ -4523,19 +4787,36 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", - "rustls 0.21.12", + "hyper 0.14.28", + "rustls 0.21.10", "tokio", "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.7", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.28", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -4548,12 +4829,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.28", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2 0.5.6", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -4591,7 +4908,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4603,7 +4920,7 @@ dependencies = [ "identity-config-namespace-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4616,7 +4933,7 @@ dependencies = [ "faker-game-version", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4640,7 +4957,7 @@ dependencies = [ "identity-config-version-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4665,18 +4982,18 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ "proc-macro2", "quote", @@ -4700,7 +5017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.14.3", ] [[package]] @@ -4726,9 +5043,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] @@ -4741,11 +5058,11 @@ dependencies = [ "chirp-worker", "chrono", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4754,10 +5071,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.7", + "socket2 0.5.6", "widestring", "windows-sys 0.48.0", - "winreg", + "winreg 0.50.0", ] [[package]] @@ -4832,7 +5149,7 @@ dependencies = [ "rivet-pools", "rivet-runtime", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -4860,7 +5177,7 @@ dependencies = [ "chirp-client", "chirp-worker", "clickhouse", - "reqwest", + "reqwest 0.11.27", "rivet-health-checks", "rivet-metrics", "rivet-runtime", @@ -4883,7 +5200,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-job", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4897,7 +5214,7 @@ dependencies = [ "indoc 1.0.9", "job-run-get", "lazy_static", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "rivet-util-job", "serde", @@ -4923,7 +5240,7 @@ dependencies = [ "nomad_client", "rand", "region-get", - "reqwest", + "reqwest 0.11.27", "rivet-health-checks", "rivet-metrics", "rivet-runtime", @@ -4931,7 +5248,7 @@ dependencies = [ "rustls 0.20.9", "serde", "sha2", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", "webpki 0.22.4", "webpki-roots 0.22.6", @@ -4968,7 +5285,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4980,7 +5297,7 @@ dependencies = [ "kv-config-namespace-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -4993,7 +5310,7 @@ dependencies = [ "faker-game-version", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5017,7 +5334,7 @@ dependencies = [ "kv-config-version-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5028,7 +5345,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5057,18 +5374,18 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin 0.9.8", + "spin 0.5.2", ] [[package]] name = "libc" -version = "0.2.155" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -5103,9 +5420,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "libc", @@ -5126,7 +5443,7 @@ dependencies = [ "chirp-client", "chirp-worker", "chrono", - "reqwest", + "reqwest 0.11.27", "rivet-connection", "rivet-health-checks", "rivet-metrics", @@ -5136,7 +5453,7 @@ dependencies = [ "rivet-util-linode", "serde", "serde_json", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -5152,7 +5469,7 @@ dependencies = [ "rivet-operation", "rivet-util-cluster", "rivet-util-linode", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5163,11 +5480,11 @@ dependencies = [ "chirp-worker", "cluster-datacenter-get", "linode-server-provision", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "rivet-util-cluster", "rivet-util-linode", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5178,11 +5495,11 @@ dependencies = [ "chirp-worker", "cluster-datacenter-get", "linode-server-destroy", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "rivet-util-cluster", "rivet-util-linode", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5198,14 +5515,14 @@ dependencies = [ "rivet-runtime", "rivet-util-cluster", "rivet-util-linode", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "load-test-api-cloud" @@ -5215,7 +5532,7 @@ dependencies = [ "chirp-worker", "faker-game", "faker-team", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-connection", "rivet-operation", @@ -5245,7 +5562,7 @@ dependencies = [ "mm-lobby-player-count", "mm-player-count-for-namespace", "region-get", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-claims", "rivet-connection", @@ -5275,7 +5592,7 @@ dependencies = [ "mm-config-version-get", "mm-lobby-get", "rand", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-connection", "rivet-operation", @@ -5303,7 +5620,7 @@ dependencies = [ "rivet-metrics", "rivet-operation", "rivet-runtime", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -5318,7 +5635,7 @@ dependencies = [ "chirp-worker", "faker-game", "faker-team", - "reqwest", + "reqwest 0.11.27", "rivet-api", "rivet-connection", "rivet-operation", @@ -5333,9 +5650,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -5343,9 +5660,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru-cache" @@ -5358,9 +5675,9 @@ dependencies = [ [[package]] name = "lz4" -version = "1.25.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6eab492fe7f8651add23237ea56dbf11b3c4ff762ab83d40a47f11433421f91" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" dependencies = [ "libc", "lz4-sys", @@ -5368,9 +5685,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.5" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9764018d143cc854c9f17f0b907de70f14393b1f502da6375dce70f00514eb3" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" dependencies = [ "cc", "libc", @@ -5415,9 +5732,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "merkle_hash" @@ -5439,9 +5756,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.5" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ "mime", "unicase", @@ -5455,9 +5772,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -5480,7 +5797,7 @@ dependencies = [ "chirp-client", "chirp-worker", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5490,7 +5807,7 @@ dependencies = [ "chirp-client", "chirp-worker", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5504,7 +5821,7 @@ dependencies = [ "mm-config-version-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5520,7 +5837,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5534,7 +5851,7 @@ dependencies = [ "mm-config-version-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5548,7 +5865,7 @@ dependencies = [ "mm-config-namespace-get", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5569,7 +5886,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5581,7 +5898,7 @@ dependencies = [ "mm-config-namespace-create", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5598,7 +5915,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5615,13 +5932,13 @@ dependencies = [ "nomad-client", "prost 0.10.4", "region-get", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "rivet-util-build", "rivet-util-job", "rivet-util-mm", "s3-util", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tier-list", "upload-get", ] @@ -5639,7 +5956,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5674,7 +5991,7 @@ dependencies = [ "rivet-pools", "rivet-runtime", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -5723,7 +6040,7 @@ dependencies = [ "chirp-worker", "faker-mm-lobby", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5736,7 +6053,7 @@ dependencies = [ "faker-mm-lobby", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5749,7 +6066,7 @@ dependencies = [ "faker-mm-lobby-row", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5771,7 +6088,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5784,7 +6101,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5796,7 +6113,7 @@ dependencies = [ "faker-mm-lobby", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5809,7 +6126,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5829,7 +6146,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5842,7 +6159,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5855,7 +6172,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-mm", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5867,7 +6184,7 @@ dependencies = [ "faker-mm-lobby", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -5913,7 +6230,7 @@ dependencies = [ "redis-util", "regex", "region-get", - "reqwest", + "reqwest 0.11.27", "rivet-health-checks", "rivet-metrics", "rivet-runtime", @@ -5922,7 +6239,7 @@ dependencies = [ "rivet-util-mm", "s3-util", "serde", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "team-get", "tier-list", "token-create", @@ -5941,6 +6258,7 @@ dependencies = [ "chirp-client", "cloud-worker", "cluster-worker", + "ds-worker", "external-worker", "game-user-worker", "job-log-worker", @@ -5985,10 +6303,11 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ + "lazy_static", "libc", "log", "openssl", @@ -6038,7 +6357,7 @@ version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1548b7290f5a088b0e1069fb41e2d91a5c2c143b1a26bd9327c0fa4a17216bab" dependencies = [ - "reqwest", + "reqwest 0.11.27", "serde", "serde_derive", "serde_json", @@ -6082,7 +6401,7 @@ dependencies = [ "futures-util", "nomad-client", "nomad_client", - "reqwest", + "reqwest 0.11.27", "rivet-pools", "serde", "serde_json", @@ -6096,7 +6415,7 @@ name = "nomad_client" version = "1.1.4" source = "git+https://github.com/rivet-gg/nomad-client?rev=abb66bf0c30c7ff5b0c695dae952481c33e538b5#abb66bf0c30c7ff5b0c695dae952481c33e538b5" dependencies = [ - "reqwest", + "reqwest 0.11.27", "serde", "serde_derive", "serde_json", @@ -6116,7 +6435,7 @@ dependencies = [ "chirp-client", "chirp-worker", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", ] @@ -6142,10 +6461,11 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ + "autocfg", "num-integer", "num-traits", ] @@ -6184,9 +6504,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -6195,9 +6515,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.19" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -6215,9 +6535,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -6234,7 +6554,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -6251,7 +6571,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -6335,12 +6655,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", + "parking_lot_core 0.9.9", ] [[package]] @@ -6359,22 +6679,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.2", + "redox_syscall 0.4.1", "smallvec", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] name = "paste" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pem" @@ -6412,9 +6732,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.11" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -6423,9 +6743,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -6433,22 +6753,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", @@ -6457,9 +6777,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", "indexmap 2.2.6", @@ -6482,7 +6802,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -6524,6 +6844,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "portable-atomic" version = "1.6.0" @@ -6598,9 +6924,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -6619,15 +6945,15 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.3", + "parking_lot 0.12.1", "protobuf", "thiserror", ] @@ -6654,12 +6980,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", - "prost-derive 0.12.6", + "prost-derive 0.12.5", ] [[package]] @@ -6712,15 +7038,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -6744,11 +7070,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ - "prost 0.12.6", + "prost 0.12.4", ] [[package]] @@ -6789,9 +7115,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -6828,11 +7154,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.0.2" +version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" +checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", ] [[package]] @@ -6876,7 +7202,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-retry", - "tokio-util 0.7.11", + "tokio-util 0.7.10", "url", ] @@ -6907,25 +7233,16 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "regex" -version = "1.10.5" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -6939,13 +7256,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.3", ] [[package]] @@ -6956,9 +7273,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "region-get" @@ -6971,7 +7288,7 @@ dependencies = [ "faker-region", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -6985,7 +7302,7 @@ dependencies = [ "prost 0.10.4", "rivet-operation", "rivet-util-cluster", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -6999,7 +7316,7 @@ dependencies = [ "faker-region", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -7014,7 +7331,7 @@ dependencies = [ "region-get", "region-list", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -7029,7 +7346,7 @@ dependencies = [ "region-get", "region-list", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -7044,7 +7361,7 @@ dependencies = [ "region-get", "region-list-for-game", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -7058,12 +7375,12 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-rustls 0.24.2", - "hyper-tls", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -7073,17 +7390,17 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile", + "rustls 0.21.10", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", - "tokio-util 0.7.11", + "tokio-util 0.7.10", "tower-service", "url", "wasm-bindgen", @@ -7091,7 +7408,50 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots 0.25.4", - "winreg", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls 0.27.2", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.52.0", ] [[package]] @@ -7148,7 +7508,7 @@ dependencies = [ name = "rivet-api" version = "0.0.1" dependencies = [ - "reqwest", + "reqwest 0.11.27", "serde", "serde_derive", "serde_json", @@ -7332,7 +7692,7 @@ name = "rivet-health-checks" version = "0.1.0" dependencies = [ "chirp-client", - "hyper", + "hyper 0.14.28", "rivet-pools", "serde", "serde_json", @@ -7416,7 +7776,7 @@ dependencies = [ name = "rivet-metrics" version = "0.1.0" dependencies = [ - "hyper", + "hyper 0.14.28", "lazy_static", "prometheus", "tracing", @@ -7454,7 +7814,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -7466,16 +7826,16 @@ dependencies = [ "funty", "global-error", "governor", - "hyper", - "hyper-tls", + "hyper 0.14.28", + "hyper-tls 0.5.0", "lazy_static", "rand", "redis", "rivet-metrics", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "thiserror", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.10", "tracing", "url", ] @@ -7566,7 +7926,7 @@ dependencies = [ "lazy_static", "rand", "regex", - "reqwest", + "reqwest 0.11.27", "rivet-util-env", "rivet-util-macros", "serde", @@ -7610,6 +7970,26 @@ dependencies = [ "uuid", ] +[[package]] +name = "rivet-util-ds" +version = "0.1.0" +dependencies = [ + "bit-vec", + "chirp-client", + "heck 0.3.3", + "http 0.2.12", + "ip-info", + "mm-lobby-list-for-user-id", + "region-get", + "rivet-operation", + "rivet-util", + "serde", + "serde_json", + "strum 0.24.1", + "user-identity-get", + "uuid", +] + [[package]] name = "rivet-util-env" version = "0.1.0" @@ -7647,7 +8027,7 @@ version = "0.1.0" dependencies = [ "chrono", "rand", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "serde", "serde_json", @@ -7726,9 +8106,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc_version" @@ -7741,11 +8121,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -7779,9 +8159,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring 0.17.8", @@ -7791,15 +8171,27 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.10" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" dependencies = [ "log", - "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.5", + "rustls-webpki 0.102.2", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -7823,7 +8215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -7837,11 +8229,21 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" [[package]] name = "rustls-webpki" @@ -7855,9 +8257,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.5" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -7866,15 +8268,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "s3-util" @@ -7963,11 +8365,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -7976,9 +8378,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -7986,15 +8388,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -8010,13 +8412,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -8032,9 +8434,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa 1.0.11", "ryu", @@ -8063,20 +8465,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -8106,7 +8508,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.36", + "time 0.3.34", ] [[package]] @@ -8118,7 +8520,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -8173,9 +8575,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -8220,7 +8622,7 @@ dependencies = [ "num-bigint", "num-traits", "thiserror", - "time 0.3.36", + "time 0.3.34", ] [[package]] @@ -8250,9 +8652,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", "windows-sys 0.52.0", @@ -8294,26 +8696,72 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" dependencies = [ + "itertools 0.12.1", "nom", "unicode_categories", ] +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sqlx" version = "0.7.4" source = "git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b#08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" dependencies = [ - "sqlx-core", + "sqlx-core 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "sqlx-macros", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", ] +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash 0.8.11", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.2.6", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tracing", + "url", +] + [[package]] name = "sqlx-core" version = "0.7.4" @@ -8364,7 +8812,7 @@ source = "git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72c dependencies = [ "proc-macro2", "quote", - "sqlx-core", + "sqlx-core 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "sqlx-macros-core", "syn 1.0.109", ] @@ -8384,7 +8832,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "sqlx-core", + "sqlx-core 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", @@ -8401,7 +8849,7 @@ source = "git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72c dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.5.0", "byteorder", "bytes", "crc", @@ -8428,7 +8876,7 @@ dependencies = [ "sha1", "sha2", "smallvec", - "sqlx-core", + "sqlx-core 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "stringprep", "thiserror", "tracing", @@ -8444,7 +8892,7 @@ dependencies = [ "atoi", "base64 0.21.7", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.5.0", "byteorder", "crc", "dotenvy", @@ -8468,7 +8916,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "sqlx-core", + "sqlx-core 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "stringprep", "thiserror", "tracing", @@ -8492,7 +8940,7 @@ dependencies = [ "log", "percent-encoding", "serde", - "sqlx-core", + "sqlx-core 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tracing", "url", "urlencoding", @@ -8522,9 +8970,9 @@ dependencies = [ [[package]] name = "ssh-key" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9b366a80cf18bb6406f4cf4d10aebfb46140a8c0c33f666a144c5c76ecbafc" +checksum = "3b71299a724c8d84956caaf8fc3b3ea57c3587fe2d0b800cd0dc1f3599905d7e" dependencies = [ "p256", "p384", @@ -8560,20 +9008,20 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stringprep" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", - "unicode-properties", ] [[package]] name = "strsim" -version = "0.11.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" @@ -8613,14 +9061,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] name = "subtle" -version = "2.6.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -8635,9 +9083,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -8650,6 +9098,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -8679,9 +9133,9 @@ dependencies = [ "chirp-worker", "faker-team", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -8695,7 +9149,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-file-list", "upload-get", ] @@ -8709,7 +9163,7 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8722,7 +9176,7 @@ dependencies = [ "rivet-health-checks", "rivet-metrics", "rivet-runtime", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "team-member-list", "team-user-ban-get", ] @@ -8735,7 +9189,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8746,7 +9200,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8757,7 +9211,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8768,7 +9222,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8780,7 +9234,7 @@ dependencies = [ "faker-team", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8791,7 +9245,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8802,7 +9256,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8814,7 +9268,7 @@ dependencies = [ "faker-team", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "team-get", ] @@ -8828,7 +9282,7 @@ dependencies = [ "prost 0.10.4", "regex", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8839,7 +9293,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8850,7 +9304,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -8874,7 +9328,7 @@ dependencies = [ "faker-team", "game-namespace-list", "mm-lobby-list-for-namespace", - "reqwest", + "reqwest 0.11.27", "rivet-health-checks", "rivet-metrics", "rivet-runtime", @@ -8907,7 +9361,7 @@ dependencies = [ "rivet-operation", "rivet-pools", "rivet-runtime", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "team-get", "team-member-count", "tokio", @@ -8923,29 +9377,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.1.0", + "fastrand 2.0.2", "rustix", "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -8985,9 +9439,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa 1.0.11", @@ -9006,9 +9460,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ "num-conv", "time-core", @@ -9016,9 +9470,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6b6a2fb3a985e99cebfaefa9faa3024743da73304ca1c683a36429613d3d22" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -9042,7 +9496,7 @@ dependencies = [ "prost 0.10.4", "rivet-claims", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -9054,7 +9508,7 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -9067,7 +9521,7 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -9080,25 +9534,25 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] [[package]] name = "tokio" -version = "1.38.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", - "parking_lot 0.12.3", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.6", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -9116,13 +9570,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -9174,7 +9628,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.12", + "rustls 0.21.10", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.7", + "rustls-pki-types", "tokio", ] @@ -9219,15 +9684,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -9253,9 +9719,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -9285,10 +9751,10 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-timeout", "percent-encoding", "pin-project", @@ -9315,7 +9781,7 @@ dependencies = [ "rand", "slab", "tokio", - "tokio-util 0.7.11", + "tokio-util 0.7.10", "tower-layer", "tower-service", "tracing", @@ -9353,7 +9819,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] @@ -9393,7 +9859,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22b8e455f6caa5212a102ec530bf86b8dc5a4c536299bffd84b238fed9119be7" dependencies = [ - "time 0.3.36", + "time 0.3.34", "tracing", "tracing-core", "tracing-subscriber", @@ -9468,7 +9934,7 @@ dependencies = [ "ipconfig", "lru-cache", "once_cell", - "parking_lot 0.12.3", + "parking_lot 0.12.1", "rand", "resolv-conf", "smallvec", @@ -9576,12 +10042,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-properties" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" - [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -9632,10 +10092,10 @@ dependencies = [ "chrono", "nsfw-image-score", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "s3-util", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-get", "upload-prepare", "url", @@ -9650,7 +10110,7 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -9662,7 +10122,7 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-prepare", ] @@ -9675,7 +10135,7 @@ dependencies = [ "chrono", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-prepare", ] @@ -9688,10 +10148,10 @@ dependencies = [ "chrono", "prost 0.10.4", "rand", - "reqwest", + "reqwest 0.11.27", "rivet-operation", "s3-util", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", ] @@ -9705,12 +10165,12 @@ dependencies = [ "futures-util", "indoc 1.0.9", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-connection", "rivet-operation", "rivet-pools", "s3-util", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "tokio", "tracing", "tracing-logfmt", @@ -9727,32 +10187,33 @@ dependencies = [ "rivet-metrics", "rivet-runtime", "s3-util", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-get", "upload-prepare", ] [[package]] name = "ureq" -version = "2.10.0" +version = "2.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "flate2", "log", "once_cell", - "rustls 0.23.10", + "rustls 0.22.3", "rustls-pki-types", + "rustls-webpki 0.102.2", "url", - "webpki-roots 0.26.3", + "webpki-roots 0.26.1", ] [[package]] name = "url" -version = "2.5.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -9773,9 +10234,9 @@ dependencies = [ "chirp-worker", "faker-user", "prost 0.10.4", - "reqwest", + "reqwest 0.11.27", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-complete", "upload-get", "upload-prepare", @@ -9820,7 +10281,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-follow-toggle", ] @@ -9832,7 +10293,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-follow-toggle", ] @@ -9844,7 +10305,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-follow-toggle", ] @@ -9856,7 +10317,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-follow-toggle", ] @@ -9868,7 +10329,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-follow-toggle", ] @@ -9880,7 +10341,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -9904,7 +10365,7 @@ dependencies = [ "prost 0.10.4", "rand", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "upload-file-list", "upload-get", ] @@ -9930,7 +10391,7 @@ dependencies = [ "faker-user", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-identity-create", ] @@ -9943,7 +10404,7 @@ dependencies = [ "faker-user", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-identity-create", ] @@ -9955,7 +10416,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-follow-toggle", ] @@ -9968,7 +10429,7 @@ dependencies = [ "faker-user", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-identity-create", "user-identity-get", ] @@ -10033,7 +10494,7 @@ dependencies = [ "rivet-metrics", "rivet-runtime", "rivet-util-user-presence", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -10046,7 +10507,7 @@ dependencies = [ "profanity-check", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-get", ] @@ -10070,7 +10531,7 @@ dependencies = [ "faker-user", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-identity-create", ] @@ -10083,7 +10544,7 @@ dependencies = [ "faker-user", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-identity-create", ] @@ -10097,7 +10558,7 @@ dependencies = [ "prost 0.10.4", "regex", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "user-identity-create", ] @@ -10131,7 +10592,7 @@ dependencies = [ "chirp-worker", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", ] [[package]] @@ -10143,7 +10604,7 @@ dependencies = [ "faker-user", "prost 0.10.4", "rivet-operation", - "sqlx", + "sqlx 0.7.4 (git+https://github.com/rivet-gg/sqlx?rev=08d6e61aa0572e7ec557abbedb72cebb96e1ac5b)", "token-create", ] @@ -10182,9 +10643,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.9.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "serde", @@ -10256,7 +10717,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -10290,7 +10751,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10361,9 +10822,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" dependencies = [ "rustls-pki-types", ] @@ -10392,9 +10853,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.1.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -10424,7 +10885,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -10442,7 +10903,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.4", ] [[package]] @@ -10462,18 +10923,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -10484,9 +10944,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -10496,9 +10956,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -10508,15 +10968,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -10526,9 +10980,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -10538,9 +10992,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -10550,9 +11004,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -10562,9 +11016,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" @@ -10585,6 +11039,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "workflow-gc" version = "0.0.1" @@ -10609,38 +11073,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] -name = "yaml-rust2" -version = "0.8.1" +name = "yaml-rust" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", + "linked-hash-map", ] [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.58", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/svc/Cargo.toml b/svc/Cargo.toml index 750404838a..a89a0578a3 100644 --- a/svc/Cargo.toml +++ b/svc/Cargo.toml @@ -16,6 +16,7 @@ members = [ "api/monolith", "api/portal", "api/provision", + "api/servers", "api/status", "api/traefik-provider", "pkg/build/ops/create", @@ -51,6 +52,7 @@ members = [ "pkg/cloud/ops/device-link-create", "pkg/cloud/ops/game-config-create", "pkg/cloud/ops/game-config-get", + "pkg/cloud/ops/game-service-token-create", "pkg/cloud/ops/game-token-create", "pkg/cloud/ops/namespace-create", "pkg/cloud/ops/namespace-get", @@ -82,6 +84,11 @@ members = [ "pkg/custom-user-avatar/ops/list-for-game", "pkg/custom-user-avatar/ops/upload-complete", "pkg/debug/ops/email-res", + "pkg/ds/ops/server-create", + "pkg/ds/ops/server-delete", + "pkg/ds/ops/server-get", + "pkg/ds/ops/server-list-for-game", + "pkg/ds/worker", "pkg/email-verification/ops/complete", "pkg/email-verification/ops/create", "pkg/email/ops/send", diff --git a/svc/api/cloud/Cargo.toml b/svc/api/cloud/Cargo.toml index 5aad793ab6..3a7d6a0dac 100644 --- a/svc/api/cloud/Cargo.toml +++ b/svc/api/cloud/Cargo.toml @@ -58,6 +58,7 @@ cloud-device-link-create = { path = "../../pkg/cloud/ops/device-link-create" } cloud-game-config-create = { path = "../../pkg/cloud/ops/game-config-create" } cloud-game-config-get = { path = "../../pkg/cloud/ops/game-config-get" } cloud-game-token-create = { path = "../../pkg/cloud/ops/game-token-create" } +cloud-service-game-token-create = { path = "../../pkg/cloud/ops/game-service-token-create" } cloud-namespace-create = { path = "../../pkg/cloud/ops/namespace-create" } cloud-namespace-get = { path = "../../pkg/cloud/ops/namespace-get" } cloud-namespace-token-development-create = { path = "../../pkg/cloud/ops/namespace-token-development-create" } @@ -105,6 +106,7 @@ team-get = { path = "../../pkg/team/ops/get" } team-member-count = { path = "../../pkg/team/ops/member-count" } team-validate = { path = "../../pkg/team/ops/validate" } tier-list = { path = "../../pkg/tier/ops/list" } +token-create = { path = "../../pkg/token/ops/create" } token-revoke = { path = "../../pkg/token/ops/revoke" } upload-complete = { path = "../../pkg/upload/ops/complete" } upload-file-list = { path = "../../pkg/upload/ops/file-list" } diff --git a/svc/api/cloud/src/route/games/builds.rs b/svc/api/cloud/src/route/games/builds.rs index 0796b99e5b..b182763279 100644 --- a/svc/api/cloud/src/route/games/builds.rs +++ b/svc/api/cloud/src/route/games/builds.rs @@ -59,6 +59,8 @@ pub async fn get_builds( .map_or(0, |upload| upload.content_length) .api_try_into()?, complete: upload.map_or(true, |upload| upload.complete_ts.is_some()), + tags: build + .tags.clone(), }, )) }) diff --git a/svc/api/cloud/src/route/games/tokens.rs b/svc/api/cloud/src/route/games/tokens.rs index ad4e73e8e4..0e2b6a5f1e 100644 --- a/svc/api/cloud/src/route/games/tokens.rs +++ b/svc/api/cloud/src/route/games/tokens.rs @@ -1,9 +1,12 @@ use api_helper::ctx::Ctx; use rivet_api::models; -use rivet_operation::prelude::*; +use rivet_operation::prelude::{proto::backend::pkg::token, *}; use crate::auth::Auth; +// Also see user-token-create/src/main.rs +pub const TOKEN_TTL: i64 = util::duration::minutes(15); + // MARK: POST /games/{}/tokens/cloud pub async fn create_cloud_token( ctx: Ctx, @@ -23,3 +26,23 @@ pub async fn create_cloud_token( token: token_res.token, }) } + +// MARK: POST /games/{}/tokens/service +pub async fn create_service_token( + ctx: Ctx, + game_id: Uuid, + _body: serde_json::Value, +) -> GlobalResult { + ctx.auth() + .check_game_write_or_admin(ctx.op_ctx(), game_id) + .await?; + + let token_res = op!([ctx] cloud_service_game_token_create { + game_id: Some(game_id.into()), + }) + .await?; + + Ok(models::CloudGamesCreateServiceTokenResponse { + token: token_res.token, + }) +} diff --git a/svc/api/cloud/src/route/mod.rs b/svc/api/cloud/src/route/mod.rs index 5eb6f04838..de861ab113 100644 --- a/svc/api/cloud/src/route/mod.rs +++ b/svc/api/cloud/src/route/mod.rs @@ -147,6 +147,9 @@ define_router! { "games" / Uuid / "tokens" / "cloud": { POST: games::tokens::create_cloud_token(body: serde_json::Value), }, + "games" / Uuid / "tokens" / "service": { + POST: games::tokens::create_service_token(body: serde_json::Value), + }, "games" / Uuid / "matchmaker" / "lobbies" / "export-history": { POST: games::matchmaker::export_history(body: models::CloudGamesExportMatchmakerLobbyHistoryRequest), }, diff --git a/svc/api/monolith/Cargo.toml b/svc/api/monolith/Cargo.toml index 0d8132676a..3b33435cdf 100644 --- a/svc/api/monolith/Cargo.toml +++ b/svc/api/monolith/Cargo.toml @@ -31,4 +31,5 @@ api-job = { path = "../job" } api-kv = { path = "../kv" } api-matchmaker = { path = "../matchmaker" } api-portal = { path = "../portal" } +api-servers = { path = "../servers" } api-status = { path = "../status" } diff --git a/svc/api/monolith/Service.toml b/svc/api/monolith/Service.toml index 0fcaa7f929..4f509cae8b 100644 --- a/svc/api/monolith/Service.toml +++ b/svc/api/monolith/Service.toml @@ -19,6 +19,7 @@ paths = [ "/module", "/portal", "/provision", + "/servers", "/status", ] @@ -82,6 +83,12 @@ subdomain = "portal.api" strip-prefix = "/v1" add-path = "/portal" +[[api.router.mounts]] +deprecated = true +subdomain = "servers.api" +strip-prefix = "/v1" +add-path = "/servers" + [[api.router.mounts]] deprecated = true subdomain = "status.api" diff --git a/svc/api/monolith/src/route/mod.rs b/svc/api/monolith/src/route/mod.rs index 8d5905e4c0..0ed0ba74dc 100644 --- a/svc/api/monolith/src/route/mod.rs +++ b/svc/api/monolith/src/route/mod.rs @@ -58,6 +58,10 @@ define_router! { path: api_portal::route::Router, prefix: "portal", }, + { + path: api_servers::route::Router, + prefix: "servers", + }, { path: api_status::route::Router, prefix: "status", diff --git a/svc/api/servers/Cargo.toml b/svc/api/servers/Cargo.toml new file mode 100644 index 0000000000..665236a722 --- /dev/null +++ b/svc/api/servers/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "api-servers" +version = "0.0.1" +authors = ["Rivet Gaming, LLC "] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +api-helper = { path = "../../../lib/api-helper/build" } +chirp-client = { path = "../../../lib/chirp/client" } +rivet-operation = { path = "../../../lib/operation/core" } +chrono = "0.4" +http = "0.2" +hyper = { version = "0.14", features = ["server", "http1", "stream", "tcp"] } +rivet-api = { path = "../../../sdks/full/rust" } +rivet-cache = { path = "../../../lib/cache/build" } +rivet-claims = { path = "../../../lib/claims" } +rivet-health-checks = { path = "../../../lib/health-checks" } +rivet-convert = { path = "../../../lib/convert" } +rivet-pools = { path = "../../../lib/pools" } +s3-util = { path = "../../../lib/s3-util" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.29" } +tracing = "0.1" +tracing-futures = "0.2" +tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "json", "ansi"] } +url = "2.2.2" +uuid = { version = "1", features = ["v4"] } + +build-create = { path = "../../pkg/build/ops/create" } +build-get = { path = "../../pkg/build/ops/get" } +game-namespace-get = { path = "../../pkg/game/ops/namespace-get" } +build-list-for-game = { path = "../../pkg/build/ops/list-for-game" } +cluster-datacenter-get = { path = "../../pkg/cluster/ops/datacenter-get" } +cluster-datacenter-list = { path = "../../pkg/cluster/ops/datacenter-list" } +cluster-datacenter-resolve-for-name-id = { path = "../../pkg/cluster/ops/datacenter-resolve-for-name-id" } +cluster-get = { path = "../../pkg/cluster/ops/get" } +cluster-get-for-game = { path = "../../pkg/cluster/ops/get-for-game" } +game-version-get = { path = "../../pkg/game/ops/version-get" } +cluster-list = { path = "../../pkg/cluster/ops/list" } +cluster-resolve-for-name-id = { path = "../../pkg/cluster/ops/resolve-for-name-id" } +ds-server-create = { path = "../../pkg/ds/ops/server-create" } +ds-server-list-for-game = { path = "../../pkg/ds/ops/server-list-for-game" } +ds-server-delete = { path = "../../pkg/ds/ops/server-delete" } +ds-server-get = { path = "../../pkg/ds/ops/server-get" } +user-identity-get = { path = "../../pkg/user-identity/ops/get" } +game-get = { path = "../../pkg/game/ops/get" } +token-revoke = { path = "../../pkg/token/ops/revoke" } +upload-complete = { path = "../../pkg/upload/ops/complete" } +upload-get = { path = "../../pkg/upload/ops/get" } +user-get = { path = "../../pkg/user/ops/get" } +user-team-list = { path = "../../pkg/user/ops/team-list" } + +[dev-dependencies] +rivet-connection = { path = "../../../lib/connection" } +reqwest = "0.11" + +cdn-namespace-domain-create = { path = "../../pkg/cdn/ops/namespace-domain-create" } +cloud-namespace-token-development-create = { path = "../../pkg/cloud/ops/namespace-token-development-create" } +cloud-namespace-token-public-create = { path = "../../pkg/cloud/ops/namespace-token-public-create" } +cloud-service-game-token-create = { path = "../../pkg/cloud/ops/game-service-token-create" } +faker-build = { path = "../../pkg/faker/ops/build" } +faker-game = { path = "../../pkg/faker/ops/game" } +faker-game-namespace = { path = "../../pkg/faker/ops/game-namespace" } +faker-game-version = { path = "../../pkg/faker/ops/game-version" } +faker-region = { path = "../../pkg/faker/ops/region" } +game-get = { path = "../../pkg/game/ops/get" } +region-get = { path = "../../pkg/region/ops/get" } diff --git a/svc/api/servers/Service.toml b/svc/api/servers/Service.toml new file mode 100644 index 0000000000..c11f709ca4 --- /dev/null +++ b/svc/api/servers/Service.toml @@ -0,0 +1,7 @@ +[service] +name = "api-servers" + +[runtime] +kind = "rust" + +[api-routes] diff --git a/svc/api/servers/src/assert.rs b/svc/api/servers/src/assert.rs new file mode 100644 index 0000000000..009b938784 --- /dev/null +++ b/svc/api/servers/src/assert.rs @@ -0,0 +1,63 @@ +use api_helper::ctx::Ctx; +use proto::backend; +use rivet_operation::prelude::*; + +use crate::auth::Auth; + +/// Validates that a given user ID is registered. +pub async fn user_registered(ctx: &OperationContext<()>, user_id: Uuid) -> GlobalResult<()> { + // If the user has at least one identity they are considered registered + let identity = op!([ctx] user_identity_get { + user_ids: vec![user_id.into()] + }) + .await?; + + let identities = &unwrap_ref!(identity.users.first()).identities; + ensure_with!(!identities.is_empty(), IDENTITY_NOT_REGISTERED); + + Ok(()) +} + +/// Validates that a game's version exists and belongs to the given game ID. +/// +/// Throws `NotFound` if version does not exist and `BadRequest` if does not belong to the given +/// game. +pub async fn version_for_game( + ctx: &Ctx, + game_id: Uuid, + version_id: Uuid, +) -> GlobalResult { + let version_get_res = op!([ctx] game_version_get { + version_ids: vec![version_id.into()], + }) + .await?; + + let version_data = unwrap!(version_get_res.versions.first()); + let version_game_id = unwrap_ref!(version_data.game_id).as_uuid(); + + ensure_eq!(version_game_id, game_id, "version does not belong to game"); + + Ok(version_data.clone()) +} + +/// Validates that a game's namespace exists and belongs to the given game ID. +/// +/// Throws `NotFound` if namespace does not exist and `BadRequest` if does not belong to the given +/// game. +pub async fn namespace_for_game( + ctx: &Ctx, + game_id: Uuid, + namespace_id: Uuid, +) -> GlobalResult { + let ns_get_res = op!([ctx] game_namespace_get { + namespace_ids: vec![namespace_id.into()], + }) + .await?; + + let ns_data = unwrap!(ns_get_res.namespaces.first()); + let ns_game_id = unwrap_ref!(ns_data.game_id).as_uuid(); + + ensure_eq!(ns_game_id, game_id, "namespace does not belong to game"); + + Ok(ns_data.clone()) +} diff --git a/svc/api/servers/src/auth.rs b/svc/api/servers/src/auth.rs new file mode 100644 index 0000000000..df20b1f7d9 --- /dev/null +++ b/svc/api/servers/src/auth.rs @@ -0,0 +1,386 @@ +use std::collections::HashSet; + +use api_helper::{ + auth::{ApiAuth, AuthRateLimitCtx}, + util::{as_auth_expired, basic_rate_limit}, +}; +use proto::{backend, claims::Claims}; +use rivet_claims::ClaimsDecode; +use rivet_operation::prelude::*; + +use crate::assert; + +pub struct Auth { + claims: Option, +} + +#[async_trait] +impl ApiAuth for Auth { + async fn new( + api_token: Option, + rate_limit_ctx: AuthRateLimitCtx<'_>, + ) -> GlobalResult { + Self::rate_limit(rate_limit_ctx).await?; + + Ok(Auth { + claims: if let Some(api_token) = api_token { + Some(as_auth_expired(rivet_claims::decode(&api_token)?)?) + } else { + None + }, + }) + } + + async fn rate_limit(rate_limit_ctx: AuthRateLimitCtx<'_>) -> GlobalResult<()> { + basic_rate_limit(rate_limit_ctx).await + } +} + +impl Auth { + pub fn claims(&self) -> GlobalResult<&Claims> { + self.claims + .as_ref() + .ok_or_else(|| err_code!(API_UNAUTHORIZED, reason = "No bearer token provided.")) + } + + pub fn server(&self) -> GlobalResult { + self.claims()?.as_game_service() + } + + /// Validates that the agent can read the given games or is an admin. + pub async fn check_games_read_or_admin( + &self, + ctx: &OperationContext<()>, + game_ids: Vec, + ) -> GlobalResult<()> { + match self.check_games_read(ctx, game_ids).await { + Err(err) if err.is(formatted_error::code::API_FORBIDDEN) => self.or_admin(ctx).await, + other => other, + } + } + + /// Validates that the agent can read the given games or is an admin. + pub async fn check_game_service_or_cloud_token(&self) -> GlobalResult { + let claims = self.claims()?; + + match (claims.as_game_service(), claims.as_game_cloud()) { + (Ok(game_service), _) => Ok(game_service.game_id), + (_, Ok(game_cloud)) => Ok(game_cloud.game_id), + _ => bail_with!( + CLAIMS_MISSING_ENTITLEMENT, + entitlements = "GameService, GameCloud" + ), + } + } + + /// Validates that the agent can read the given game or is an admin. + pub async fn check_game_read_or_admin( + &self, + ctx: &OperationContext<()>, + game_id: Uuid, + ) -> GlobalResult<()> { + match self.check_game_read(ctx, game_id).await { + Err(err) if err.is(formatted_error::code::API_FORBIDDEN) => self.or_admin(ctx).await, + other => other, + } + } + + /// Validates that the agent can write the given game or is an admin. + pub async fn check_game_write_or_admin( + &self, + ctx: &OperationContext<()>, + game_id: Uuid, + ) -> GlobalResult<()> { + match self.check_game_write(ctx, game_id).await { + Err(err) if err.is(formatted_error::code::API_FORBIDDEN) => self.or_admin(ctx).await, + other => other, + } + } + + /// Validates that the given agent is an admin user. + pub async fn admin(&self, ctx: &OperationContext<()>) -> GlobalResult<()> { + let claims = self.claims()?; + + if claims.as_user().is_ok() { + let (user, _) = self.user(ctx).await?; + + ensure_with!(user.is_admin, IDENTITY_NOT_ADMIN); + + Ok(()) + } else { + bail_with!(CLAIMS_MISSING_ENTITLEMENT, entitlements = "User"); + } + } + + // Helper function + async fn or_admin(&self, ctx: &OperationContext<()>) -> GlobalResult<()> { + match self.admin(ctx).await { + Err(err) + if err.is(formatted_error::code::API_FORBIDDEN) + || err.is(formatted_error::code::IDENTITY_NOT_ADMIN) => + { + bail_with!(CLAIMS_MISSING_ENTITLEMENT, entitlements = "User, GameCloud"); + } + other => other, + } + } + + pub async fn user( + &self, + ctx: &OperationContext<()>, + ) -> GlobalResult<(backend::user::User, rivet_claims::ent::User)> { + let claims = self.claims()?; + let user_ent = claims.as_user()?; + + let user_res = op!([ctx] user_get { + user_ids: vec![user_ent.user_id.into()], + }) + .await?; + let user = unwrap!(user_res.users.first()); + + // Verify user is not deleted + if user.delete_complete_ts.is_some() { + let jti = unwrap!(claims.jti); + op!([ctx] token_revoke { + jtis: vec![jti], + }) + .await?; + + bail_with!(TOKEN_REVOKED); + } + + Ok((user.clone(), user_ent)) + } + + /// Validates that the agent can read a list of teams. + pub async fn check_teams_read( + &self, + ctx: &OperationContext<()>, + team_ids: Vec, + ) -> GlobalResult<()> { + let claims = self.claims()?; + + if claims.as_user().is_ok() { + let (user, user_ent) = self.user(ctx).await?; + // assert::user_registered(ctx, user_ent.user_id).await?; + + let team_list_res = op!([ctx] user_team_list { + user_ids: vec![user_ent.user_id.into()], + }) + .await?; + + let user_teams = unwrap!(team_list_res.users.first()); + let user_team_ids = user_teams + .teams + .iter() + .map(|t| Ok(unwrap_ref!(t.team_id).as_uuid())) + .collect::>>()?; + let has_teams = team_ids + .iter() + .all(|team_id| user_team_ids.contains(team_id)); + + ensure_with!(has_teams || user.is_admin, GROUP_NOT_MEMBER); + + Ok(()) + } else if claims.as_game_cloud().is_ok() { + bail_with!( + API_FORBIDDEN, + reason = "Game cloud token cannot write to this game", + ); + } else { + bail_with!(CLAIMS_MISSING_ENTITLEMENT, entitlements = "User, GameCloud"); + } + } + + // /// Validates that the agent can read a given team. + // pub async fn check_team_read( + // &self, + // ctx: &OperationContext<()>, + // team_id: Uuid, + // ) -> GlobalResult<()> { + // self.check_teams_read(ctx, vec![team_id]).await + // } + + // /// Validates that the agent can write to a given team. + // pub async fn check_team_write( + // &self, + // ctx: &OperationContext<()>, + // team_id: Uuid, + // ) -> GlobalResult<()> { + // tokio::try_join!( + // self.check_team_read(ctx, team_id), + // self.check_dev_team_active(ctx, team_id) + // )?; + + // Ok(()) + // } + + /// Validates that the agent can read a list of games. + pub async fn check_games_read( + &self, + ctx: &OperationContext<()>, + game_ids: Vec, + ) -> GlobalResult<()> { + let claims = self.claims()?; + + if claims.as_user().is_ok() { + let (_user, user_ent) = self.user(ctx).await?; + + assert::user_registered(ctx, user_ent.user_id).await?; + + // Find the game's development teams + let dev_team_ids = { + let games_res = op!([ctx] game_get { + game_ids: game_ids + .into_iter() + .map(Into::into) + .collect::>(), + }) + .await?; + ensure!(!games_res.games.is_empty(), "games not found"); + + games_res + .games + .iter() + .map(|g| Ok(unwrap_ref!(g.developer_team_id).as_uuid())) + .collect::>>()? + }; + + // Validate can read teams + self.check_teams_read(ctx, dev_team_ids).await + } else if let Ok(cloud_ent) = claims.as_game_cloud() { + ensure_with!( + game_ids.iter().any(|id| id == &cloud_ent.game_id), + API_FORBIDDEN, + reason = "Game cloud token cannot write to this game", + ); + + Ok(()) + } else { + bail_with!(CLAIMS_MISSING_ENTITLEMENT, entitlements = "User, GameCloud"); + } + } + + /// Validates that the agent can read a given game. + pub async fn check_game_read( + &self, + ctx: &OperationContext<()>, + game_id: Uuid, + ) -> GlobalResult<()> { + self.check_games_read(ctx, vec![game_id]).await + } + + /// Validates that the agent can write to a given game. + pub async fn check_game_write( + &self, + ctx: &OperationContext<()>, + game_id: Uuid, + ) -> GlobalResult<()> { + let claims = self.claims()?; + + if claims.as_user().is_ok() { + let (_user, user_ent) = self.user(ctx).await?; + + assert::user_registered(ctx, user_ent.user_id).await?; + + // Find the game's development team + let dev_team_id = { + let games_res = op!([ctx] game_get { + game_ids: vec![game_id.into()], + }) + .await?; + let game = unwrap!(games_res.games.first(), "game not found"); + + unwrap_ref!(game.developer_team_id).as_uuid() + }; + + // Validate can write to the team + // self.check_team_write(ctx, dev_team_id).await + Ok(()) + } else if let Ok(cloud_ent) = claims.as_game_cloud() { + ensure_eq_with!( + cloud_ent.game_id, + game_id, + API_FORBIDDEN, + reason = "Game cloud token cannot write to this game", + ); + + Ok(()) + } else { + bail_with!(CLAIMS_MISSING_ENTITLEMENT, entitlements = "User, GameCloud"); + } + } + + // /// Validates that the given dev team is active. + // pub async fn check_dev_team_active( + // &self, + // ctx: &OperationContext<()>, + // team_id: Uuid, + // ) -> GlobalResult<()> { + // let team_res = op!([ctx] team_get { + // team_ids: vec![team_id.into()], + // }) + // .await?; + // let team = unwrap!(team_res.teams.first()); + + // ensure_with!( + // team.deactivate_reasons.is_empty(), + // GROUP_DEACTIVATED, + // reasons = util_team::format_deactivate_reasons(&team.deactivate_reasons)?, + // ); + + // Ok(()) + // } + + // pub async fn accessible_games( + // &self, + // ctx: &OperationContext<()>, + // ) -> GlobalResult { + // let claims = self.claims()?; + + // let (user_id, team_ids, game_ids) = if claims.as_user().is_ok() { + // let (_, user_ent) = self.user(ctx).await?; + + // // Fetch teams associated with user + // let teams_res = op!([ctx] user_team_list { + // user_ids: vec![user_ent.user_id.into()], + // }) + // .await?; + // let user = unwrap!(teams_res.users.first()); + // let team_ids_proto = user + // .teams + // .iter() + // .filter_map(|t| t.team_id) + // .collect::>(); + // let team_ids = team_ids_proto + // .iter() + // .map(common::Uuid::as_uuid) + // .collect::>(); + + // // Fetch games associated with teams + // let games_res = op!([ctx] game_list_for_team { + // team_ids: team_ids_proto, + // }) + // .await?; + + // let game_ids = games_res + // .teams + // .iter() + // .flat_map(|team| &team.game_ids) + // .map(|id| id.as_uuid()) + // .collect::>(); + + // (Some(user_ent.user_id), team_ids, game_ids) + // } else if let Ok(cloud_ent) = claims.as_game_cloud() { + // (None, Vec::new(), vec![cloud_ent.game_id]) + // } else { + // bail_with!(CLAIMS_MISSING_ENTITLEMENT, entitlements = "User, GameCloud"); + // }; + + // Ok(AccessibleGameIdsResponse { + // user_id, + // team_ids, + // game_ids, + // }) + // } +} diff --git a/svc/api/servers/src/lib.rs b/svc/api/servers/src/lib.rs new file mode 100644 index 0000000000..da71d9c8cf --- /dev/null +++ b/svc/api/servers/src/lib.rs @@ -0,0 +1,3 @@ +pub mod assert; +pub mod auth; +pub mod route; diff --git a/svc/api/servers/src/main.rs b/svc/api/servers/src/main.rs new file mode 100644 index 0000000000..26c0bd45b7 --- /dev/null +++ b/svc/api/servers/src/main.rs @@ -0,0 +1,5 @@ +use api_helper::start; + +fn main() { + start(api_servers::route::handle); +} diff --git a/svc/api/servers/src/route/builds.rs b/svc/api/servers/src/route/builds.rs new file mode 100644 index 0000000000..3c519dacd5 --- /dev/null +++ b/svc/api/servers/src/route/builds.rs @@ -0,0 +1,178 @@ +use std::collections::HashMap; + +use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; +use proto::backend; +use rivet_api::models; +use rivet_claims::ClaimsDecode; +use rivet_convert::ApiTryInto; +use rivet_operation::prelude::*; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::auth::Auth; + +// MARK: GET /builds +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetQuery { + tags: Option, +} + +pub async fn get_builds( + ctx: Ctx, + _watch_index: WatchIndexQuery, + query: GetQuery, +) -> GlobalResult { + let game_id = ctx.auth().check_game_service_or_cloud_token().await?; + + let list_res = op!([ctx] build_list_for_game { + game_id: Some(game_id.into()), + tags: query.tags.as_deref().map_or(Ok(HashMap::new()), serde_json::from_str)?, + }) + .await?; + + let builds_res = op!([ctx] build_get { + build_ids: list_res.build_ids.clone(), + }) + .await?; + + let uploads_res = op!([ctx] upload_get { + upload_ids: builds_res + .builds + .iter() + .flat_map(|build| build.upload_id) + .collect::>(), + }) + .await?; + + // Convert the build data structures + let mut builds = builds_res + .builds + .iter() + .map(|build| { + let upload = uploads_res + .uploads + .iter() + .find(|u| u.upload_id == build.upload_id); + if upload.is_none() { + tracing::warn!("unable to find upload for build"); + } + + GlobalResult::Ok(( + build.create_ts, + models::CloudBuildSummary { + build_id: unwrap_ref!(build.build_id).as_uuid(), + upload_id: unwrap_ref!(build.upload_id).as_uuid(), + display_name: build.display_name.clone(), + create_ts: util::timestamp::to_string(build.create_ts)?, + content_length: upload + .map_or(0, |upload| upload.content_length) + .api_try_into()?, + complete: upload.map_or(true, |upload| upload.complete_ts.is_some()), + tags: build.tags.clone(), + }, + )) + }) + .collect::, _>>()?; + + // Sort by date desc + builds.sort_by_key(|(create_ts, _)| *create_ts); + builds.reverse(); + + Ok(models::ServersListBuildsResponse { + builds: builds.into_iter().map(|(_, x)| x).collect::>(), + }) +} + +// MARK: POST /games/{}/versions/builds +pub async fn create_build( + ctx: Ctx, + body: models::ServersCreateBuildRequest, +) -> GlobalResult { + let game_id = ctx.auth().check_game_service_or_cloud_token().await?; + + // TODO: Read and validate image file + + let multipart_upload = body.multipart_upload.unwrap_or(false); + + let kind = match body.kind { + None | Some(models::ServersBuildKind::DockerImage) => { + backend::build::BuildKind::DockerImage + } + Some(models::ServersBuildKind::OciBundle) => backend::build::BuildKind::OciBundle, + }; + + let compression = match body.compression { + None | Some(models::ServersBuildCompression::None) => { + backend::build::BuildCompression::None + } + Some(models::ServersBuildCompression::Lz4) => backend::build::BuildCompression::Lz4, + }; + + // Verify that tags are valid + let tags: HashMap = body + .tags + .map_or(Ok(HashMap::new()), serde_json::from_value)?; + + let create_res = op!([ctx] build_create { + game_id: Some(game_id.into()), + display_name: body.display_name, + image_tag: Some(body.image_tag), + image_file: Some((*body.image_file).api_try_into()?), + multipart: multipart_upload, + kind: kind as i32, + compression: compression as i32, + tags: tags, + }) + .await?; + + let image_presigned_request = if !multipart_upload { + Some(Box::new( + unwrap!(create_res.image_presigned_requests.first()) + .clone() + .api_try_into()?, + )) + } else { + None + }; + + let image_presigned_requests = if multipart_upload { + Some( + create_res + .image_presigned_requests + .iter() + .cloned() + .map(ApiTryInto::api_try_into) + .collect::>>()?, + ) + } else { + None + }; + + Ok(models::ServersCreateBuildResponse { + build_id: unwrap_ref!(create_res.build_id).as_uuid(), + upload_id: unwrap_ref!(create_res.upload_id).as_uuid(), + image_presigned_request, + image_presigned_requests, + }) +} + +// MARK: POST /uploads/{}/complete +pub async fn complete_build( + ctx: Ctx, + upload_id: Uuid, + _body: serde_json::Value, +) -> GlobalResult { + // TODO: use auth module instead + // let claims = ctx.auth().claims()?; + // if claims.as_user().is_err() { + // claims.as_game_cloud()?; + // } + + op!([ctx] @dont_log_body upload_complete { + upload_id: Some(upload_id.into()), + bucket: None, + }) + .await?; + + Ok(json!({})) +} diff --git a/svc/api/servers/src/route/mod.rs b/svc/api/servers/src/route/mod.rs new file mode 100644 index 0000000000..9e46216e6e --- /dev/null +++ b/svc/api/servers/src/route/mod.rs @@ -0,0 +1,54 @@ +use api_helper::define_router; +use hyper::{Body, Request, Response}; +use rivet_api::models; +use uuid::Uuid; + +pub mod builds; +pub mod servers; + +pub async fn handle( + shared_client: chirp_client::SharedClientHandle, + pools: rivet_pools::Pools, + cache: rivet_cache::Cache, + ray_id: uuid::Uuid, + request: Request, +) -> Result, http::Error> { + let response = Response::builder(); + + // Handle route + Router::handle(shared_client, pools, cache, ray_id, request, response).await +} + +define_router! { + routes: { + "" : { + POST: servers::create( + body: models::ServersCreateServerRequest, + ), + }, + + "" / Uuid : { + GET: servers::get(), + DELETE: servers::destroy( + query: servers::DeleteQuery, + ), + }, + + "" / "list": { + GET: servers::list_servers( + query: servers::ListQuery, + ), + }, + + "" / "builds": { + GET: builds::get_builds( + query: builds::GetQuery, + ), + POST: builds::create_build(body: models::ServersCreateBuildRequest), + }, + + "" / "uploads" / Uuid / "complete": { + POST: builds::complete_build(body: serde_json::Value), + }, + }, +} diff --git a/svc/api/servers/src/route/servers.rs b/svc/api/servers/src/route/servers.rs new file mode 100644 index 0000000000..5b25c4d04f --- /dev/null +++ b/svc/api/servers/src/route/servers.rs @@ -0,0 +1,180 @@ +use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; +use proto::backend::{self, pkg::dynamic_servers}; +use rivet_api::models; +use rivet_convert::{ApiFrom, ApiInto, ApiTryFrom, ApiTryInto}; +use rivet_operation::prelude::*; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::auth::Auth; + +// MARK: GET /servers/{server_id} +pub async fn get( + ctx: Ctx, + server_id: Uuid, + _watch_index: WatchIndexQuery, +) -> GlobalResult { + let game_id = ctx.auth().server()?.game_id; + + // Get the server + let get_res = op!([ctx] ds_server_get { + server_ids: vec![server_id.into()], + }) + .await?; + + let server = models::ServersServer::api_try_from( + unwrap_with!(get_res.servers.first(), SERVERS_SERVER_NOT_FOUND).clone(), + )?; + + // Validate token can access server + ensure_with!(server.game_id == game_id, SERVERS_SERVER_NOT_FOUND); + + Ok(models::ServersGetServerResponse { + server: Box::new(server), + }) +} + +// MARK: POST /servers +pub async fn create( + ctx: Ctx, + body: models::ServersCreateServerRequest, +) -> GlobalResult { + let game_id = ctx.auth().server()?.game_id; + let games = op!([ctx] cluster_get_for_game { + game_ids: vec![game_id.into()] + }) + .await? + .games; + + let cluster_id = unwrap!(unwrap!(games.first()).cluster_id); + + let datacenters = op!([ctx] cluster_datacenter_resolve_for_name_id { + cluster_id: Some(cluster_id), + name_ids: vec![body.datacenter.clone()] + }) + .await? + .datacenters; + + if datacenters.is_empty() { + bail_with!(CLUSTER_DATACENTER_NOT_FOUND); + } + + let datacenter_id = unwrap!(unwrap!(datacenters.first()).datacenter_id); + + let tags = serde_json::from_value(body.tags.unwrap_or_default())?; + + tracing::info!("Creating server with tags: {:?}", tags); + + let server = op!([ctx] ds_server_create { + game_id: Some(game_id.into()), + datacenter_id: Some(datacenter_id), + cluster_id: Some(cluster_id), + tags: tags, + resources: Some((*body.resources).api_into()), + kill_timeout_ms: body.kill_timeout.unwrap_or_default(), + webhook_url: body.webhook_url, + image_id: Some(body.image_id.into()), + args: body.arguments.unwrap_or_default(), + network_mode: backend::ds::NetworkMode::api_from( + body.network.mode.unwrap_or_default(), + ) as i32, + environment: body.environment.unwrap_or_default(), + network_ports: unwrap!(body.network + .ports + .into_iter() + .map(|(s, p)| Ok((s, dynamic_servers::server_create::Port { + internal_port: p.internal_port, + routing: Some(if let Some(routing) = p.routing { + match *routing { + models::ServersPortRouting { + game_guard: Some(_), + host: None, + } => dynamic_servers::server_create::port::Routing::GameGuard( + backend::ds::GameGuardRouting { + protocol: backend::ds::GameGuardProtocol::api_from(p.protocol) as i32, + }, + ), + models::ServersPortRouting { + game_guard: None, + host: Some(_), + } => dynamic_servers::server_create::port::Routing::Host(backend::ds::HostRouting { + protocol: backend::ds::HostProtocol::api_try_from(p.protocol)? as i32, + }), + models::ServersPortRouting { .. } => { + bail_with!(SERVERS_MUST_SPECIFY_ROUTING_TYPE) + } + } + } else { + dynamic_servers::server_create::port::Routing::GameGuard(backend::ds::GameGuardRouting { + protocol: backend::ds::GameGuardProtocol::api_from(p.protocol) as i32, + }) + }) + }))) + .collect::>>()), + }) + .await? + .server; + + Ok(models::ServersCreateServerResponse { + server: Box::new(unwrap!(server).api_try_into()?), + }) +} + +// MARK: DELETE /servers/{server_id} +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeleteQuery { + override_kill_timeout: Option, +} + +pub async fn destroy( + ctx: Ctx, + server_id: Uuid, + query: DeleteQuery, +) -> GlobalResult { + let server_id = op!([ctx] ds_server_delete { + server_id: Some(server_id.into()), + override_kill_timeout_ms: query.override_kill_timeout.unwrap_or_default(), + }) + .await? + .server_id; + + Ok(models::ServersDestroyServerResponse { + server_id: unwrap!(server_id).as_uuid(), + }) +} + +// MARK: LIST /servers/list +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListQuery { + tags: Option, +} + +pub async fn list_servers( + ctx: Ctx, + _watch_index: WatchIndexQuery, + query: ListQuery, +) -> GlobalResult { + let game_id = ctx.auth().check_game_service_or_cloud_token().await?; + + let list_res = op!([ctx] ds_server_list_for_game { + game_id: Some(game_id.into()), + tags: query.tags.as_deref().map_or(Ok(HashMap::new()), serde_json::from_str)?, + }) + .await?; + + let servers_res = op!([ctx] ds_server_get { + server_ids: list_res.server_ids.clone(), + }) + .await?; + + let servers = servers_res + .servers + .into_iter() + .map(|server| { + let server = models::ServersServer::api_try_from(server)?; + Ok(server) + }) + .collect::>>()?; + + Ok(models::ServersListServersResponse { servers }) +} diff --git a/svc/api/servers/tests/basic.rs b/svc/api/servers/tests/basic.rs new file mode 100644 index 0000000000..5b7539a9fe --- /dev/null +++ b/svc/api/servers/tests/basic.rs @@ -0,0 +1,225 @@ +use std::{collections::HashMap, sync::Once}; + +use rivet_api::{apis::*, models}; +use rivet_operation::prelude::*; + +static GLOBAL_INIT: Once = Once::new(); + +struct Ctx { + pub op_ctx: OperationContext<()>, + pub ns_auth_token: String, + pub datacenter_id: Uuid, + pub image_id: Uuid, +} + +impl Ctx { + async fn init() -> GlobalResult { + GLOBAL_INIT.call_once(|| { + tracing_subscriber::fmt() + .pretty() + .with_max_level(tracing::Level::INFO) + .with_target(false) + .init(); + }); + + let pools = rivet_pools::from_env("api-servers-test").await?; + let cache = rivet_cache::CacheInner::new( + "api-servers-test".to_string(), + util::env::var("RIVET_SOURCE_HASH")?, + pools.redis_cache()?, + ); + let client = chirp_client::SharedClient::from_env(pools.clone()) + .expect("create client") + .wrap_new("api-servers-test"); + let conn = rivet_connection::Connection::new(client, pools, cache); + let op_ctx = OperationContext::new( + "api-servers-test".to_string(), + std::time::Duration::from_secs(60), + conn, + Uuid::new_v4(), + Uuid::new_v4(), + util::timestamp::now(), + util::timestamp::now(), + (), + ); + + let (datacenter_id, primary_datacenter_name_id) = Self::setup_datacenter(&op_ctx).await?; + let (game_id, image_id) = Self::setup_game(&op_ctx, datacenter_id).await?; + let ns_auth_token = Self::setup_public_token(&op_ctx, game_id).await?; + + Ok(Ctx { + op_ctx, + ns_auth_token, + datacenter_id, + image_id, + }) + } + + fn chirp(&self) -> &chirp_client::Client { + self.op_ctx.chirp() + } + + fn op_ctx(&self) -> &OperationContext<()> { + &self.op_ctx + } + + pub fn config( + &self, + bearer_token: String, + ) -> GlobalResult { + Ok(rivet_api::apis::configuration::Configuration { + base_path: "http://traefik.traefik.svc.cluster.local:80".into(), + bearer_access_token: Some(bearer_token), + client: { + let mut headers = http::header::HeaderMap::new(); + headers.insert( + http::header::HOST, + unwrap!(http::header::HeaderValue::from_str(unwrap!( + util::env::domain_main_api() + ))), + ); + headers.insert( + "cf-connecting-ip", + http::header::HeaderValue::from_static("127.0.0.1"), + ); + unwrap!(reqwest::Client::builder().default_headers(headers).build()) + }, + ..Default::default() + }) + } + + pub async fn setup_datacenter(ctx: &OperationContext<()>) -> GlobalResult<(Uuid, String)> { + tracing::info!("setup region"); + + let region_res = op!([ctx] faker_region {}).await?; + let region_id = unwrap!(region_res.region_id).as_uuid(); + + let get_res = op!([ctx] region_get { + region_ids: vec![region_id.into()], + }) + .await?; + let region_data = unwrap!(get_res.regions.first()); + + Ok((region_id, region_data.name_id.clone())) + } + + pub async fn setup_game( + ctx: &OperationContext<()>, + region_id: Uuid, + ) -> GlobalResult<(Uuid, Uuid)> { + let game_res = op!([ctx] faker_game { + ..Default::default() + }) + .await?; + let game_id = unwrap!(game_res.game_id); + + let build_res = op!([ctx] faker_build { + game_id: Some(game_id), + image: proto::backend::faker::Image::DsEcho as i32, + }) + .await?; + + Ok(( + unwrap!(game_res.game_id).as_uuid(), + unwrap!(build_res.build_id).as_uuid(), + )) + } + + pub async fn setup_public_token( + ctx: &OperationContext<()>, + game_id: Uuid, + ) -> GlobalResult { + let token_res = op!([ctx] cloud_service_game_token_create { + game_id: Some(game_id.into()), + }) + .await?; + + Ok(token_res.token) + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn create_http() -> GlobalResult<()> { + let ctx = Ctx::init().await?; + + let ctx_config = ctx.config(ctx.ns_auth_token.clone())?; + + let res = servers_api::servers_create( + &ctx_config, + models::ServersCreateServerRequest { + arguments: None, + datacenter: ctx.datacenter_id.to_string(), + environment: Some(HashMap::new()), + image_id: ctx.image_id, + kill_timeout: Some(0), + webhook_url: None, + tags: None, + network: Box::new(models::ServersCreateServerNetworkRequest { + mode: Some(models::ServersNetworkMode::Bridge), + ports: vec![( + "testing2".to_string(), + models::ServersCreateServerPortRequest { + protocol: models::ServersPortProtocol::Http, + routing: Some(Box::new(models::ServersPortRouting { + game_guard: Some(serde_json::Value::Object(serde_json::Map::new())), + host: None, + })), + internal_port: Some(12523), + }, + )] + // Collect into hashmap + .into_iter() + .collect(), + }), + resources: Box::new(models::ServersResources { + cpu: 100, + memory: 200, + }), + }, + ); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +async fn list_builds_with_tags() -> GlobalResult<()> { + let ctx = Ctx::init().await?; + + let ctx_config = ctx.config(ctx.ns_auth_token.clone())?; + + let res = servers_api::servers_create( + &ctx_config, + models::ServersCreateServerRequest { + arguments: None, + datacenter: ctx.datacenter_id.to_string(), + environment: Some(HashMap::new()), + image_id: ctx.image_id, + kill_timeout: Some(0), + webhook_url: None, + tags: None, + network: Box::new(models::ServersCreateServerNetworkRequest { + mode: Some(models::ServersNetworkMode::Bridge), + ports: vec![( + "testing2".to_string(), + models::ServersCreateServerPortRequest { + protocol: models::ServersPortProtocol::Http, + routing: Some(Box::new(models::ServersPortRouting { + game_guard: Some(serde_json::Value::Object(serde_json::Map::new())), + host: None, + })), + internal_port: Some(12523), + }, + )] + // Collect into hashmap + .into_iter() + .collect(), + }), + resources: Box::new(models::ServersResources { + cpu: 100, + memory: 200, + }), + }, + ); + + Ok(()) +} diff --git a/svc/api/traefik-provider/Cargo.toml b/svc/api/traefik-provider/Cargo.toml index 69708ac008..1fbf61dc79 100644 --- a/svc/api/traefik-provider/Cargo.toml +++ b/svc/api/traefik-provider/Cargo.toml @@ -33,12 +33,20 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [ "ansi", ] } url = "2.2.2" +util-ds = { package = "rivet-util-ds", path = "../../pkg/ds/util" } util-cdn = { package = "rivet-util-cdn", path = "../../pkg/cdn/util" } util-job = { package = "rivet-util-job", path = "../../pkg/job/util" } uuid = { version = "1", features = ["v4"] } cluster-server-list = { path = "../../pkg/cluster/ops/server-list" } +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false +features = ["json"] + + [dev-dependencies] rivet-connection = { path = "../../../lib/connection" } rivet-route = { path = "../../../lib/smithy-output/api-traefik-provider/rust" } diff --git a/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs b/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs new file mode 100644 index 0000000000..26e4b6c672 --- /dev/null +++ b/svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs @@ -0,0 +1,334 @@ +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, +}; + +use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; +use proto::backend::{self, pkg::*}; +use redis::AsyncCommands; +use rivet_operation::prelude::*; +use rivet_pools::prelude::*; +use serde::{Deserialize, Serialize}; +use url::Url; + +use crate::{auth::Auth, types}; + +// TODO: Rename to ProxiedPort since this is not 1:1 with servers +#[derive(sqlx::FromRow, Clone, Debug, Serialize, Deserialize)] +struct DynamicServer { + server_id: Uuid, + datacenter_id: Uuid, + label: String, + nomad_ip: String, + nomad_source: i64, + port_number: i64, + gg_port: i64, + port_name: String, + protocol: i64, +} + +impl DynamicServer { + fn parent_host(&self) -> GlobalResult { + Ok(format!( + "lobby.{}.{}", + self.datacenter_id, + unwrap!(util::env::domain_job()), + )) + } + + fn hostname(&self) -> GlobalResult { + util_ds::build_ds_hostname(self.server_id, &self.port_name, self.datacenter_id) + } +} + +pub async fn build_ds( + ctx: &Ctx, + dc_id: Uuid, + config: &mut types::TraefikConfigResponse, +) -> GlobalResult<()> { + // TODO put in function, clean up + // TODO: remove cache for now + tracing::info!(?config, "config timeeee"); + + // let dynamic_servers: Option> = ctx + // .cache() + // // TODO: Set this for longer, this should mean that no caching happens + // .ttl(1) + // .fetch_one_json("servers_ports", dc_id, |mut cache, dc_id| { + // let ctx = ctx.clone(); + // async move { + let dynamic_servers = sql_fetch_all!( + [ctx, DynamicServer] + " + SELECT + servers.server_id, + servers.datacenter_id, + internal_ports.nomad_label AS label, + internal_ports.nomad_ip, + internal_ports.nomad_source, + docker_ports_protocol_game_guard.port_number, + docker_ports_protocol_game_guard.gg_port, + docker_ports_protocol_game_guard.port_name, + docker_ports_protocol_game_guard.protocol + FROM + db_dynamic_servers.internal_ports + JOIN + db_dynamic_servers.servers + ON + internal_ports.server_id = servers.server_id + JOIN + db_dynamic_servers.docker_ports_protocol_game_guard + ON + internal_ports.server_id = docker_ports_protocol_game_guard.server_id + AND + internal_ports.nomad_label = CONCAT('ds_', docker_ports_protocol_game_guard.port_name) + WHERE + servers.datacenter_id = $1 AND servers.stop_ts IS NULL + ", + dc_id + ) + .await?; + // cache.resolve(&dc_id, rows); + + // Ok(cache) + // } + // }) + // .await?; + + tracing::info!(?config, "config timeeee2"); + + // let dynamic_servers = unwrap!(dynamic_servers); + tracing::info!(?dynamic_servers, "ds0time"); + + // Process proxied ports + for dynamic_server in &dynamic_servers { + tracing::info!(?dynamic_server, "ds1time"); + + let server_id = dynamic_server.server_id; + let register_res = ds_register_proxied_port(server_id, dynamic_server, config); + match register_res { + Ok(_) => {} + Err(err) => { + tracing::error!(?err, "failed to register proxied port route") + } + } + } + + tracing::info!(?config, "config timeeee3"); + + config.http.middlewares.insert( + "ds-rate-limit".to_owned(), + types::TraefikMiddlewareHttp::RateLimit { + average: 100, + period: "5m".into(), + burst: 256, + source_criterion: types::InFlightReqSourceCriterion::IpStrategy(types::IpStrategy { + depth: 0, + exclude_ips: None, + }), + }, + ); + config.http.middlewares.insert( + "ds-in-flight".to_owned(), + types::TraefikMiddlewareHttp::InFlightReq { + // This number needs to be high to allow for parallel requests + amount: 4, + source_criterion: types::InFlightReqSourceCriterion::IpStrategy(types::IpStrategy { + depth: 0, + exclude_ips: None, + }), + }, + ); + + tracing::info!(?config, "config timeeee"); + + // TODO: add middleware & services & ports + // TODO: same as jobs, watch out for namespaces + Ok(()) +} + +#[tracing::instrument(skip(config))] +fn ds_register_proxied_port( + run_id: Uuid, + proxied_port: &DynamicServer, + config: &mut types::TraefikConfigResponse, +) -> GlobalResult<()> { + let ingress_port = proxied_port.gg_port.clone(); + let target_nomad_port_label = proxied_port.label.clone(); + let service_id = format!("ds-run:{}:{}", run_id, target_nomad_port_label); + let proxy_protocol = unwrap!(backend::ds::GameGuardProtocol::from_i32( + proxied_port.protocol as i32 + )); + + // Insert the relevant service + match proxy_protocol { + backend::ds::GameGuardProtocol::Http | backend::ds::GameGuardProtocol::Https => { + config.http.services.insert( + service_id.clone(), + types::TraefikService { + load_balancer: types::TraefikLoadBalancer { + servers: vec![types::TraefikServer { + url: Some(format!( + "http://{}:{}", + proxied_port.nomad_ip, proxied_port.nomad_source + )), + address: None, + }], + sticky: None, + }, + }, + ); + } + backend::ds::GameGuardProtocol::Tcp | backend::ds::GameGuardProtocol::TcpTls => { + config.tcp.services.insert( + service_id.clone(), + types::TraefikService { + load_balancer: types::TraefikLoadBalancer { + servers: vec![types::TraefikServer { + url: None, + address: Some(format!( + "{}:{}", + proxied_port.nomad_ip, proxied_port.nomad_source + )), + }], + sticky: None, + }, + }, + ); + } + backend::ds::GameGuardProtocol::Udp => { + config.udp.services.insert( + service_id.clone(), + types::TraefikService { + load_balancer: types::TraefikLoadBalancer { + servers: vec![types::TraefikServer { + url: None, + address: Some(format!( + "{}:{}", + proxied_port.nomad_ip, proxied_port.nomad_source + )), + }], + sticky: None, + }, + }, + ); + } + }; + + // Insert the relevant router + match proxy_protocol { + backend::ds::GameGuardProtocol::Http => { + // Generate config + let middlewares = http_router_middlewares(); + let rule = format_http_rule(proxied_port)?; + + // Hash key + let unique_key = (&run_id, &target_nomad_port_label, &rule, &middlewares); + let mut hasher = DefaultHasher::new(); + unique_key.hash(&mut hasher); + let hash = hasher.finish(); + + config.http.routers.insert( + format!("ds-run:{run_id}:{hash:x}:http"), + types::TraefikRouter { + entry_points: vec![format!("lb-{ingress_port}")], + rule: Some(rule), + priority: None, + service: service_id.clone(), + middlewares, + tls: None, + }, + ); + } + backend::ds::GameGuardProtocol::Https => { + // Generate config + let middlewares = http_router_middlewares(); + let rule = format_http_rule(proxied_port)?; + + // Hash key + let unique_key = (&run_id, &target_nomad_port_label, &rule, &middlewares); + let mut hasher = DefaultHasher::new(); + unique_key.hash(&mut hasher); + let hash = hasher.finish(); + + config.http.routers.insert( + format!("ds-run:{run_id}:{hash:x}:https"), + types::TraefikRouter { + entry_points: vec![format!("lb-{ingress_port}")], + rule: Some(rule), + priority: None, + service: service_id.clone(), + middlewares, + tls: Some(types::TraefikTls::build(build_tls_domains(proxied_port)?)), + }, + ); + } + backend::ds::GameGuardProtocol::Tcp => { + config.tcp.routers.insert( + format!("ds-run:{}:{}:tcp", run_id, target_nomad_port_label), + types::TraefikRouter { + entry_points: vec![format!("lb-{ingress_port}-tcp")], + rule: Some("HostSNI(`*`)".into()), + priority: None, + service: service_id, + middlewares: vec![], + tls: None, + }, + ); + } + backend::ds::GameGuardProtocol::TcpTls => { + config.tcp.routers.insert( + format!("ds-run:{}:{}:tcp-tls", run_id, target_nomad_port_label), + types::TraefikRouter { + entry_points: vec![format!("lb-{ingress_port}-tcp")], + rule: Some("HostSNI(`*`)".into()), + priority: None, + service: service_id, + middlewares: vec![], + tls: Some(types::TraefikTls::build(build_tls_domains(proxied_port)?)), + }, + ); + } + backend::ds::GameGuardProtocol::Udp => { + config.udp.routers.insert( + format!("ds-run:{}:{}:udp", run_id, target_nomad_port_label), + types::TraefikRouter { + entry_points: vec![format!("lb-{ingress_port}-udp")], + rule: None, + priority: None, + service: service_id, + middlewares: vec![], + tls: None, + }, + ); + } + } + + Ok(()) +} + +fn format_http_rule(proxied_port: &DynamicServer) -> GlobalResult { + Ok(format!("Host(`{}`)", proxied_port.hostname()?)) +} + +fn build_tls_domains(proxied_port: &DynamicServer) -> GlobalResult> { + // Derive TLS config. Jobs can specify their own ingress rules, so we + // need to derive which domains to use for the job. + // + // A parent wildcard SSL mode will use the parent domain as the SSL + // name. + let mut domains = Vec::new(); + let parent_host = proxied_port.parent_host()?; + domains.push(types::TraefikTlsDomain { + main: parent_host.to_owned(), + sans: vec![format!("*.{}", parent_host)], + }); + + Ok(domains) +} + +fn http_router_middlewares() -> Vec { + let middlewares = vec!["ds-rate-limit".to_string(), "ds-in-flight".to_string()]; + + middlewares +} diff --git a/svc/api/traefik-provider/src/route/game_guard.rs b/svc/api/traefik-provider/src/route/game_guard/job.rs similarity index 87% rename from svc/api/traefik-provider/src/route/game_guard.rs rename to svc/api/traefik-provider/src/route/game_guard/job.rs index 5f7758a635..05d9c7a39c 100644 --- a/svc/api/traefik-provider/src/route/game_guard.rs +++ b/svc/api/traefik-provider/src/route/game_guard/job.rs @@ -12,52 +12,13 @@ use url::Url; use crate::{auth::Auth, types}; -#[derive(Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ConfigQuery { - token: String, - datacenter: Uuid, -} - -#[tracing::instrument(skip(ctx))] -pub async fn config( - ctx: Ctx, - _watch_index: WatchIndexQuery, - ConfigQuery { token, datacenter }: ConfigQuery, -) -> GlobalResult { - ctx.auth().token(&token).await?; - - // Fetch configs and catch any errors - let config = build_job(&ctx, datacenter).await?; - - // tracing::info!( - // http_services = ?config.http.services.len(), - // http_routers = config.http.routers.len(), - // http_middlewares = ?config.http.middlewares.len(), - // tcp_services = ?config.tcp.services.len(), - // tcp_routers = config.tcp.routers.len(), - // tcp_middlewares = ?config.tcp.middlewares.len(), - // udp_services = ?config.udp.services.len(), - // udp_routers = config.udp.routers.len(), - // udp_middlewares = ?config.udp.middlewares.len(), - // "traefik config" - // ); - - Ok(types::TraefikConfigResponseNullified { - http: config.http.nullified(), - tcp: config.tcp.nullified(), - udp: config.udp.nullified(), - }) -} - /// Builds configuration for job routes. #[tracing::instrument(skip(ctx))] pub async fn build_job( ctx: &Ctx, region_id: Uuid, -) -> GlobalResult { - let mut config = types::TraefikConfigResponse::default(); - + config: &mut types::TraefikConfigResponse, +) -> GlobalResult<()> { let redis_job = ctx.op_ctx().redis_job().await?; let job_runs_fetch = fetch_job_runs(redis_job, region_id).await?; @@ -90,7 +51,7 @@ pub async fn build_job( let run_id = unwrap_ref!(run_proxied_ports.run_id); tracing::info!(proxied_ports_len = ?run_proxied_ports.proxied_ports.len(), "adding job run"); for proxied_port in &run_proxied_ports.proxied_ports { - let register_res = register_proxied_port(**run_id, proxied_port, &mut config); + let register_res = job_register_proxied_port(**run_id, proxied_port, config); match register_res { Ok(_) => {} Err(err) => { @@ -113,7 +74,7 @@ pub async fn build_job( "job traefik config" ); - Ok(config) + Ok(()) } #[tracing::instrument(skip(redis_job))] @@ -141,7 +102,7 @@ async fn fetch_job_runs( } #[tracing::instrument(skip(config))] -fn register_proxied_port( +fn job_register_proxied_port( run_id: Uuid, proxied_port: &job::redis_job::run_proxied_ports::ProxiedPort, config: &mut types::TraefikConfigResponse, diff --git a/svc/api/traefik-provider/src/route/game_guard/mod.rs b/svc/api/traefik-provider/src/route/game_guard/mod.rs new file mode 100644 index 0000000000..e12012dec5 --- /dev/null +++ b/svc/api/traefik-provider/src/route/game_guard/mod.rs @@ -0,0 +1,64 @@ +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, +}; + +use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; +use dynamic_servers::build_ds; +use job::build_job; +use proto::backend::{self, pkg::*}; +use redis::AsyncCommands; +use rivet_operation::prelude::*; +use serde::{Deserialize, Serialize}; +use url::Url; + +use crate::{auth::Auth, types}; + +pub mod dynamic_servers; +pub mod job; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigQuery { + token: String, + datacenter: Uuid, +} + +#[tracing::instrument(skip(ctx))] +pub async fn config( + ctx: Ctx, + _watch_index: WatchIndexQuery, + ConfigQuery { token, datacenter }: ConfigQuery, +) -> GlobalResult { + ctx.auth().token(&token).await?; + + let mut config = types::TraefikConfigResponse::default(); + + // Fetch configs and catch any errors + tracing::info!(?config, "traefik config ds"); + tracing::info!("asdgaerwvsdfvasdf"); + + build_ds(&ctx, datacenter, &mut config).await?; + build_job(&ctx, datacenter, &mut config).await?; + + tracing::info!(?config, "traefik config ds"); + + // tracing::info!( + // http_services = ?config.http.services.len(), + // http_routers = config.http.routers.len(), + // http_middlewares = ?config.http.middlewares.len(), + // tcp_services = ?config.tcp.services.len(), + // tcp_routers = config.tcp.routers.len(), + // tcp_middlewares = ?config.tcp.middlewares.len(), + // udp_services = ?config.udp.services.len(), + // udp_routers = config.udp.routers.len(), + // udp_middlewares = ?config.udp.middlewares.len(), + // "traefik config" + // ); + + Ok(types::TraefikConfigResponseNullified { + http: config.http.nullified(), + tcp: config.tcp.nullified(), + udp: config.udp.nullified(), + }) +} diff --git a/svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.down.sql b/svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.up.sql b/svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.up.sql new file mode 100644 index 0000000000..a5e6a83f67 --- /dev/null +++ b/svc/pkg/build/db/build/migrations/20240717023701_add_build_tags.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE builds + ADD COLUMN tags JSONB NOT NULL DEFAULT '{}'; \ No newline at end of file diff --git a/svc/pkg/build/ops/create/src/lib.rs b/svc/pkg/build/ops/create/src/lib.rs index 958ee042fe..ca79af8972 100644 --- a/svc/pkg/build/ops/create/src/lib.rs +++ b/svc/pkg/build/ops/create/src/lib.rs @@ -98,8 +98,20 @@ async fn handle( sql_execute!( [ctx] " - INSERT INTO db_build.builds (build_id, game_id, upload_id, display_name, image_tag, create_ts, kind, compression) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + INSERT INTO + db_build.builds ( + build_id, + game_id, + upload_id, + display_name, + image_tag, + create_ts, + kind, + compression, + tags + ) + VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9) ", build_id, game_id, @@ -109,6 +121,7 @@ async fn handle( ctx.ts(), kind as i32, compression as i32, + serde_json::to_value(ctx.tags.clone())?, ) .await?; diff --git a/svc/pkg/build/ops/get/src/lib.rs b/svc/pkg/build/ops/get/src/lib.rs index 42cf6f7305..4ddbd59b3a 100644 --- a/svc/pkg/build/ops/get/src/lib.rs +++ b/svc/pkg/build/ops/get/src/lib.rs @@ -1,5 +1,8 @@ +use std::collections::HashMap; + use proto::backend::{self, pkg::*}; use rivet_operation::prelude::*; +use serde_json::Value; #[derive(sqlx::FromRow)] struct BuildRow { @@ -11,6 +14,7 @@ struct BuildRow { create_ts: i64, kind: i64, compression: i64, + tags: Value, } #[operation(name = "build-get")] @@ -24,25 +28,39 @@ async fn handle(ctx: OperationContext) -> GlobalResult>(); + .collect::>>()?; Ok(build::get::Response { builds }) } diff --git a/svc/pkg/build/ops/list-for-game/src/lib.rs b/svc/pkg/build/ops/list-for-game/src/lib.rs index 389e32438a..0690cf34da 100644 --- a/svc/pkg/build/ops/list-for-game/src/lib.rs +++ b/svc/pkg/build/ops/list-for-game/src/lib.rs @@ -10,11 +10,17 @@ async fn handle( let build_ids = sql_fetch_all!( [ctx, (Uuid,)] " - SELECT build_id - FROM db_build.builds - WHERE game_id = $1 + SELECT + build_id + FROM + db_build.builds + WHERE + game_id = $1 + AND + tags @> $2 ", game_id, + serde_json::to_value(&ctx.tags)? ) .await? .into_iter() diff --git a/svc/pkg/build/ops/list-for-game/tests/integration.rs b/svc/pkg/build/ops/list-for-game/tests/integration.rs index fd98990b15..c6b4e4f3b8 100644 --- a/svc/pkg/build/ops/list-for-game/tests/integration.rs +++ b/svc/pkg/build/ops/list-for-game/tests/integration.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, hash::Hash}; + use chirp_worker::prelude::*; use proto::backend::{self}; @@ -26,6 +28,7 @@ async fn empty(ctx: TestCtx) { let res = op!([ctx] build_list_for_game { game_id: game_res.game_id, + tags: HashMap::new(), }) .await .unwrap(); diff --git a/svc/pkg/build/types/create.proto b/svc/pkg/build/types/create.proto index 7471815e0a..ecef100dbd 100644 --- a/svc/pkg/build/types/create.proto +++ b/svc/pkg/build/types/create.proto @@ -14,6 +14,7 @@ message Request { bool multipart = 6; rivet.backend.build.BuildKind kind = 7; rivet.backend.build.BuildCompression compression = 8; + map tags = 9; // JSON // If provided, will look up the pre-uploaded upload ID for a given default // build kind. This is used to create builds from reusable pre-built images. diff --git a/svc/pkg/build/types/list-for-game.proto b/svc/pkg/build/types/list-for-game.proto index 288c3b402e..659eff1353 100644 --- a/svc/pkg/build/types/list-for-game.proto +++ b/svc/pkg/build/types/list-for-game.proto @@ -6,6 +6,7 @@ import "proto/common.proto"; message Request { rivet.common.Uuid game_id = 1; + map tags = 2; // JSON } message Response { diff --git a/svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.down.sql b/svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.up.sql b/svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.up.sql new file mode 100644 index 0000000000..5dbc294999 --- /dev/null +++ b/svc/pkg/cloud/db/cloud/migrations/20240708222624_game_service_token.up.sql @@ -0,0 +1,5 @@ +CREATE TABLE service_cloud_tokens ( + game_id UUID NOT NULL REFERENCES game_configs (game_id), + token_session_id UUID NOT NULL, -- References db-tokens.sessions + PRIMARY KEY (game_id, token_session_id) +) \ No newline at end of file diff --git a/svc/pkg/cloud/ops/game-service-token-create/Cargo.toml b/svc/pkg/cloud/ops/game-service-token-create/Cargo.toml new file mode 100644 index 0000000000..b9044852a4 --- /dev/null +++ b/svc/pkg/cloud/ops/game-service-token-create/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "cloud-service-game-token-create" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +rivet-operation = { path = "../../../../../lib/operation/core" } +chirp-client = { path = "../../../../../lib/chirp/client" } +prost = "0.10" + +token-create = { path = "../../../token/ops/create" } + +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false + +[dev-dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } + +faker-game = { path = "../../../faker/ops/game" } diff --git a/svc/pkg/cloud/ops/game-service-token-create/README.md b/svc/pkg/cloud/ops/game-service-token-create/README.md new file mode 100644 index 0000000000..45fefbb2c7 --- /dev/null +++ b/svc/pkg/cloud/ops/game-service-token-create/README.md @@ -0,0 +1 @@ +# cloud-game-token-create diff --git a/svc/pkg/cloud/ops/game-service-token-create/Service.toml b/svc/pkg/cloud/ops/game-service-token-create/Service.toml new file mode 100644 index 0000000000..3da51dee5e --- /dev/null +++ b/svc/pkg/cloud/ops/game-service-token-create/Service.toml @@ -0,0 +1,10 @@ +[service] +name = "cloud-game-token-create" + +[runtime] +kind = "rust" + +[operation] + +[databases] +db-cloud = {} diff --git a/svc/pkg/cloud/ops/game-service-token-create/src/lib.rs b/svc/pkg/cloud/ops/game-service-token-create/src/lib.rs new file mode 100644 index 0000000000..4f1403f017 --- /dev/null +++ b/svc/pkg/cloud/ops/game-service-token-create/src/lib.rs @@ -0,0 +1,46 @@ +use common::glob::Token; +use proto::backend::pkg::*; +use rivet_operation::prelude::*; + +#[operation(name = "cloud-service-game-token-create")] +async fn handle( + ctx: OperationContext, +) -> GlobalResult { + let game_id = unwrap_ref!(ctx.game_id).as_uuid(); + + let token_res = op!([ctx] token_create { + token_config: Some(token::create::request::TokenConfig { + ttl: util::duration::days(15 * 365) + }), + refresh_token_config: None, + issuer: "api-cloud".to_owned(), + client: None, + kind: Some(token::create::request::Kind::New( + token::create::request::KindNew { entitlements: vec![proto::claims::Entitlement { + kind: Some(proto::claims::entitlement::Kind::GameService( + proto::claims::entitlement::GameService { + game_id: Some(game_id.into()), + } + )), + }]}, + )), + label: Some("game_service".to_owned()), + ..Default::default() + }) + .await?; + + let token = unwrap_ref!(token_res.token); + let token_session_id = unwrap_ref!(token_res.session_id).as_uuid(); + + sql_execute!( + [ctx] + "INSERT INTO db_cloud.service_cloud_tokens (game_id, token_session_id) VALUES ($1, $2)", + game_id, + token_session_id, + ) + .await?; + + Ok(cloud::game_token_create::Response { + token: token.token.clone(), + }) +} diff --git a/svc/pkg/cloud/ops/game-service-token-create/tests/integration.rs b/svc/pkg/cloud/ops/game-service-token-create/tests/integration.rs new file mode 100644 index 0000000000..860a6b67fc --- /dev/null +++ b/svc/pkg/cloud/ops/game-service-token-create/tests/integration.rs @@ -0,0 +1,16 @@ +use chirp_worker::prelude::*; + +#[worker_test] +async fn empty(ctx: TestCtx) { + let game_res = op!([ctx] faker_game { + ..Default::default() + }) + .await + .unwrap(); + + // op!([ctx] cloud_game_token_create { + // game_id: game_res.game_id, + // }) + // .await + // .unwrap(); +} diff --git a/svc/pkg/ds/db/servers/Service.toml b/svc/pkg/ds/db/servers/Service.toml new file mode 100644 index 0000000000..352986b38d --- /dev/null +++ b/svc/pkg/ds/db/servers/Service.toml @@ -0,0 +1,7 @@ +[service] +name = "db-dynamic-servers" + +[runtime] +kind = "crdb" + +[database] diff --git a/svc/pkg/ds/db/servers/migrations/20240501133910_init.down.sql b/svc/pkg/ds/db/servers/migrations/20240501133910_init.down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql b/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql new file mode 100644 index 0000000000..b74991e9a1 --- /dev/null +++ b/svc/pkg/ds/db/servers/migrations/20240501133910_init.up.sql @@ -0,0 +1,76 @@ +CREATE TABLE servers ( + server_id UUID PRIMARY KEY, + game_id UUID NOT NULL, + datacenter_id UUID NOT NULL, + -- The server will be locked to a certain cluster, but a game might change + -- clusters, and therefore the server will be moved to a new cluster. + cluster_id UUID NOT NULL, + -- This represents a map + tags JSONB NOT NULL, + resources_cpu_millicores INT NOT NULL, + resources_memory_mib INT NOT NULL, + kill_timeout_ms INT NOT NULL, + webhook_url STRING, + + create_ts INT NOT NULL, + stop_ts INT, + finish_ts INT, + cleanup_ts INT, + -- When the server was marked to be deleted by Rivet + destroy_ts INT, + + -- Docker + image_id UUID NOT NULL, + args STRING[], + network_mode INT NOT NULL, -- rivet.backend.ds.DockerNetworkMode + -- This is a map + environment JSONB NOT NULL, + + INDEX (game_id) +); + + +CREATE TABLE docker_ports_protocol_game_guard ( + server_id UUID NOT NULL REFERENCES servers, + port_name STRING NOT NULL, + port_number INT NOT NULL, + gg_port INT NOT NULL, + protocol INT NOT NULL, -- rivet.backend.ds.GameGuardProtocol + + PRIMARY KEY (server_id, port_name) +); + +CREATE TABLE docker_ports_host ( + server_id UUID NOT NULL REFERENCES servers, + port_name STRING NOT NULL, + port_number INT NOT NULL, + protocol INT NOT NULL, -- rivet.backend.ds.HostProtocol + + PRIMARY KEY (server_id, port_name) +); + +-- TODO make all nomad stucc clear +CREATE TABLE server_nomad ( + server_id UUID PRIMARY KEY REFERENCES servers, + nomad_dispatched_job_id STRING, + nomad_alloc_id STRING, + nomad_node_id STRING, + nomad_alloc_plan_ts INT, + nomad_alloc_state JSONB, + nomad_eval_plan_ts INT, + nomad_node_name STRING, + nomad_node_public_ipv4 STRING, + nomad_node_vlan_ipv4 STRING, + + INDEX (nomad_dispatched_job_id) +); + + +CREATE TABLE internal_ports ( + server_id UUID NOT NULL REFERENCES servers, + nomad_label STRING NOT NULL, + nomad_ip STRING NOT NULL, + nomad_source INT NOT NULL, + + PRIMARY KEY (server_id, nomad_label) +); diff --git a/svc/pkg/ds/ops/server-create/Cargo.toml b/svc/pkg/ds/ops/server-create/Cargo.toml new file mode 100644 index 0000000000..3922ea1da0 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "ds-server-create" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +chirp-client = { path = "../../../../../lib/chirp/client" } +chirp-worker = { path = "../../../../../lib/chirp/worker" } +rivet-operation = { path = "../../../../../lib/operation/core" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +lazy_static = "1.4.0" +uuid = { version = "1", features = ["v4", "serde"] } +http = "0.2" +bit-vec = "0.6" +cjson = "0.1" +nomad-util = { path = "../../../../../lib/nomad-util" } +strum = { version = "0.24", features = ["derive"] } +sha2 = "0.10" +hex = "0.4" +rivet-util = { path = "../../../../../lib/util/core" } +heck = "0.3" +s3-util = { path = "../../../../../lib/s3-util" } +util-build = { package = "rivet-util-build", path = "../../../build/util" } +util-ds = { package = "rivet-util-ds", path = "../../util" } +regex = "1.10" +rand = "0.8" +reqwest = "0.11" +rivet-api = { path = "../../../../../sdks/full/rust" } +tracing-subscriber = { version = "0.3", default-features = false, features = [ + "fmt", + "json", + "ansi", +] } + +mm-lobby-list-for-user-id = { path = "../../../mm/ops/lobby-list-for-user-id" } +build-get = { path = "../../../build/ops/get" } +user-identity-get = { path = "../../../user-identity/ops/get" } +upload-get = { path = "../../../upload/ops/get" } +region-get = { path = "../../../region/ops/get" } +ip-info = { path = "../../../ip/ops/info" } +tier-list = { path = "../../../tier/ops/list" } +token-create = { path = "../../../token/ops/create" } +cluster-datacenter-list = { path = "../../../cluster/ops/datacenter-list" } +cluster-list = { path = "../../../cluster/ops/list" } + +[dependencies.nomad_client] +package = "nomad_client" +git = "https://github.com/rivet-gg/nomad-client" +rev = "abb66bf0c30c7ff5b0c695dae952481c33e538b5" # pragma: allowlist secret + +[dependencies.sqlx] +version = "0.7" +default-features = false + +[dev-dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } +rivet-connection = { path = "../../../../../lib/connection" } + +faker-build = { path = "../../../faker/ops/build" } +faker-user = { path = "../../../faker/ops/user" } +faker-game = { path = "../../../faker/ops/game" } +faker-region = { path = "../../../faker/ops/region" } +game-get = { path = "../../../../pkg/game/ops/get" } + diff --git a/svc/pkg/ds/ops/server-create/Service.toml b/svc/pkg/ds/ops/server-create/Service.toml new file mode 100644 index 0000000000..a0260ec28c --- /dev/null +++ b/svc/pkg/ds/ops/server-create/Service.toml @@ -0,0 +1,10 @@ +[service] +name = "ds-server-create" + +[runtime] +kind = "rust" + +[operation] + +[databases] +db-dynamic-servers = {} diff --git a/svc/pkg/ds/ops/server-create/src/lib.rs b/svc/pkg/ds/ops/server-create/src/lib.rs new file mode 100644 index 0000000000..c4c41f0fdb --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/lib.rs @@ -0,0 +1,1517 @@ +// use chirp_worker::prelude::*; +use crate::sqlx; +use futures_util::FutureExt; +use nomad_client::models::*; +use nomad_job::{ + escape_go_template, gen_oci_bundle_config, inject_consul_env_template, nomad_host_port_env_var, + template_env_var, template_env_var_int, DecodedPort, ProxyProtocol, TransportProtocol, +}; +use proto::backend::{self}; +use proto::{backend::pkg::*, chirp::response::Ok}; +use rand::Rng; +use regex::Regex; +use rivet_operation::prelude::*; +use serde_json::json; +use sha2::{Digest, Sha256}; +use std::hash::Hasher; +use std::{collections::HashMap, hash::DefaultHasher, net::IpAddr, time::Duration}; +use team::member_get::request; + +mod nomad_job; +mod oci_config; +mod seccomp; +mod util_job; + +lazy_static::lazy_static! { + pub static ref NEW_NOMAD_CONFIG: nomad_client::apis::configuration::Configuration = + nomad_util::new_config_from_env().unwrap(); +} + +/// Choose which port to assign for a job's ingress port. +/// +/// If not provided by `ProxiedPort`, then: +/// - HTTP: 80 +/// - HTTPS: 443 +/// - TCP/TLS: random +/// - UDP: random +/// +/// This is somewhat poorly written for TCP & UDP ports and may bite us in the ass +/// some day. See https://linear.app/rivet-gg/issue/RIV-1799 +async fn choose_ingress_port( + ctx: OperationContext, + ingress_port: i32, + protocol: i32, +) -> GlobalResult { + use backend::job::ProxyProtocol; + + let ingress_port = match unwrap!(backend::job::ProxyProtocol::from_i32(protocol)) { + ProxyProtocol::Http => 80_i32, + ProxyProtocol::Https => 443, + ProxyProtocol::Tcp | ProxyProtocol::TcpTls => { + bind_with_retries( + ctx, + protocol, + util::net::job::MIN_INGRESS_PORT_TCP..=util::net::job::MAX_INGRESS_PORT_TCP, + ) + .await? + } + ProxyProtocol::Udp => { + bind_with_retries( + ctx, + protocol, + util::net::job::MIN_INGRESS_PORT_UDP..=util::net::job::MAX_INGRESS_PORT_UDP, + ) + .await? + } + }; + + Ok(ingress_port) +} + +async fn bind_with_retries( + ctx: OperationContext, + proxy_protocol: i32, + range: std::ops::RangeInclusive, +) -> GlobalResult { + let mut attempts = 3u32; + + // Try to bind to a random port, verifying that it is not already bound + loop { + if attempts == 0 { + bail!("failed all attempts to bind to unique port"); + } + attempts -= 1; + + let port = rand::thread_rng().gen_range(range.clone()) as i32; + + let (already_exists,) = sql_fetch_one!( + [ctx, (bool,)] + " + SELECT EXISTS( + SELECT 1 + FROM db_dynamic_servers.servers as r + JOIN db_dynamic_servers.docker_ports_protocol_game_guard as p + ON r.server_id = p.server_id + WHERE + r.cleanup_ts IS NULL AND + p.gg_port = $1 AND + p.protocol = $2 + ) + ", + port, + proxy_protocol, + ) + .await?; + + if !already_exists { + break Ok(port); + } + + tracing::info!(?port, ?attempts, "port collision, retrying"); + } +} + +#[operation(name = "ds-server-create")] +pub async fn handle( + ctx: OperationContext, +) -> GlobalResult { + let resources = unwrap_ref!(ctx.resources).clone(); + let server_id = Uuid::new_v4(); + let game_id = unwrap_ref!(ctx.game_id).as_uuid(); + let cluster_id = unwrap_ref!(ctx.cluster_id).as_uuid(); + let datacenter_id = unwrap_ref!(ctx.datacenter_id).as_uuid(); + + let create_ts = ctx.ts(); + + // MARK: db insert + + #[derive(Default, Clone)] + struct GameGuardUnnest { + port_names: Vec, + port_numbers: Vec>, + gg_ports: Vec>, + protocols: Vec, + } + + #[derive(Default, Clone)] + struct HostUnnest { + port_names: Vec, + port_numbers: Vec>, + } + + let mut game_guard_unnest = GameGuardUnnest::default(); + let mut host_unnest = HostUnnest::default(); + + for (name, port) in ctx.network_ports.iter() { + let routing = unwrap!(port.routing.clone()); + match routing { + dynamic_servers::server_create::port::Routing::GameGuard(gameguard_protocol) => { + game_guard_unnest.port_names.push(name.clone()); + game_guard_unnest.port_numbers.push(port.internal_port); + game_guard_unnest.gg_ports.push(match port.internal_port { + Some(port) => Some( + choose_ingress_port(ctx.clone(), port, gameguard_protocol.protocol).await?, + ), + None => None, + }); + game_guard_unnest + .protocols + .push(gameguard_protocol.protocol); + } + dynamic_servers::server_create::port::Routing::Host(_) => { + host_unnest.port_names.push(name.clone()); + host_unnest.port_numbers.push(port.internal_port); + } + }; + } + + rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { + let ctx = ctx.clone(); + let host_unnest = host_unnest.clone(); + let game_guard_unnest = game_guard_unnest.clone(); + + async move { + sql_execute!( + [ctx, @tx tx] + " + WITH + servers_cte AS ( + INSERT INTO + db_dynamic_servers.servers ( + server_id, + game_id, + datacenter_id, + cluster_id, + tags, + resources_cpu_millicores, + resources_memory_mib, + kill_timeout_ms, + webhook_url, + create_ts, + image_id, + args, + network_mode, + environment + ) + VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) + RETURNING + 1 + ), + docker_ports_host_cte AS ( + INSERT INTO + db_dynamic_servers.docker_ports_host ( + server_id, + port_name, + port_number + ) + SELECT + $1, + t.* + FROM + unnest($15, $16) AS t (port_name, port_number) + RETURNING + 1 + ), + docker_ports_protocol_game_guard_cte AS ( + INSERT INTO + db_dynamic_servers.docker_ports_protocol_game_guard ( + server_id, + port_name, + port_number, + gg_port, + protocol + ) + SELECT + $1, + t.* + FROM + unnest($17, $18, $19, $20) AS t (port_name, port_number, protocol) + RETURNING + 1 + ) + SELECT + 1 + ", + server_id, + game_id, + datacenter_id, + cluster_id, + serde_json::value::to_raw_value(&ctx.tags.to_owned())?.to_string(), // 5 + resources.cpu_millicores, + resources.memory_mib, + ctx.kill_timeout_ms, + ctx.webhook_url.clone(), + create_ts, // 10 + unwrap!(ctx.image_id).as_uuid(), + &ctx.args, + ctx.network_mode, + serde_json::value::to_raw_value(&ctx.environment)?.to_string(), + host_unnest.port_names, // 15 + host_unnest.port_numbers, + game_guard_unnest.port_names, + game_guard_unnest.port_numbers, + game_guard_unnest.gg_ports, + game_guard_unnest.protocols, // 20 + ) + .await + } + .boxed() + }) + .await?; + + // let ( + // (mm_game_config, namespace), + // mm_ns_config, + // (lobby_group, lobby_group_meta, version_id), + // region, + // tiers, + // ) = tokio::try_join!( + // fetch_namespace(ctx, namespace_id), + // fetch_mm_namespace_config(ctx, namespace_id), + // fetch_lobby_group_config(ctx, lobby_group_id), + // fetch_region(ctx, region_id), + // fetch_tiers(ctx, region_id), + // )?; + // let (mm_game_config, namespace) = fetch_namespace(ctx, namespace_id).await?; + // let mm_ns_config = fetch_mm_namespace_config(ctx, namespace_id).await?; + // let (lobby_group, lobby_group_meta, version_id) = fetch_lobby_group_config(ctx, lobby_group_id) + // .await?; + // let region = fetch_region(ctx, region_id).await?; + // let tiers = fetch_tiers(ctx, region_id).await?; + // let version = fetch_version(ctx, version_id).await?; + + // // Do all nomad stuff + // let namespace_id = unwrap_ref!(namespace.namespace_id).as_uuid(); + // let version_id = unwrap_ref!(version.version_id).as_uuid(); + // let lobby_group_id = unwrap_ref!(lobby_group_meta.lobby_group_id).as_uuid(); + // let region_id = unwrap_ref!(region.region_id).as_uuid(); + + // let job_runner_binary_url = resolve_job_runner_binary_url(ctx).await?; + + // let resolve_perf = ctx.perf().start("resolve-image-artifact-url").await; + // let build_id = unwrap_ref!(runtime.build_id).as_uuid(); + // let image_artifact_url = resolve_image_artifact_url(ctx, build_id, region).await?; + // resolve_perf.end(); + + // // Validate build exists and belongs to this game + // let build_id = unwrap_ref!(runtime.build_id).as_uuid(); + // let build_get = op!([ctx] build_get { + // build_ids: vec![build_id.into()], + // }) + // .await?; + // let build = unwrap!(build_get.builds.first()); + // let build_kind = unwrap!(backend::build::BuildKind::from_i32(build.kind)); + // let build_compression = unwrap!(backend::build::BuildCompression::from_i32( + // build.compression + // )); + + let ctx: OperationContext = ctx; + + // Generate the Docker job + + // let runtime = backend::ds::lobby_runtime::Docker { + // build_id: todo!(), + // args: docker_runtime.args, + // env_vars: todo!(), + // network_mode: todo!(), + // ports: todo!(), + // }; + // let _image_tag = &build.image_tag; + // let tier = backend::region::Tier { + // tier_name_id: todo!(), + // rivet_cores_numerator: todo!(), + // rivet_cores_denominator: todo!(), + // cpu: todo!(), + // memory: todo!(), + // memory_max: todo!(), + // disk: todo!(), + // bandwidth: todo!(), + // }; + + // let lobby_config = ctx.lobby_config_json.is_some(); + // let lobby_tags = !ctx.tags.is_empty(); + // let build_kind = backend::build::BuildKind::DockerImage; + // let build_compression = backend::build::BuildCompression::None; + + // IMPORTANT: This job spec must be deterministic. Do not pass in parameters + // that change with every run, such as the lobby ID. Ensure the + // `reuse_job_id` test passes when changing this function. + use nomad_client::models::*; + + let resources = unwrap!(ctx.resources.clone()); + + let tier_res = op!([ctx] tier_list { + region_ids: vec![datacenter_id.into()], + }) + .await?; + let tier_region = unwrap!(tier_res.regions.first()); + + // // runc-compatible resourcesd + // let cpu = resources.cpu_millicores; // Millicore (1/1000 of a core) + // let memory = resources.memory_mib * (1024 * 1024); // bytes + // // let memory_max = tier.memory_max * (1024 * 1024); // bytes + + // Find the first tier that has more CPU and memory than the requested + // resources + let mut tiers = tier_region.tiers.clone(); + + // Sort the tiers by cpu + tiers.sort_by(|a, b| a.cpu.cmp(&b.cpu)); + let tier = unwrap!(tiers.iter().find(|t| { + t.cpu as i32 >= resources.cpu_millicores && t.memory as i32 >= resources.memory_mib + })); + + // runc-compatible resources + let cpu = tier.rivet_cores_numerator as u64 * 1_000 / tier.rivet_cores_denominator as u64; // Millicore (1/1000 of a core) + let memory = tier.memory * (1024 * 1024); // bytes + let memory_max = tier.memory_max * (1024 * 1024); // bytes + + // dbg!(tier, cpu, memory, memory_max); + // panic!(); + + // Validate build exists and belongs to this game + let build_id = unwrap_ref!(ctx.image_id).as_uuid(); + let build_get = op!([ctx] build_get { + build_ids: vec![build_id.into()], + }) + .await?; + let build = unwrap!(build_get.builds.first()); + let build_kind = unwrap!(backend::build::BuildKind::from_i32(build.kind)); + let build_compression = unwrap!(backend::build::BuildCompression::from_i32( + build.compression + )); + + // // Nomad-compatible resources + // let resources = Resources { + // // TODO: Configure this per-provider + // // Nomad configures CPU based on MHz, not millicores. We havel to calculate the CPU share + // // by knowing how many MHz are on the client. + // CPU: if cpu < 1000 { + // Some((cpu - util_job::TASK_CLEANUP_CPU).try_into()?) + // } else { + // None + // }, + // cores: if cpu >= 1000 { + // Some((cpu / 1000) as i32) + // } else { + // None + // }, + // memory_mb: Some( + // (TryInto::::try_into(memory)? / (1024 * 1024) + // - util_job::TASK_CLEANUP_MEMORY as i64) + // .try_into()?, + // ), + // // Allow oversubscribing memory by 50% of the reserved + // // memory if using less than the node's total memory + // memory_max_mb: Some( + // (TryInto::::try_into((memory as f64 * 1.5) as i64)? / (1024 * 1024) + // - util_job::TASK_CLEANUP_MEMORY as i64) + // .try_into()?, + // ), + // ..Resources::new() + // }; + + // Nomad-compatible resources + let nomad_resources = Resources { + // TODO: Configure this per-provider + // Nomad configures CPU based on MHz, not millicores. We havel to calculate the CPU share + // by knowing how many MHz are on the client. + CPU: if tier.rivet_cores_numerator < tier.rivet_cores_denominator { + Some((tier.cpu - util_job::TASK_CLEANUP_CPU as u64).try_into()?) + } else { + None + }, + cores: if tier.rivet_cores_numerator >= tier.rivet_cores_denominator { + Some((tier.rivet_cores_numerator / tier.rivet_cores_denominator) as i32) + } else { + None + }, + memory_mb: Some( + (TryInto::::try_into(memory)? / (1024 * 1024) + - util_job::TASK_CLEANUP_MEMORY as i64) + .try_into()?, + ), + // Allow oversubscribing memory by 50% of the reserved + // memory if using less than the node's total memory + memory_max_mb: None, + // Some( + // (TryInto::::try_into(memory_max)? / (1024 * 1024) + // - util_job::TASK_CLEANUP_MEMORY as i64) + // .try_into()?, + // ), + disk_mb: Some(tier.disk as i32), // TODO: Is this deprecated? + ..Resources::new() + }; + + // // let network_mode = unwrap!(LobbyRuntimeNetworkMode::from_i32(runtime.network_mode)); + + // Read ports + let decoded_ports = ctx + .network_ports + .clone() + .into_iter() + .map(|(port_label, port)| match port.routing { + Some(dynamic_servers::server_create::port::Routing::GameGuard(game_guard_routing)) => { + let target = unwrap!(port.internal_port) as u16; + + GlobalResult::Ok(DecodedPort { + label: port_label.clone(), + nomad_port_label: util_ds::format_nomad_port_label(&port_label), + target, + proxy_protocol: unwrap!(backend::ds::GameGuardProtocol::from_i32( + game_guard_routing.protocol + )) + .into(), + }) + } + Some(dynamic_servers::server_create::port::Routing::Host(_)) => { + todo!() + } + None => { + todo!() + } + }) + .collect::>>()?; + + // The container will set up port forwarding manually from the Nomad-defined ports on the host + // to the CNI container + let dynamic_ports = decoded_ports + .iter() + .map(|port| Port { + label: Some(port.nomad_port_label.clone()), + ..Port::new() + }) + .collect::>(); + + // Port mappings to pass to the container. Only used in bridge networking. + let cni_port_mappings = decoded_ports + .clone() + .into_iter() + .map(|port| { + json!({ + "HostPort": template_env_var_int(&nomad_host_port_env_var(&port.nomad_port_label)), + "ContainerPort": port.target, + "Protocol": TransportProtocol::from(port.proxy_protocol).as_cni_protocol(), + }) + }) + .collect::>(); + + let prepared_ports = ctx.network_ports.iter().map(|(label, port)| { + let mode = unwrap!(backend::ds::NetworkMode::from_i32(ctx.network_mode)); + let port_value = match mode { + // CNI will handle mapping the host port to the container port + backend::ds::NetworkMode::Bridge => unwrap!(port.internal_port).to_string(), + // The container needs to listen on the correct port + backend::ds::NetworkMode::Host => template_env_var(&nomad_host_port_env_var(&label)), + }; + + GlobalResult::Ok(Some(String::new())) + // TODO + // Port with the kebab case port key. Included for backward compatabiilty & for + // less confusion. + // Ok((format!("PORT_{}", port.label.replace('-', "_")), port_value)) + }); + + // Also see util_ds:consts::DEFAULT_ENV_KEYS + let mut env = Vec::<(String, String)>::new() + .into_iter() + // TODO + // .chain(if lobby_config { + // Some(( + // "RIVET_LOBBY_CONFIG".to_string(), + // template_env_var("NOMAD_META_LOBBY_CONFIG"), + // )) + // } else { + // None + // }) + // .chain(if lobby_tags { + // Some(( + // "RIVET_LOBBY_TAGS".to_string(), + // template_env_var("NOMAD_META_LOBBY_TAGS"), + // )) + // } else { + // None + // }) + .chain([( + "RIVET_API_ENDPOINT".to_string(), + util::env::origin_api().to_string(), + )]) + // Ports + // TODO + // .chain(prepared_ports) + // // Port ranges + // .chain( + // decoded_ports + // .iter() + // .filter_map(|port| { + // if let PortTarget::Range { min, max } = &port.target { + // let snake_port_label = port.label.replace('-', "_"); + // Some([ + // ( + // format!("PORT_RANGE_MIN_{}", snake_port_label), + // min.to_string(), + // ), + // ( + // format!("PORT_RANGE_MAX_{}", snake_port_label), + // max.to_string(), + // ), + // ]) + // } else { + // None + // } + // }) + // .flatten(), + // ) + .map(|(k, v)| format!("{k}={v}")) + .collect::>(); + env.sort(); + + let services = decoded_ports + .iter() + .map(|port| { + let service_name = format!("${{NOMAD_META_LOBBY_ID}}-{}", port.label); + GlobalResult::Ok(Some(Service { + provider: Some("nomad".into()), + name: Some(service_name), + tags: Some(vec!["game".into()]), + port_label: Some(port.nomad_port_label.clone()), + // checks: if TransportProtocol::from(port.proxy_protocol) + // == TransportProtocol::Tcp + // { + // Some(vec![ServiceCheck { + // name: Some(format!("{}-probe", port.label)), + // port_label: Some(port.nomad_port_label.clone()), + // _type: Some("tcp".into()), + // interval: Some(30_000_000_000), + // timeout: Some(2_000_000_000), + // ..ServiceCheck::new() + // }]) + // } else { + // None + // }, + ..Service::new() + })) + }) + .filter_map(|x| x.transpose()) + .collect::>>()?; + + // Generate the command to download and decompress the file + let mut download_cmd = r#"curl -Lf "$NOMAD_META_IMAGE_ARTIFACT_URL""#.to_string(); + match build_compression { + backend::build::BuildCompression::None => {} + backend::build::BuildCompression::Lz4 => { + download_cmd.push_str(" | lz4 -d -"); + } + } + + // MARK: Job spec + + let job_spec = Job { + _type: Some("batch".into()), + // constraints: Some(vec![Constraint { + // l_target: Some("${node.class}".into()), + // r_target: Some("job".into()), + // operand: Some("=".into()), + // }]), + parameterized_job: Some(Box::new(ParameterizedJobConfig { + payload: Some("forbidden".into()), + meta_required: Some(vec![ + "job_runner_binary_url".into(), + "vector_socket_addr".into(), + "image_artifact_url".into(), + "root_user_enabled".into(), + "user_env".into(), + ]), + meta_optional: Some(vec!["rivet_test_id".into()]), + })), + task_groups: Some(vec![TaskGroup { + name: Some(util_job::RUN_MAIN_TASK_NAME.into()), + constraints: None, // TODO: Use parameter meta to specify the hardware + affinities: None, // TODO: + // Allows for jobs to keep running and receiving players in the + // event of a disconnection from the Nomad server. + max_client_disconnect: Some(5 * 60 * 1_000_000_000), + restart_policy: Some(Box::new(RestartPolicy { + attempts: Some(0), + mode: Some("fail".into()), + ..RestartPolicy::new() + })), + reschedule_policy: Some(Box::new(ReschedulePolicy { + attempts: Some(0), + unlimited: Some(false), + ..ReschedulePolicy::new() + })), + networks: Some(vec![NetworkResource { + // The setup.sh script will set up a CNI network if using bridge networking + mode: Some("host".into()), + dynamic_ports: Some(dynamic_ports.clone()), + ..NetworkResource::new() + }]), + services: Some(services), + // Configure ephemeral disk for logs + ephemeral_disk: Some(Box::new(EphemeralDisk { + size_mb: Some(tier.disk as i32), + ..EphemeralDisk::new() + })), + tasks: Some(vec![ + // TODO + Task { + name: Some("runc-setup".into()), + lifecycle: Some(Box::new(TaskLifecycle { + hook: Some("prestart".into()), + sidecar: Some(false), + })), + driver: Some("raw_exec".into()), + config: Some({ + let mut x = HashMap::new(); + x.insert("command".into(), json!("${NOMAD_TASK_DIR}/setup.sh")); + x + }), + templates: Some(vec![ + Template { + embedded_tmpl: Some(include_str!("./scripts/setup.sh").replace( + "__HOST_NETWORK__", + match unwrap!(backend::ds::NetworkMode::from_i32(ctx.network_mode)) + { + backend::ds::NetworkMode::Bridge => "false", + backend::ds::NetworkMode::Host => "true", + }, + )), + dest_path: Some("${NOMAD_TASK_DIR}/setup.sh".into()), + perms: Some("744".into()), + ..Template::new() + }, + Template { + embedded_tmpl: Some( + include_str!("./scripts/setup_job_runner.sh").into(), + ), + dest_path: Some("${NOMAD_TASK_DIR}/setup_job_runner.sh".into()), + perms: Some("744".into()), + ..Template::new() + }, + Template { + embedded_tmpl: Some( + include_str!("./scripts/setup_oci_bundle.sh") + .replace("__DOWNLOAD_CMD__", &download_cmd) + .replace( + "__BUILD_KIND__", + match build_kind { + backend::build::BuildKind::DockerImage => { + "docker-image" + } + backend::build::BuildKind::OciBundle => "oci-bundle", + }, + ), + ), + dest_path: Some("${NOMAD_TASK_DIR}/setup_oci_bundle.sh".into()), + perms: Some("744".into()), + ..Template::new() + }, + Template { + embedded_tmpl: Some( + include_str!("./scripts/setup_cni_network.sh").into(), + ), + dest_path: Some("${NOMAD_TASK_DIR}/setup_cni_network.sh".into()), + perms: Some("744".into()), + ..Template::new() + }, + Template { + embedded_tmpl: Some(gen_oci_bundle_config( + cpu, memory, memory_max, env, + )?), + dest_path: Some( + "${NOMAD_ALLOC_DIR}/oci-bundle-config.base.json".into(), + ), + ..Template::new() + }, + Template { + embedded_tmpl: Some(inject_consul_env_template( + &serde_json::to_string(&cni_port_mappings)?, + )?), + dest_path: Some("${NOMAD_ALLOC_DIR}/cni-port-mappings.json".into()), + ..Template::new() + }, + ]), + resources: Some(Box::new(Resources { + CPU: Some(util_ds::RUNC_SETUP_CPU), + memory_mb: Some(util_ds::RUNC_SETUP_MEMORY), + ..Resources::new() + })), + log_config: Some(Box::new(LogConfig { + max_files: Some(4), + max_file_size_mb: Some(2), + disabled: None, + })), + ..Task::new() + }, + // TODO + Task { + name: Some(util_job::RUN_MAIN_TASK_NAME.into()), + driver: Some("raw_exec".into()), + config: Some({ + let mut x = HashMap::new(); + // This is downloaded in setup_job_runner.sh + x.insert("command".into(), json!("${NOMAD_ALLOC_DIR}/job-runner")); + x + }), + resources: Some(Box::new(nomad_resources.clone())), + // Intentionally high timeout. Killing jobs is handled manually with signals. + kill_timeout: Some(86400 * 1_000_000_000), + kill_signal: Some("SIGTERM".into()), + log_config: Some(Box::new(LogConfig { + max_files: Some(4), + max_file_size_mb: Some(4), + disabled: None, + })), + ..Task::new() + }, + // TODO: Remove + // Task { + // name: Some("runc-cleanup".into()), + // lifecycle: Some(Box::new(TaskLifecycle { + // hook: Some("poststop".into()), + // sidecar: Some(false), + // })), + // driver: Some("raw_exec".into()), + // config: Some({ + // let mut x = HashMap::new(); + // x.insert("command".into(), json!("${NOMAD_TASK_DIR}/cleanup.sh")); + // x + // }), + // templates: Some(vec![Template { + // embedded_tmpl: Some(include_str!("./scripts/cleanup.sh").into()), + // dest_path: Some("${NOMAD_TASK_DIR}/cleanup.sh".into()), + // perms: Some("744".into()), + // ..Template::new() + // }]), + // resources: Some(Box::new(Resources { + // CPU: Some(util_mm::RUNC_CLEANUP_CPU), + // memory_mb: Some(util_mm::RUNC_CLEANUP_MEMORY), + // ..Resources::new() + // })), + // log_config: Some(Box::new(LogConfig { + // max_files: Some(4), + // max_file_size_mb: Some(2), + // })), + // ..Task::new() + // }, + ]), + ..TaskGroup::new() + }]), + ..Job::new() + }; + + let job_spec_json = serde_json::to_string(&job_spec)?; + + // // Build proxied ports for each exposed port + // let proxied_ports = runtime + // .ports + // .iter() + // .filter(|port| { + // port.proxy_kind == backend::ds::lobby_runtime::ProxyKind::GameGuard as i32 + // && port.port_range.is_none() + // }) + // .flat_map(|port| { + // let mut ports = vec![direct_proxied_port(lobby_id, region_id, port)]; + // match backend::ds::lobby_runtime::ProxyProtocol::from_i32( + // port.proxy_protocol, + // ) { + // Some( + // backend::ds::lobby_runtime::ProxyProtocol::Http + // | backend::ds::lobby_runtime::ProxyProtocol::Https, + // ) => { + // ports.push(path_proxied_port(lobby_id, region_id, port)); + // } + // Some( + // backend::ds::lobby_runtime::ProxyProtocol::Udp + // | backend::ds::lobby_runtime::ProxyProtocol::Tcp + // | backend::ds::lobby_runtime::ProxyProtocol::TcpTls, + // ) + // | None => {} + // } + // ports + // }) + // .collect::>>()?; + + // submit_job(&job_spec_json, Some(region_id.into())); + + // Get the region to dispatch in + let region_res = op!([ctx] region_get { + region_ids: vec![datacenter_id.into()], + }) + .await?; + let region = unwrap!(region_res.regions.first()); + + // let region = region; + let base_job: Job = serde_json::from_str::(&job_spec_json)?; + + // Modify the job spec + let mut job = base_job; + // let region = region; + // Replace all job IDs with a placeholder value in order to create a + // deterministic job spec. + { + let job_id: &str = "__PLACEHOLDER__"; + let job: &mut nomad_client::models::Job = &mut job; + job.ID = Some(job_id.into()); + job.name = Some(job_id.into()); + }; + + ensure_eq!( + "batch", + unwrap_ref!(job._type).as_str(), + "only the batch job type is supported" + ); + + // Update the job's region + job.region = Some(region.nomad_region.clone()); + job.datacenters = Some(vec![region.nomad_datacenter.clone()]); + + // Validate that the job is parameterized + // TODO: clean up how stuff is put in here + let parameters = unwrap!(job.parameterized_job.as_mut(), "job not parameterized"); + + // Add run parameters + parameters.meta_required = Some({ + let mut meta_required = parameters.meta_required.clone().unwrap_or_default(); + meta_required.push("job_run_id".into()); + meta_required + }); + + // Get task group + let task_groups = unwrap!(job.task_groups.as_mut()); + ensure_eq!(1, task_groups.len(), "must have exactly 1 task group"); + let task_group = unwrap!(task_groups.first_mut()); + ensure_eq!( + task_group.name.as_deref(), + Some(RUN_MAIN_TASK_NAME), + "must have main task group" + ); + + // Ensure has main task + let main_task = unwrap!( + task_group + .tasks + .iter_mut() + .flatten() + .find(|x| x.name.as_deref() == Some(RUN_MAIN_TASK_NAME)), + "must have main task" + ); + ensure!( + main_task + .lifecycle + .as_ref() + .map_or(true, |x| x.hook.is_none()), + "main task must not have a lifecycle hook" + ); + + // Configure networks + let networks = unwrap!(task_group.networks.as_mut()); + ensure_eq!(1, networks.len(), "must have exactly 1 network"); + let network = unwrap!(networks.first_mut()); + // Disable IPv6 DNS since Docker doesn't support IPv6 yet + network.DNS = Some(Box::new(nomad_client::models::DnsConfig { + servers: Some(vec![ + // Google + "8.8.8.8".into(), + "8.8.4.4".into(), + "2001:4860:4860::8888".into(), + "2001:4860:4860::8844".into(), + ]), + // Disable default search from the host + searches: Some(Vec::new()), + options: Some(vec!["rotate".into(), "edns0".into(), "attempts:2".into()]), + ..nomad_client::models::DnsConfig::new() + })); + + // Disable rescheduling, since job-run doesn't support this at the moment + task_group.reschedule_policy = Some(Box::new(nomad_client::models::ReschedulePolicy { + attempts: Some(0), + unlimited: Some(false), + ..nomad_client::models::ReschedulePolicy::new() + })); + + // Disable restarts. Our Nomad monitoring workflow doesn't support restarts + // at the moment. + task_group.restart_policy = Some(Box::new(nomad_client::models::RestartPolicy { + attempts: Some(0), + // unlimited: Some(false), + ..nomad_client::models::RestartPolicy::new() + })); + + // MARK: Cleanup task + + // Add cleanup task + let tasks: &mut Vec = unwrap!(task_group.tasks.as_mut()); + tasks.push({ + Task { + name: Some(RUN_CLEANUP_TASK_NAME.into()), + lifecycle: Some(Box::new(TaskLifecycle { + hook: Some("poststop".into()), + sidecar: Some(false), + })), + driver: Some("docker".into()), + config: Some({ + let mut config = HashMap::new(); + + config.insert("image".into(), json!("python:3.10.7-alpine3.16")); + config.insert( + "args".into(), + json!([ + "/bin/sh", + "-c", + "apk add --no-cache ca-certificates && python3 /local/cleanup.py" + ]), + ); + + config + }), + templates: Some(vec![Template { + dest_path: Some("local/cleanup.py".into()), + embedded_tmpl: Some(formatdoc!( + r#" + import ssl + import urllib.request, json, os, mimetypes, sys + + BEARER = '{{{{env "NOMAD_META_JOB_RUN_TOKEN"}}}}' + + ctx = ssl.create_default_context() + + def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + def req(method, url, data = None, headers = {{}}): + request = urllib.request.Request( + url=url, + data=data, + method=method, + headers=headers + ) + + try: + res = urllib.request.urlopen(request, context=ctx) + assert res.status == 200, f"Received non-200 status: {{res.status}}" + return res + except urllib.error.HTTPError as err: + eprint(f"HTTP Error ({{err.code}} {{err.reason}}):\n\nBODY:\n{{err.read().decode()}}\n\nHEADERS:\n{{err.headers}}") + + raise err + + print(f'\n> Cleaning up job') + + res_json = None + with req('POST', f'{origin_api}/job/runs/cleanup', + data = json.dumps({{}}).encode(), + headers = {{ + 'Authorization': f'Bearer {{BEARER}}', + 'Content-Type': 'application/json' + }} + ) as res: + res_json = json.load(res) + + + print('\n> Finished') + "#, + origin_api = util::env::origin_api(), + )), + ..Template::new() + }]), + resources: Some(Box::new(Resources { + CPU: Some(TASK_CLEANUP_CPU), + memory_mb: Some(TASK_CLEANUP_MEMORY), + ..Resources::new() + })), + log_config: Some(Box::new(LogConfig { + max_files: Some(4), + max_file_size_mb: Some(2), + disabled: Some(false), + })), + ..Task::new() + } + }); + + // Derive jobspec hash + // + // We serialize the JSON to a canonical string then take a SHA hash of the output. + let job_cjson_str = match cjson::to_string(&job) { + Ok(x) => x, + Err(err) => { + tracing::error!(?err, "cjson serialization failed"); + bail!("cjson serialization failed") + } + }; + let job_hash = Sha256::digest(job_cjson_str.as_bytes()); + let job_hash_str = hex::encode(job_hash); + + // Generate new job ID + let job_id = format!( + "job-{hash}:{region}", + hash = &job_hash_str[0..12], + region = region.name_id + ); + { + let job_id: &str = &job_id; + let job: &mut nomad_client::models::Job = &mut job; + job.ID = Some(job_id.into()); + job.name = Some(job_id.into()); + }; + + // Submit the job + tracing::info!("submitting job"); + + // dbg!( + // // &NEW_NOMAD_CONFIG, + // &job_id, + // nomad_client::models::JobRegisterRequest { + // job: Some(Box::new(job.clone())), + // ..nomad_client::models::JobRegisterRequest::new() + // }, + // Some(®ion.nomad_region), + // ); + // panic!(); + + // pub struct Configuration { + // pub base_path: String, + // pub user_agent: Option, + // pub client: reqwest::Client, + // pub basic_auth: Option, + // pub oauth_access_token: Option, + // pub bearer_access_token: Option, + // pub api_key: Option, + // // TODO: take an oauth2 token source, similar to the go one + // } + + // dbg!( + // &NEW_NOMAD_CONFIG.base_path, + // &NEW_NOMAD_CONFIG.user_agent, + // &NEW_NOMAD_CONFIG.client, + // &NEW_NOMAD_CONFIG.basic_auth, + // &NEW_NOMAD_CONFIG.oauth_access_token, + // &NEW_NOMAD_CONFIG.bearer_access_token, + // &NEW_NOMAD_CONFIG.api_key, + // ); + // panic!(); + + let a = nomad_client::apis::jobs_api::post_job( + &NEW_NOMAD_CONFIG, + &job_id, + nomad_client::models::JobRegisterRequest { + job: Some(Box::new(job)), + ..nomad_client::models::JobRegisterRequest::new() + }, + Some(®ion.nomad_region), + None, + None, + None, + ) + .await?; + dbg!(a); + + // let build_res = op!([ctx] build_get { + // build_ids: vec![build_id.into()], + // }) + // .await?; + // let build = build_res.builds.first(); + // let build = unwrap_ref!(build); + // let build_kind = unwrap!(backend::build::BuildKind::from_i32(build.kind)); + // let build_compression = unwrap!(backend::build::BuildCompression::from_i32( + // build.compression + // )); + let upload_id_proto = unwrap!(build.upload_id); + + let upload_res = op!([ctx] upload_get { + upload_ids: vec![upload_id_proto], + }) + .await?; + let upload = unwrap!(upload_res.uploads.first()); + + // Get provider + let proto_provider = unwrap!( + backend::upload::Provider::from_i32(upload.provider), + "invalid upload provider" + ); + let provider = match proto_provider { + backend::upload::Provider::Minio => s3_util::Provider::Minio, + backend::upload::Provider::Backblaze => s3_util::Provider::Backblaze, + backend::upload::Provider::Aws => s3_util::Provider::Aws, + }; + + let file_name = util_build::file_name(build_kind, build_compression); + + let mm_lobby_delivery_method = unwrap!( + backend::cluster::BuildDeliveryMethod::from_i32(region.build_delivery_method), + "invalid datacenter build delivery method" + ); + let image_artifact_url = match mm_lobby_delivery_method { + backend::cluster::BuildDeliveryMethod::S3Direct => { + tracing::info!("using s3 direct delivery"); + + let bucket = "bucket-build"; + + // Build client + let s3_client = + s3_util::Client::from_env_opt(bucket, provider, s3_util::EndpointKind::External) + .await?; + + let upload_id = unwrap_ref!(upload.upload_id).as_uuid(); + let presigned_req = s3_client + .get_object() + .bucket(s3_client.bucket()) + .key(format!("{upload_id}/{file_name}")) + .presigned( + s3_util::aws_sdk_s3::presigning::config::PresigningConfig::builder() + .expires_in(std::time::Duration::from_secs(15 * 60)) + .build()?, + ) + .await?; + + let addr = presigned_req.uri().clone(); + + let addr_str = addr.to_string(); + tracing::info!(addr = %addr_str, "resolved artifact s3 presigned request"); + + addr_str + } + backend::cluster::BuildDeliveryMethod::TrafficServer => { + tracing::info!("using traffic server delivery"); + + let region_id = unwrap_ref!(region.region_id).as_uuid(); + + // Hash build so that the ATS server that we download the build from is always the same one. This + // improves cache hit rates and reduces download times. + let build_id = unwrap_ref!(build.build_id).as_uuid(); + let mut hasher = DefaultHasher::new(); + hasher.write(build_id.as_bytes()); + let hash = hasher.finish() as i64; + + // NOTE: The algorithm for choosing the vlan_ip from the hash should match the one in + // prewarm_ats.rs @ prewarm_ats_cache + // Get vlan ip from build id hash for consistent routing + let (ats_vlan_ip,) = sql_fetch_one!( + [ctx, (IpAddr,)] + " + WITH sel AS ( + -- Select candidate vlan ips + SELECT + vlan_ip + FROM db_cluster.servers + WHERE + datacenter_id = $1 AND + pool_type = $2 AND + vlan_ip IS NOT NULL AND + install_complete_ts IS NOT NULL AND + drain_ts IS NULL AND + cloud_destroy_ts IS NULL + ) + SELECT vlan_ip + FROM sel + -- Use mod to make sure the hash stays within bounds + OFFSET abs($3 % GREATEST((SELECT COUNT(*) FROM sel), 1)) + LIMIT 1 + ", + // NOTE: region_id is just the old name for datacenter_id + ®ion_id, + backend::cluster::PoolType::Ats as i64, + hash, + ) + .await?; + + let upload_id = unwrap_ref!(upload.upload_id).as_uuid(); + let addr = format!( + "http://{vlan_ip}:8080/s3-cache/{provider}/{namespace}-bucket-build/{upload_id}/{file_name}", + vlan_ip = ats_vlan_ip, + provider = heck::KebabCase::to_kebab_case(provider.as_str()), + namespace = util::env::namespace(), + upload_id = upload_id, + ); + + tracing::info!(%addr, "resolved artifact s3 url"); + + addr + } + }; + + let job_runner_binary_url = resolve_job_runner_binary_url(&ctx, region).await?; + + // MARK: Parameters + + let parameters: Vec = vec![ + backend::job::Parameter { + key: "job_runner_binary_url".into(), + value: job_runner_binary_url, + }, + backend::job::Parameter { + key: "vector_socket_addr".into(), + value: "127.0.0.1:5021".to_string(), + }, + backend::job::Parameter { + key: "image_artifact_url".into(), + value: image_artifact_url.to_string(), + }, + backend::job::Parameter { + key: "root_user_enabled".into(), + // TODO make table dynamic host, make reference so that we can find + // other locations + value: "0".into(), + }, + backend::job::Parameter { + key: "user_env".into(), + // other locations + value: unwrap!(serde_json::to_string( + &ctx.environment + .iter() + .map(|(k, v)| (k.clone(), escape_go_template(v))) + .collect::>(), + )), + }, + ] + .into_iter() + // .chain(ctx.parameters.clone()) + // .chain(port_parameters) + .collect(); + + let job_params: Vec<(String, String)> = vec![("job_run_id".into(), server_id.to_string())]; + + // MARK: Insert into db + sql_execute!( + [ctx] + " + INSERT INTO + db_dynamic_servers.server_nomad (server_id) + VALUES + ($1) + ", + server_id, + ) + .await?; + + // MARK: Dispatch + let dispatch_res = nomad_client::apis::jobs_api::post_job_dispatch( + &NEW_NOMAD_CONFIG, + &job_id, + nomad_client::models::JobDispatchRequest { + job_id: Some(job_id.to_string()), + payload: None, + meta: Some( + parameters + .iter() + .map(|p| (p.key.clone(), p.value.clone())) + .chain(job_params.into_iter()) + .collect::>(), + ), + }, + Some(®ion.nomad_region), + None, + None, + None, + ) + .await; + let nomad_dispatched_job_id: Option = match dispatch_res { + Ok(dispatch_res) => { + // We will use the dispatched job ID to identify this allocation for the future. We can't use + // eval ID, since that changes if we mutate the allocation (i.e. try to stop it). + let nomad_dispatched_job_id = unwrap_ref!(dispatch_res.dispatched_job_id); + GlobalResult::Ok(Some(nomad_dispatched_job_id.clone())) + } + Err(err) => { + tracing::error!(?err, "failed to dispatch job"); + Ok(None) + } + }?; + + // MARK: Write to db after run + sql_execute!( + [ctx] + " + UPDATE + db_dynamic_servers.server_nomad + SET + nomad_dispatched_job_id = $2 + WHERE + server_id = $1 + ", + server_id, + unwrap!(nomad_dispatched_job_id), + ) + .await?; + + // Ok(job_id); + + // msg!([ctx] job_run::msg::create(run_id) { + // run_id: Some(run_id.into()), + // region_id: Some(region_id.into()), + + // job_spec_json: job_spec_json, + // proxied_ports: proxied_ports, + // ..Default::default() + // }) + // .await?; + + // Build response ports + let network_ports = ctx + .network_ports + .iter() + .map(|(port_label, port)| { + GlobalResult::Ok(( + port_label.clone(), + backend::ds::Port { + internal_port: port.internal_port, + public_hostname: None, + public_port: None, + routing: Some(match unwrap!(port.routing.clone()) { + dynamic_servers::server_create::port::Routing::GameGuard(x) => { + backend::ds::port::Routing::GameGuard(x) + } + dynamic_servers::server_create::port::Routing::Host(x) => { + backend::ds::port::Routing::Host(x) + } + }), + }, + )) + }) + .collect::>>()?; + + Ok(dynamic_servers::server_create::Response { + server: Some(backend::ds::Server { + server_id: Some(server_id.into()), + game_id: Some(game_id.into()), + datacenter_id: Some(datacenter_id.into()), + cluster_id: Some(cluster_id.into()), + tags: ctx.tags.clone(), + resources: Some(backend::ds::ServerResources { + cpu_millicores: resources.cpu_millicores, + memory_mib: resources.memory_mib, + }), + kill_timeout_ms: ctx.kill_timeout_ms, + webhook_url: ctx.webhook_url.clone(), + create_ts, + destroy_ts: None, + args: ctx.args.clone(), + environment: ctx.environment.clone(), + image_id: ctx.image_id, + network_mode: ctx.network_mode, + network_ports, + }), + }) +} + +/// Determines if a Nomad job is dispatched from our run. +/// +/// We use this when monitoring Nomad in order to determine which events to +/// pay attention to. +pub fn is_nomad_job_run(job_id: &str) -> bool { + job_id.starts_with("job-") && job_id.contains("/dispatch-") +} + +// Timeout from when `stop_job` is called and the kill signal is sent +pub const JOB_STOP_TIMEOUT: Duration = Duration::from_secs(30); + +pub const TASK_CLEANUP_CPU: i32 = 50; + +// Query Prometheus with: +// +// ``` +// max(nomad_client_allocs_memory_max_usage{ns="prod",exported_job=~"job-.*",task="run-cleanup"}) / 1000 / 1000 +// ``` +// +// 13.5 MB baseline, 29 MB highest peak +pub const TASK_CLEANUP_MEMORY: i32 = 32; + +pub const RUN_MAIN_TASK_NAME: &str = "main"; +pub const RUN_CLEANUP_TASK_NAME: &str = "run-cleanup"; + +// dispatch, need alloc, nomad monitor stuff, lots of stuff here, means that +// jobs can't be destroyed, maybe by job id? + +/// Generates a presigned URL for the job runner binary. +#[tracing::instrument] +async fn resolve_job_runner_binary_url( + ctx: &OperationContext, + region: &backend::region::Region, +) -> GlobalResult { + // Get provider + let provider = s3_util::Provider::default()?; + + let file_name = std::env::var("JOB_RUNNER_BINARY_KEY")?; + + // Build URL + let mm_lobby_delivery_method = unwrap!( + backend::cluster::BuildDeliveryMethod::from_i32(region.build_delivery_method), + "invalid datacenter build delivery method" + ); + match mm_lobby_delivery_method { + backend::cluster::BuildDeliveryMethod::S3Direct => { + tracing::info!("job runner using s3 direct delivery"); + + // Build client + let s3_client = s3_util::Client::from_env_opt( + "bucket-infra-artifacts", + provider, + s3_util::EndpointKind::External, + ) + .await?; + let presigned_req = s3_client + .get_object() + .bucket(s3_client.bucket()) + .key(file_name) + .presigned( + s3_util::aws_sdk_s3::presigning::config::PresigningConfig::builder() + .expires_in(std::time::Duration::from_secs(15 * 60)) + .build()?, + ) + .await?; + + let addr = presigned_req.uri().clone(); + + let addr_str = addr.to_string(); + tracing::info!(addr = %addr_str, "resolved job runner presigned request"); + + Ok(addr_str) + } + backend::cluster::BuildDeliveryMethod::TrafficServer => { + tracing::info!("job runner using traffic server delivery"); + + let region_id = unwrap_ref!(region.region_id).as_uuid(); + + // Choose a random ATS node to pull from + let (ats_vlan_ip,) = sql_fetch_one!( + [ctx, (IpAddr,)] + " + WITH sel AS ( + -- Select candidate vlan ips + SELECT + vlan_ip + FROM db_cluster.servers + WHERE + datacenter_id = $1 AND + pool_type = $2 AND + vlan_ip IS NOT NULL AND + install_complete_ts IS NOT NULL AND + drain_ts IS NULL AND + cloud_destroy_ts IS NULL + ) + SELECT vlan_ip + FROM sel + ORDER BY random() + LIMIT 1 + ", + // NOTE: region_id is just the old name for datacenter_id + ®ion_id, + backend::cluster::PoolType::Ats as i64, + ) + .await?; + + let addr = format!( + "http://{vlan_ip}:8080/s3-cache/{provider}/{namespace}-bucket-infra-artifacts/{file_name}", + vlan_ip = ats_vlan_ip, + provider = heck::KebabCase::to_kebab_case(provider.as_str()), + namespace = util::env::namespace(), + ); + + tracing::info!(%addr, "resolved artifact s3 url"); + + Ok(addr) + } + } +} diff --git a/svc/pkg/ds/ops/server-create/src/nomad_job.rs b/svc/pkg/ds/ops/server-create/src/nomad_job.rs new file mode 100644 index 0000000000..bf683aaa25 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/nomad_job.rs @@ -0,0 +1,613 @@ +use std::{collections::HashMap, convert::TryInto}; + +use chirp_worker::prelude::*; +use proto::backend::{self, matchmaker::lobby_runtime::NetworkMode as LobbyRuntimeNetworkMode}; +use regex::Regex; +use serde_json::json; + +use crate::{oci_config, util_job}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TransportProtocol { + Tcp, + Udp, +} + +impl From for TransportProtocol { + fn from(proxy_protocol: ProxyProtocol) -> Self { + match proxy_protocol { + ProxyProtocol::Http + | ProxyProtocol::Https + | ProxyProtocol::Tcp + | ProxyProtocol::TcpTls => Self::Tcp, + ProxyProtocol::Udp => Self::Udp, + } + } +} + +impl TransportProtocol { + pub fn as_cni_protocol(&self) -> &'static str { + match self { + Self::Tcp => "tcp", + Self::Udp => "udp", + } + } +} + +#[derive(Clone)] +pub enum ProxyProtocol { + Http, + Https, + Tcp, + TcpTls, + Udp, +} + +impl From for ProxyProtocol { + fn from(protocol: backend::ds::GameGuardProtocol) -> Self { + match protocol { + backend::ds::GameGuardProtocol::Http => Self::Http, + backend::ds::GameGuardProtocol::Https => Self::Https, + backend::ds::GameGuardProtocol::Tcp => Self::Tcp, + backend::ds::GameGuardProtocol::TcpTls => Self::TcpTls, + backend::ds::GameGuardProtocol::Udp => Self::Udp, + } + } +} + +/// Helper structure for parsing all of the runtime's ports before building the +/// config. +#[derive(Clone)] +pub struct DecodedPort { + pub label: String, + pub nomad_port_label: String, + pub target: u16, + pub proxy_protocol: ProxyProtocol, +} + +// pub fn gen_lobby_docker_job( +// runtime: &backend::matchmaker::lobby_runtime::Docker, +// _image_tag: &str, +// tier: &backend::region::Tier, +// lobby_config: bool, +// lobby_tags: bool, +// build_kind: backend::build::BuildKind, +// build_compression: backend::build::BuildCompression, +// ) -> GlobalResult { +// // IMPORTANT: This job spec must be deterministic. Do not pass in parameters +// // that change with every run, such as the lobby ID. Ensure the +// // `reuse_job_id` test passes when changing this function. +// use nomad_client::models::*; +// +// // runc-compatible resources +// let cpu = tier.rivet_cores_numerator as u64 * 1_000 / tier.rivet_cores_denominator as u64; // Millicore (1/1000 of a core) +// let memory = tier.memory * (1024 * 1024); // bytes +// let memory_max = tier.memory_max * (1024 * 1024); // bytes +// +// // Nomad-compatible resources +// let resources = Resources { +// // TODO: Configure this per-provider +// // Nomad configures CPU based on MHz, not millicores. We havel to calculate the CPU share +// // by knowing how many MHz are on the client. +// CPU: if tier.rivet_cores_numerator < tier.rivet_cores_denominator { +// Some((tier.cpu - util_job::TASK_CLEANUP_CPU as u64).try_into()?) +// } else { +// None +// }, +// cores: if tier.rivet_cores_numerator >= tier.rivet_cores_denominator { +// Some((tier.rivet_cores_numerator / tier.rivet_cores_denominator) as i32) +// } else { +// None +// }, +// memory_mb: Some( +// (TryInto::::try_into(memory)? / (1024 * 1024) +// - util_job::TASK_CLEANUP_MEMORY as i64) +// .try_into()?, +// ), +// // Allow oversubscribing memory by 50% of the reserved +// // memory if using less than the node's total memory +// memory_max_mb: Some( +// (TryInto::::try_into(memory_max)? / (1024 * 1024) +// - util_job::TASK_CLEANUP_MEMORY as i64) +// .try_into()?, +// ), +// disk_mb: Some(tier.disk as i32), // TODO: Is this deprecated? +// ..Resources::new() +// }; +// +// let network_mode = unwrap!(LobbyRuntimeNetworkMode::from_i32(runtime.network_mode)); +// +// // Read ports +// let decoded_ports = runtime +// .ports +// .iter() +// .map(|port| { +// let target = unwrap!(port.target_port) as u16; +// +// // TODO +// // GlobalResult::Ok(DecodedPort { +// // label: port.label.clone(), +// // nomad_port_label: util_mm::format_nomad_port_label(&port.label), +// // target, +// // proxy_protocol: unwrap!(ProxyProtocol::from_i32(port.proxy_protocol)), +// // }) +// GlobalResult::Ok(DecodedPort { +// label: port.label.clone(), +// nomad_port_label: String::new(), +// target, +// proxy_protocol: ProxyProtocol::Http, +// }) +// }) +// .collect::>>()?; +// +// // The container will set up port forwarding manually from the Nomad-defined ports on the host +// // to the CNI container +// let dynamic_ports = decoded_ports +// .iter() +// .map(|port| Port { +// label: Some(port.nomad_port_label.clone()), +// ..Port::new() +// }) +// .collect::>(); +// +// // Port mappings to pass to the container. Only used in bridge networking. +// let cni_port_mappings = decoded_ports +// .iter() +// .map(|port| { +// json!({ +// "HostPort": template_env_var_int(&nomad_host_port_env_var(&port.nomad_port_label)), +// "ContainerPort": port.target, +// // TODO +// // "Protocol": TransportProtocol::from(port.proxy_protocol).as_cni_protocol(), +// "Protocol": TransportProtocol::Udp.as_cni_protocol(), +// }) +// }) +// .collect::>(); +// +// // Also see util_mm:consts::DEFAULT_ENV_KEYS +// let mut my_env = runtime +// .env_vars +// .iter() +// .map(|v| (v.key.clone(), escape_go_template(&v.value))) +// .chain(if lobby_config { +// Some(( +// "RIVET_LOBBY_CONFIG".to_string(), +// template_env_var("NOMAD_META_LOBBY_CONFIG"), +// )) +// } else { +// None +// }) +// .chain(if lobby_tags { +// Some(( +// "RIVET_LOBBY_TAGS".to_string(), +// template_env_var("NOMAD_META_LOBBY_TAGS"), +// )) +// } else { +// None +// }) +// .chain([( +// "RIVET_API_ENDPOINT".to_string(), +// util::env::origin_api().to_string(), +// )]) +// .chain( +// // DEPRECATED: +// [ +// ("RIVET_CHAT_API_URL", "chat"), +// ("RIVET_GROUP_API_URL", "group"), +// ("RIVET_IDENTITY_API_URL", "identity"), +// ("RIVET_KV_API_URL", "kv"), +// ("RIVET_MATCHMAKER_API_URL", "matchmaker"), +// ] +// .iter() +// .filter(|_| util::env::support_deprecated_subdomains()) +// .map(|(env, service)| { +// ( +// env.to_string(), +// util::env::origin_api().replace("://", &format!("://{}.", service)), +// ) +// }), +// ) +// .chain( +// [ +// ( +// "RIVET_NAMESPACE_NAME", +// template_env_var("NOMAD_META_NAMESPACE_NAME"), +// ), +// ( +// "RIVET_NAMESPACE_ID", +// template_env_var("NOMAD_META_NAMESPACE_ID"), +// ), +// ( +// "RIVET_VERSION_NAME", +// template_env_var("NOMAD_META_VERSION_NAME"), +// ), +// ( +// "RIVET_VERSION_ID", +// template_env_var("NOMAD_META_VERSION_ID"), +// ), +// ( +// "RIVET_GAME_MODE_ID", +// template_env_var("NOMAD_META_LOBBY_GROUP_ID"), +// ), +// ( +// "RIVET_GAME_MODE_NAME", +// template_env_var("NOMAD_META_LOBBY_GROUP_NAME"), +// ), +// ("RIVET_LOBBY_ID", template_env_var("NOMAD_META_LOBBY_ID")), +// ("RIVET_TOKEN", template_env_var("NOMAD_META_LOBBY_TOKEN")), +// ("RIVET_REGION_ID", template_env_var("NOMAD_META_REGION_ID")), +// ( +// "RIVET_REGION_NAME", +// template_env_var("NOMAD_META_REGION_NAME"), +// ), +// ( +// "RIVET_MAX_PLAYERS_NORMAL", +// template_env_var("NOMAD_META_MAX_PLAYERS_NORMAL"), +// ), +// ( +// "RIVET_MAX_PLAYERS_DIRECT", +// template_env_var("NOMAD_META_MAX_PLAYERS_DIRECT"), +// ), +// ( +// "RIVET_MAX_PLAYERS_PARTY", +// template_env_var("NOMAD_META_MAX_PLAYERS_PARTY"), +// ), +// // CPU in millicores +// // +// // < 1000 is for fractional CPU +// // > 1000 is for whole CPU, will always be 1000 increments +// ("RIVET_CPU", cpu.to_string()), +// // Memory in bytes +// ("RIVET_MEMORY", memory.to_string()), +// // Memory in bytes for oversubscription +// ("RIVET_MEMORY_OVERSUBSCRIBE", memory_max.to_string()), +// // DEPRECATED: +// ( +// "RIVET_LOBBY_TOKEN", +// template_env_var("NOMAD_META_LOBBY_TOKEN"), +// ), +// ( +// "RIVET_LOBBY_GROUP_ID", +// template_env_var("NOMAD_META_LOBBY_GROUP_ID"), +// ), +// ( +// "RIVET_LOBBY_GROUP_NAME", +// template_env_var("NOMAD_META_LOBBY_GROUP_NAME"), +// ), +// ] +// .iter() +// .map(|(k, v)| (k.to_string(), v.to_string())), +// ) +// // Ports +// .chain(decoded_ports.iter().map(|port| { +// let port_value = match network_mode { +// // CNI will handle mapping the host port to the container port +// LobbyRuntimeNetworkMode::Bridge => port.target.to_string(), +// // The container needs to listen on the correct port +// LobbyRuntimeNetworkMode::Host => { +// template_env_var(&nomad_host_port_env_var(&port.nomad_port_label)) +// } +// }; +// +// // Port with the kebab case port key. Included for backward compatabiilty & for +// // less confusion. +// (format!("PORT_{}", port.label.replace('-', "_")), port_value) +// })) +// .map(|(k, v)| format!("{k}={v}")) +// .collect::>(); +// my_env.sort(); +// tracing::info!(?my_env, "my env"); +// +// let services = decoded_ports +// .iter() +// .map(|port| { +// let service_name = format!("${{NOMAD_META_LOBBY_ID}}-{}", port.label); +// GlobalResult::Ok(Some(Service { +// provider: Some("nomad".into()), +// name: Some(service_name), +// tags: Some(vec!["game".into()]), +// port_label: Some(port.nomad_port_label.clone()), +// // checks: if TransportProtocol::from(port.proxy_protocol) +// // == TransportProtocol::Tcp +// // { +// // Some(vec![ServiceCheck { +// // name: Some(format!("{}-probe", port.label)), +// // port_label: Some(port.nomad_port_label.clone()), +// // _type: Some("tcp".into()), +// // interval: Some(30_000_000_000), +// // timeout: Some(2_000_000_000), +// // ..ServiceCheck::new() +// // }]) +// // } else { +// // None +// // }, +// ..Service::new() +// })) +// }) +// .filter_map(|x| x.transpose()) +// .collect::>>()?; +// +// // Generate the command to download and decompress the file +// let mut download_cmd = r#"curl -Lf "$NOMAD_META_IMAGE_ARTIFACT_URL""#.to_string(); +// match build_compression { +// backend::build::BuildCompression::None => {} +// backend::build::BuildCompression::Lz4 => { +// download_cmd.push_str(" | lz4 -d -"); +// } +// } +// +// Ok(Job { +// _type: Some("batch".into()), +// constraints: Some(vec![Constraint { +// l_target: Some("${node.class}".into()), +// r_target: Some("job".into()), +// operand: Some("=".into()), +// }]), +// parameterized_job: Some(Box::new(ParameterizedJobConfig { +// payload: Some("forbidden".into()), +// meta_required: Some(vec![ +// "job_runner_binary_url".into(), +// "vector_socket_addr".into(), +// "image_artifact_url".into(), +// "namespace_id".into(), +// "namespace_name".into(), +// "version_id".into(), +// "version_name".into(), +// "lobby_group_id".into(), +// "lobby_group_name".into(), +// "lobby_id".into(), +// "lobby_token".into(), +// "lobby_config".into(), +// "lobby_tags".into(), +// "region_id".into(), +// "region_name".into(), +// "max_players_normal".into(), +// "max_players_direct".into(), +// "max_players_party".into(), +// "root_user_enabled".into(), +// ]), +// meta_optional: Some(vec!["rivet_test_id".into()]), +// })), +// task_groups: Some(vec![TaskGroup { +// name: Some(util_job::RUN_MAIN_TASK_NAME.into()), +// constraints: None, // TODO: Use parameter meta to specify the hardware +// affinities: None, // TODO: +// // Allows for jobs to keep running and receiving players in the +// // event of a disconnection from the Nomad server. +// max_client_disconnect: Some(5 * 60 * 1_000_000_000), +// restart_policy: Some(Box::new(RestartPolicy { +// attempts: Some(0), +// mode: Some("fail".into()), +// ..RestartPolicy::new() +// })), +// reschedule_policy: Some(Box::new(ReschedulePolicy { +// attempts: Some(0), +// unlimited: Some(false), +// ..ReschedulePolicy::new() +// })), +// networks: Some(vec![NetworkResource { +// // The setup.sh script will set up a CNI network if using bridge networking +// mode: Some("host".into()), +// dynamic_ports: Some(dynamic_ports), +// ..NetworkResource::new() +// }]), +// services: Some(services), +// // Configure ephemeral disk for logs +// ephemeral_disk: Some(Box::new(EphemeralDisk { +// size_mb: Some(tier.disk as i32), +// ..EphemeralDisk::new() +// })), +// tasks: Some(vec![ +// Task { +// name: Some("runc-setup".into()), +// lifecycle: Some(Box::new(TaskLifecycle { +// hook: Some("prestart".into()), +// sidecar: Some(false), +// })), +// driver: Some("raw_exec".into()), +// config: Some({ +// let mut x = HashMap::new(); +// x.insert("command".into(), json!("${NOMAD_TASK_DIR}/setup.sh")); +// x +// }), +// templates: Some(vec![ +// Template { +// embedded_tmpl: Some(include_str!("./scripts/setup.sh").replace( +// "__HOST_NETWORK__", +// match network_mode { +// LobbyRuntimeNetworkMode::Bridge => "false", +// LobbyRuntimeNetworkMode::Host => "true", +// }, +// )), +// dest_path: Some("${NOMAD_TASK_DIR}/setup.sh".into()), +// perms: Some("744".into()), +// ..Template::new() +// }, +// Template { +// embedded_tmpl: Some( +// include_str!("./scripts/setup_job_runner.sh").into(), +// ), +// dest_path: Some("${NOMAD_TASK_DIR}/setup_job_runner.sh".into()), +// perms: Some("744".into()), +// ..Template::new() +// }, +// Template { +// embedded_tmpl: Some( +// include_str!("./scripts/setup_oci_bundle.sh") +// .replace("__DOWNLOAD_CMD__", &download_cmd) +// .replace( +// "__BUILD_KIND__", +// match build_kind { +// backend::build::BuildKind::DockerImage => { +// "docker-image" +// } +// backend::build::BuildKind::OciBundle => "oci-bundle", +// }, +// ), +// ), +// dest_path: Some("${NOMAD_TASK_DIR}/setup_oci_bundle.sh".into()), +// perms: Some("744".into()), +// ..Template::new() +// }, +// Template { +// embedded_tmpl: Some( +// include_str!("./scripts/setup_cni_network.sh").into(), +// ), +// dest_path: Some("${NOMAD_TASK_DIR}/setup_cni_network.sh".into()), +// perms: Some("744".into()), +// ..Template::new() +// }, +// Template { +// embedded_tmpl: Some(gen_oci_bundle_config( +// cpu, memory, memory_max, my_env, +// )?), +// dest_path: Some( +// "${NOMAD_ALLOC_DIR}/oci-bundle-config.base.json".into(), +// ), +// ..Template::new() +// }, +// Template { +// embedded_tmpl: Some(inject_consul_env_template( +// &serde_json::to_string(&cni_port_mappings)?, +// )?), +// dest_path: Some("${NOMAD_ALLOC_DIR}/cni-port-mappings.json".into()), +// ..Template::new() +// }, +// ]), +// resources: Some(Box::new(Resources { +// // TODO +// // CPU: Some(util_mm::RUNC_SETUP_CPU), +// // memory_mb: Some(util_mm::RUNC_SETUP_MEMORY), +// CPU: None, +// memory_mb: None, +// ..Resources::new() +// })), +// log_config: Some(Box::new(LogConfig { +// max_files: Some(4), +// max_file_size_mb: Some(2), +// disabled: None, +// })), +// ..Task::new() +// }, +// Task { +// name: Some(util_job::RUN_MAIN_TASK_NAME.into()), +// driver: Some("raw_exec".into()), +// config: Some({ +// let mut x = HashMap::new(); +// // This is downloaded in setup_job_runner.sh +// x.insert("command".into(), json!("${NOMAD_ALLOC_DIR}/job-runner")); +// x +// }), +// resources: Some(Box::new(resources.clone())), +// // Intentionally high timeout. Killing jobs is handled manually with signals. +// kill_timeout: Some(86400 * 1_000_000_000), +// kill_signal: Some("SIGTERM".into()), +// log_config: Some(Box::new(LogConfig { +// max_files: Some(4), +// max_file_size_mb: Some(4), +// disabled: None, +// })), +// ..Task::new() +// }, +// Task { +// name: Some("runc-cleanup".into()), +// lifecycle: Some(Box::new(TaskLifecycle { +// hook: Some("poststop".into()), +// sidecar: Some(false), +// })), +// driver: Some("raw_exec".into()), +// config: Some({ +// let mut x = HashMap::new(); +// x.insert("command".into(), json!("${NOMAD_TASK_DIR}/cleanup.sh")); +// x +// }), +// templates: Some(vec![Template { +// embedded_tmpl: Some(include_str!("./scripts/cleanup.sh").into()), +// dest_path: Some("${NOMAD_TASK_DIR}/cleanup.sh".into()), +// perms: Some("744".into()), +// ..Template::new() +// }]), +// resources: Some(Box::new(Resources { +// // TODO +// // CPU: Some(util_mm::RUNC_CLEANUP_CPU), +// // memory_mb: Some(util_mm::RUNC_CLEANUP_MEMORY), +// CPU: None, +// memory_mb: None, +// ..Resources::new() +// })), +// log_config: Some(Box::new(LogConfig { +// max_files: Some(4), +// max_file_size_mb: Some(2), +// disabled: None, +// })), +// ..Task::new() +// }, +// ]), +// ..TaskGroup::new() +// }]), +// ..Job::new() +// }) +// } + +/// Build base config used to generate the OCI bundle's config.json. +pub fn gen_oci_bundle_config( + cpu: u64, + memory: u64, + memory_max: u64, + env: Vec, +) -> GlobalResult { + let config_str = serde_json::to_string(&oci_config::config(cpu, memory, memory_max, env))?; + + // Escape Go template syntax + let config_str = inject_consul_env_template(&config_str)?; + + Ok(config_str) +} + +/// Makes user-generated string safe to inject in to a Go template. +pub fn escape_go_template(input: &str) -> String { + let re = Regex::new(r"(\{\{|\}\})").unwrap(); + re.replace_all(input, r#"{{"$1"}}"#) + .to_string() + // TODO: This removes exploits to inject env vars (see below) + // SVC-3307 + .replace("###", "") +} + +/// Generates a template string that we can substitute with the real environment variable +/// +/// This must be safe to inject in to a JSON string so it can be substituted after rendering the +/// JSON object. Intended to be used from within JSON. +/// +/// See inject_consul_env_template. +pub fn template_env_var(name: &str) -> String { + format!("###ENV:{name}###") +} + +/// Like template_env_var, but removes surrounding quotes. +pub fn template_env_var_int(name: &str) -> String { + format!("###ENV_INT:{name}###") +} + +/// Substitutes env vars generated from template_env_var with Consul template syntax. +/// +/// Intended to be used from within JSON. +pub fn inject_consul_env_template(input: &str) -> GlobalResult { + // Regular strings + let re = Regex::new(r"###ENV:(\w+)###")?; + let output = re + .replace_all(input, r#"{{ env "$1" | regexReplaceAll "\"" "\\\"" }}"#) + .to_string(); + + // Integers + let re = Regex::new(r####""###ENV_INT:(\w+)###""####)?; + let output = re + .replace_all(&output, r#"{{ env "$1" | regexReplaceAll "\"" "\\\"" }}"#) + .to_string(); + + Ok(output) +} + +pub fn nomad_host_port_env_var(port_label: &str) -> String { + format!("NOMAD_HOST_PORT_{}", port_label.replace('-', "_")) +} diff --git a/svc/pkg/ds/ops/server-create/src/oci_config.rs b/svc/pkg/ds/ops/server-create/src/oci_config.rs new file mode 100644 index 0000000000..f03105fd13 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/oci_config.rs @@ -0,0 +1,316 @@ +use chirp_worker::prelude::*; +use serde_json::json; + +// CPU period in microseconds. +// +// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt +const CPU_PERIOD: u64 = 100000; + +/// Generates base config.json for an OCI bundle. +pub fn config(cpu: u64, memory: u64, memory_max: u64, env: Vec) -> serde_json::Value { + // CPU shares is a relative weight. It doesn't matter what unit we pass here as + // long as the ratios between the containers are correct. + // + // Corresponds to cpu.weight in cgroups. Must be [1, 10_000] + // + // We divide by 8 in order to make sure the CPU shares are within bounds. `cpu` is measured in + // millishares, so 1_000 = 1 core. For a range of 32d1 (32_000) to 1d16 (62), we divide by 8 + // to make the range 3_200 to 6. + let mut cpu_shares = cpu / 10; + if cpu_shares > 10_000 { + cpu_shares = 10_000; + tracing::warn!(?cpu_shares, "cpu_shares > 10_000"); + } else if cpu_shares < 1 { + cpu_shares = 1; + tracing::warn!(?cpu_shares, "cpu_shares < 1"); + } + + // This is a modified version of the default config.json generated by containerd. + // + // Some values will be overridden at runtime by the values in the OCI bundle's config.json. + // + // Default Docker spec: https://github.com/moby/moby/blob/777e9f271095685543f30df0ff7a12397676f938/oci/defaults.go#L49 + // + // Generate config.json with containerd: + // ctr run --rm -t --seccomp docker.io/library/debian:latest debian-container-id /bin/bash + // cat /run/containerd/io.containerd.runtime.v2.task/default/debian-container-id/config.json | jq + json!({ + "ociVersion": "1.0.2-dev", + "process": { + // user, args, and cwd will be injected at runtime + + // Will be merged with the OCI bundle's env + // + // These will take priority over the OCI bundle's env + "env": env, + + "terminal": false, + "capabilities": { + "bounding": capabilities(), + "effective": capabilities(), + "permitted": capabilities() + }, + "rlimits": [ + { + "type": "RLIMIT_NOFILE", + "hard": 1024, + "soft": 1024 + } + ], + "noNewPrivileges": true + + // TODO: oomScoreAdj + // TODO: scheduler + // TODO: iopriority + // TODO: rlimit? + }, + "root": { + "path": "rootfs", + // This means we can't reuse the oci-bundle since the rootfs is writable. + "readonly": false + }, + "mounts": mounts(), + "linux": { + "resources": { + "devices": linux_resources_devices(), + "cpu": { + "shares": cpu_shares, + // If `quota` is greater than `period`, it is allowed to use multiple cores. + // + // Read more: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu + // "quota": CPU_PERIOD * cpu / 1_000, + // "period": CPU_PERIOD, + // Use the env var for the CPU since Nomad handles assigning CPUs to each task + // "cpus": if cpu >= 1_000 { + // Some(template_env_var("NOMAD_CPU_CORES")) + // } else { + // None + // } + }, + // Docker: https://github.com/moby/moby/blob/777e9f271095685543f30df0ff7a12397676f938/daemon/daemon_unix.go#L75 + "memory": { + "reservation": memory, + "limit": memory_max, + }, + + // TODO: network + // TODO: pids + // TODO: hugepageLimits + // TODO: blockIO + }, + // "cgroupsPath": "/default/debian-container-id", + "namespaces": [ + { + "type": "pid" + }, + { + "type": "ipc" + }, + { + "type": "uts" + }, + { + "type": "mount" + } + ], + "maskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi" + ], + "readonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "seccomp": super::seccomp::seccomp() + } + }) +} + +// Default Docker capabilities: https://github.com/moby/moby/blob/777e9f271095685543f30df0ff7a12397676f938/oci/caps/defaults.go#L4 +fn capabilities() -> Vec<&'static str> { + vec![ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + ] +} + +fn mounts() -> serde_json::Value { + json!([ + { + "destination": "/proc", + "type": "proc", + "source": "proc", + "options": [ + "nosuid", + "noexec", + "nodev" + ] + }, + { + "destination": "/dev", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "nosuid", + "strictatime", + "mode=755", + "size=65536k" + ] + }, + { + "destination": "/dev/pts", + "type": "devpts", + "source": "devpts", + "options": [ + "nosuid", + "noexec", + "newinstance", + "ptmxmode=0666", + "mode=0620", + "gid=5" + ] + }, + { + "destination": "/dev/shm", + "type": "tmpfs", + "source": "shm", + "options": [ + "nosuid", + "noexec", + "nodev", + "mode=1777", + "size=65536k" + ] + }, + { + "destination": "/dev/mqueue", + "type": "mqueue", + "source": "mqueue", + "options": [ + "nosuid", + "noexec", + "nodev" + ] + }, + { + "destination": "/sys", + "type": "sysfs", + "source": "sysfs", + "options": [ + "nosuid", + "noexec", + "nodev", + "ro" + ] + }, + { + "destination": "/run", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "nosuid", + "strictatime", + "mode=755", + "size=65536k" + ] + } + ]) +} + +fn linux_resources_devices() -> serde_json::Value { + // Devices implicitly contains the following devices: + // null, zero, full, random, urandom, tty, console, and ptmx. + // ptmx is a bind mount or symlink of the container's ptmx. + // See also: https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#default-devices + json!([ + { + "allow": false, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 1, + "minor": 3, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 1, + "minor": 8, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 1, + "minor": 7, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 5, + "minor": 0, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 1, + "minor": 5, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 1, + "minor": 9, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 5, + "minor": 1, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 136, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 5, + "minor": 2, + "access": "rwm" + } + ]) +} diff --git a/svc/pkg/ds/ops/server-create/src/scripts/cleanup.sh b/svc/pkg/ds/ops/server-create/src/scripts/cleanup.sh new file mode 100644 index 0000000000..2815a26176 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/scripts/cleanup.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +log() { + local timestamp=$(date +"%Y-%m-%d %H:%M:%S.%3N") + echo "[$timestamp] [cleanup] $@" +} + +# MARK: Generate CNI parameters +export CNI_PATH="/opt/cni/bin" +export NETCONFPATH="/opt/cni/config" +export CNI_IFNAME="eth0" +export CAP_ARGS=$(cat "$NOMAD_ALLOC_DIR/cni-cap-args.json") + +# Every step in this script gracefully fails so everything gets cleaned up no matter what. + +if [ -f "$NOMAD_ALLOC_DIR/container-id" ]; then + CONTAINER_ID=$(cat "$NOMAD_ALLOC_DIR/container-id") + NETWORK_NAME="rivet-job" + NETNS_PATH="/var/run/netns/$CONTAINER_ID" + + log "Deleting container $CONTAINER_ID" + runc delete --force "$CONTAINER_ID" || log 'Failed to delete container' >&2 + + log "Deleting network $NETWORK_NAME from namespace $NETNS_PATH" + cnitool del $NETWORK_NAME $NETNS_PATH || log 'Failed to delete network' >&2 + + log "Deleting network $CONTAINER_ID" + ip netns del "$CONTAINER_ID" || log 'Failed to delete network' >&2 +else + log "No container ID found. Network may have leaked." >&2 +fi + diff --git a/svc/pkg/ds/ops/server-create/src/scripts/setup.sh b/svc/pkg/ds/ops/server-create/src/scripts/setup.sh new file mode 100644 index 0000000000..02b3f424bd --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/scripts/setup.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +log() { + local timestamp=$(date +"%Y-%m-%d %H:%M:%S.%3N") + echo "[$timestamp] [setup] $@" +} + +log "Starting setup" + +log 'Env:' +env +echo + +# Need to prefix with "rivet-" in order to not interfere with any +# auto-generated resources that Nomad creates for the given alloc ID +export CONTAINER_ID="rivet-$NOMAD_ALLOC_ID" +log "CONTAINER_ID: $CONTAINER_ID" +echo -n "$CONTAINER_ID" > "$NOMAD_ALLOC_DIR/container-id" + +# Path to the created namespace +if __HOST_NETWORK__; then + # Host network + export NETNS_PATH="/proc/1/ns/net" +else + # CNI network that will be created + export NETNS_PATH="/var/run/netns/$CONTAINER_ID" +fi + +# Run job runner setup script +"$NOMAD_TASK_DIR/setup_job_runner.sh" & +pid_job_runner=$! + +# Run OCI setup script +"$NOMAD_TASK_DIR/setup_oci_bundle.sh" & +pid_oci=$! + +# Run CNI setup script +if ! __HOST_NETWORK__; then + "$NOMAD_TASK_DIR/setup_cni_network.sh" & + pid_cni=$! +fi + +# Wait for job runner setup scripts to finish +wait $pid_job_runner +exit_status_job_runner=$? +if [ $exit_status_job_runner -ne 0 ]; then + log "job-runner setup failed with exit code $exit_status_job_runner" + exit $exit_status_job_runner +fi + +# Wait for OCI setup scripts to finish +wait $pid_oci +exit_status_oci=$? +if [ $exit_status_oci -ne 0 ]; then + log "OCI setup failed with exit code $exit_status_oci" + exit $exit_status_oci +fi + +# Wait for CNI setup script to finish +if ! __HOST_NETWORK__; then + wait $pid_cni + exit_status_cni=$? + if [ $exit_status_cni -ne 0 ]; then + log "CNI setup failed with exit code $exit_status_cni" + exit $exit_status_cni + fi +fi + +log "Setup finished" + diff --git a/svc/pkg/ds/ops/server-create/src/scripts/setup_cni_network.sh b/svc/pkg/ds/ops/server-create/src/scripts/setup_cni_network.sh new file mode 100644 index 0000000000..8a97a2e345 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/scripts/setup_cni_network.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +log() { + local timestamp=$(date +"%Y-%m-%d %H:%M:%S.%3N") + echo "[$timestamp] [setup_cni_network] $@" +} + +# MARK: Generate CNI parameters +# +# See https://github.com/containernetworking/cni/blob/b62753aa2bfa365c1ceaff6f25774a8047c896b5/cnitool/cnitool.go#L31 + +# See Nomad capabilities equivalent: +# https://github.com/hashicorp/nomad/blob/a8f0f2612ef9d283ed903721f8453a0c0c3f51c5/client/allocrunner/networking_cni.go#L105C46-L105C46 +# +# See supported args: +# https://github.com/containerd/go-cni/blob/6603d5bd8941d7f2026bb5627f6aa4ff434f859a/namespace_opts.go#L22 +jq -c < "$NOMAD_ALLOC_DIR/cni-cap-args.json" +{ + "portMappings": $(cat "$NOMAD_ALLOC_DIR/cni-port-mappings.json") +} +EOF + +export CNI_PATH="/opt/cni/bin" +export NETCONFPATH="/opt/cni/config" +export CNI_IFNAME="eth0" +export CAP_ARGS=$(cat "$NOMAD_ALLOC_DIR/cni-cap-args.json") +log "CAP_ARGS: $CAP_ARGS" + +# MARK: Create network +# +# See Nomad network creation: +# https://github.com/hashicorp/nomad/blob/a8f0f2612ef9d283ed903721f8453a0c0c3f51c5/client/allocrunner/network_manager_linux.go#L119 + +# Name of the network in /opt/cni/config/$NETWORK_NAME.conflist +NETWORK_NAME="rivet-job" + +log "Creating network $CONTAINER_ID" +ip netns add "$CONTAINER_ID" + +log "Adding network $NETWORK_NAME to namespace $NETNS_PATH" +cnitool add "$NETWORK_NAME" "$NETNS_PATH" > $NOMAD_ALLOC_DIR/cni.json + +log "Finished setting up CNI network" + diff --git a/svc/pkg/ds/ops/server-create/src/scripts/setup_job_runner.sh b/svc/pkg/ds/ops/server-create/src/scripts/setup_job_runner.sh new file mode 100644 index 0000000000..2c2d116710 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/scripts/setup_job_runner.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +log() { + local timestamp=$(date +"%Y-%m-%d %H:%M:%S.%3N") + echo "[$timestamp] [setup_job_runner] $@" +} + +# Download job runner binary +curl -Lf "$NOMAD_META_job_runner_binary_url" -o "${NOMAD_ALLOC_DIR}/job-runner" +chmod +x "${NOMAD_ALLOC_DIR}/job-runner" +log "Finished downloading job-runner" + diff --git a/svc/pkg/ds/ops/server-create/src/scripts/setup_oci_bundle.sh b/svc/pkg/ds/ops/server-create/src/scripts/setup_oci_bundle.sh new file mode 100644 index 0000000000..4c11d8280e --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/scripts/setup_oci_bundle.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +log() { + local timestamp=$(date +"%Y-%m-%d %H:%M:%S.%3N") + echo "[$timestamp] [setup_oci_bundle] $@" +} + +DOCKER_IMAGE_PATH="$NOMAD_ALLOC_DIR/docker-image.tar" +OCI_IMAGE_PATH="$NOMAD_ALLOC_DIR/oci-image" +OCI_BUNDLE_PATH="$NOMAD_ALLOC_DIR/oci-bundle" + +# MARK: Generate OCI bundle +case "__BUILD_KIND__" in + "docker-image") + # We need to convert the Docker image to an OCI bundle in order to run it. + + log "Downloading Docker image" + __DOWNLOAD_CMD__ > "$DOCKER_IMAGE_PATH" + + # Allows us to work with the build with umoci + log "Converting Docker image -> OCI image" + skopeo copy "docker-archive:$DOCKER_IMAGE_PATH" "oci:$OCI_IMAGE_PATH:default" + + # Allows us to run the bundle natively with runc + log "Converting OCI image -> OCI bundle" + + umoci unpack --image "$OCI_IMAGE_PATH:default" "$OCI_BUNDLE_PATH" + ;; + "oci-bundle") + log "Downloading OCI bundle" + mkdir "$OCI_BUNDLE_PATH" + __DOWNLOAD_CMD__ | tar -x -C "$OCI_BUNDLE_PATH" + + ;; + *) + log "Unknown build kind" + exit 1 + ;; +esac + +# resolv.conf +# +# See also rivet-job.conflist in lib/bolt/core/src/dep/terraform/install_scripts/files/nomad.sh +cat < $NOMAD_ALLOC_DIR/resolv.conf +nameserver 8.8.8.8 +nameserver 8.8.4.4 +nameserver 2001:4860:4860::8888 +nameserver 2001:4860:4860::8844 +options rotate +options edns0 +options attempts:2 +EOF + +# MARK: Config +# +# Sanitize the config.json by copying safe properties from the provided bundle in to our base config. +# +# This way, we enforce our own capabilities on the container instead of trusting the +# provided config.json +log "Templating config.json" +OVERRIDE_CONFIG="$NOMAD_ALLOC_DIR/oci-bundle-config.overrides.json" +mv "$OCI_BUNDLE_PATH/config.json" "$OVERRIDE_CONFIG" + +USER_ENV_PATH="$NOMAD_ALLOC_DIR/user-env.json" +echo $NOMAD_META_user_env | jq -r 'to_entries | map("\(.key)=\(.value)")' > $USER_ENV_PATH + +# get new envb in here somehow +# check bounds + +# Template new config +jq " +.process.args = $(jq '.process.args' $OVERRIDE_CONFIG) | +.process.env = ( + $(cat $USER_ENV_PATH | jq -c '.') + + $(jq -c '.process.env' $OVERRIDE_CONFIG) + + .process.env +) | +.process.user = $(jq '.process.user' $OVERRIDE_CONFIG) | +.process.cwd = $(jq '.process.cwd' $OVERRIDE_CONFIG) | +.linux.namespaces += [{\"type\": \"network\", \"path\": \"$NETNS_PATH\"}] | +.mounts += [{ + \"destination\": \"/etc/resolv.conf\", + \"type\": \"bind\", + \"source\": \"$NOMAD_ALLOC_DIR/resolv.conf\", + \"options\": [\"rbind\", \"rprivate\"] +}] +" "$NOMAD_ALLOC_DIR/oci-bundle-config.base.json" > "$OCI_BUNDLE_PATH/config.json" + +# Config will be validated in `job-runner` + +log "Finished setting up OCI bundle" diff --git a/svc/pkg/ds/ops/server-create/src/seccomp.rs b/svc/pkg/ds/ops/server-create/src/seccomp.rs new file mode 100644 index 0000000000..f3f7fcfec3 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/seccomp.rs @@ -0,0 +1,481 @@ +use chirp_worker::prelude::*; +use serde_json::json; + +pub fn seccomp() -> serde_json::Value { + // Copied from auto-generated containerd + // + // See comment in super::oci_conifg::config on how to generate this + json!({ + "defaultAction": "SCMP_ACT_ERRNO", + "architectures": [ + "SCMP_ARCH_X86_64", + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ], + "syscalls": [ + { + "names": syscall_names(), + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 8, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131072, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131080, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 4294967295u32, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "ptrace" + ], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "arch_prctl", + "modify_ldt" + ], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "chroot" + ], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "clone" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2114060288, + "op": "SCMP_CMP_MASKED_EQ" + } + ] + }, + { + "names": [ + "clone3" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 38 + } + ] + }) +} + +fn syscall_names() -> Vec<&'static str> { + vec![ + "accept", + "accept4", + "access", + "adjtimex", + "alarm", + "bind", + "brk", + "capget", + "capset", + "chdir", + "chmod", + "chown", + "chown32", + "clock_adjtime", + "clock_adjtime64", + "clock_getres", + "clock_getres_time64", + "clock_gettime", + "clock_gettime64", + "clock_nanosleep", + "clock_nanosleep_time64", + "close", + "close_range", + "connect", + "copy_file_range", + "creat", + "dup", + "dup2", + "dup3", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_ctl_old", + "epoll_pwait", + "epoll_pwait2", + "epoll_wait", + "epoll_wait_old", + "eventfd", + "eventfd2", + "execve", + "execveat", + "exit", + "exit_group", + "faccessat", + "faccessat2", + "fadvise64", + "fadvise64_64", + "fallocate", + "fanotify_mark", + "fchdir", + "fchmod", + "fchmodat", + "fchown", + "fchown32", + "fchownat", + "fcntl", + "fcntl64", + "fdatasync", + "fgetxattr", + "flistxattr", + "flock", + "fork", + "fremovexattr", + "fsetxattr", + "fstat", + "fstat64", + "fstatat64", + "fstatfs", + "fstatfs64", + "fsync", + "ftruncate", + "ftruncate64", + "futex", + "futex_time64", + "futex_waitv", + "futimesat", + "getcpu", + "getcwd", + "getdents", + "getdents64", + "getegid", + "getegid32", + "geteuid", + "geteuid32", + "getgid", + "getgid32", + "getgroups", + "getgroups32", + "getitimer", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getppid", + "getpriority", + "getrandom", + "getresgid", + "getresgid32", + "getresuid", + "getresuid32", + "getrlimit", + "get_robust_list", + "getrusage", + "getsid", + "getsockname", + "getsockopt", + "get_thread_area", + "gettid", + "gettimeofday", + "getuid", + "getuid32", + "getxattr", + "inotify_add_watch", + "inotify_init", + "inotify_init1", + "inotify_rm_watch", + "io_cancel", + "ioctl", + "io_destroy", + "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", + "ioprio_get", + "ioprio_set", + "io_setup", + "io_submit", + "io_uring_enter", + "io_uring_register", + "io_uring_setup", + "ipc", + "kill", + "landlock_add_rule", + "landlock_create_ruleset", + "landlock_restrict_self", + "lchown", + "lchown32", + "lgetxattr", + "link", + "linkat", + "listen", + "listxattr", + "llistxattr", + "_llseek", + "lremovexattr", + "lseek", + "lsetxattr", + "lstat", + "lstat64", + "madvise", + "membarrier", + "memfd_create", + "memfd_secret", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlock2", + "mlockall", + "mmap", + "mmap2", + "mprotect", + "mq_getsetattr", + "mq_notify", + "mq_open", + "mq_timedreceive", + "mq_timedreceive_time64", + "mq_timedsend", + "mq_timedsend_time64", + "mq_unlink", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "name_to_handle_at", + "nanosleep", + "newfstatat", + "_newselect", + "open", + "openat", + "openat2", + "pause", + "pidfd_open", + "pidfd_send_signal", + "pipe", + "pipe2", + "pkey_alloc", + "pkey_free", + "pkey_mprotect", + "poll", + "ppoll", + "ppoll_time64", + "prctl", + "pread64", + "preadv", + "preadv2", + "prlimit64", + "process_mrelease", + "pselect6", + "pselect6_time64", + "pwrite64", + "pwritev", + "pwritev2", + "read", + "readahead", + "readlink", + "readlinkat", + "readv", + "recv", + "recvfrom", + "recvmmsg", + "recvmmsg_time64", + "recvmsg", + "remap_file_pages", + "removexattr", + "rename", + "renameat", + "renameat2", + "restart_syscall", + "rmdir", + "rseq", + "rt_sigaction", + "rt_sigpending", + "rt_sigprocmask", + "rt_sigqueueinfo", + "rt_sigreturn", + "rt_sigsuspend", + "rt_sigtimedwait", + "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", + "sched_getaffinity", + "sched_getattr", + "sched_getparam", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_rr_get_interval_time64", + "sched_setaffinity", + "sched_setattr", + "sched_setparam", + "sched_setscheduler", + "sched_yield", + "seccomp", + "select", + "semctl", + "semget", + "semop", + "semtimedop", + "semtimedop_time64", + "send", + "sendfile", + "sendfile64", + "sendmmsg", + "sendmsg", + "sendto", + "setfsgid", + "setfsgid32", + "setfsuid", + "setfsuid32", + "setgid", + "setgid32", + "setgroups", + "setgroups32", + "setitimer", + "setpgid", + "setpriority", + "setregid", + "setregid32", + "setresgid", + "setresgid32", + "setresuid", + "setresuid32", + "setreuid", + "setreuid32", + "setrlimit", + "set_robust_list", + "setsid", + "setsockopt", + "set_thread_area", + "set_tid_address", + "setuid", + "setuid32", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "shutdown", + "sigaltstack", + "signalfd", + "signalfd4", + "sigprocmask", + "sigreturn", + "socket", + "socketcall", + "socketpair", + "splice", + "stat", + "stat64", + "statfs", + "statfs64", + "statx", + "symlink", + "symlinkat", + "sync", + "sync_file_range", + "syncfs", + "sysinfo", + "tee", + "tgkill", + "time", + "timer_create", + "timer_delete", + "timer_getoverrun", + "timer_gettime", + "timer_gettime64", + "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", + "times", + "tkill", + "truncate", + "truncate64", + "ugetrlimit", + "umask", + "uname", + "unlink", + "unlinkat", + "utime", + "utimensat", + "utimensat_time64", + "utimes", + "vfork", + "vmsplice", + "wait4", + "waitid", + "waitpid", + "write", + "writev", + ] +} diff --git a/svc/pkg/ds/ops/server-create/src/util_job.rs b/svc/pkg/ds/ops/server-create/src/util_job.rs new file mode 100644 index 0000000000..b2a9b44bfd --- /dev/null +++ b/svc/pkg/ds/ops/server-create/src/util_job.rs @@ -0,0 +1,26 @@ +use std::time::Duration; + +/// Determines if a Nomad job is dispatched from our run. +/// +/// We use this when monitoring Nomad in order to determine which events to +/// pay attention to. +pub fn is_nomad_job_run(job_id: &str) -> bool { + job_id.starts_with("job-") && job_id.contains("/dispatch-") +} + +// Timeout from when `stop_job` is called and the kill signal is sent +pub const JOB_STOP_TIMEOUT: Duration = Duration::from_secs(30); + +pub const TASK_CLEANUP_CPU: i32 = 50; + +// Query Prometheus with: +// +// ``` +// max(nomad_client_allocs_memory_max_usage{ns="prod",exported_job=~"job-.*",task="run-cleanup"}) / 1000 / 1000 +// ``` +// +// 13.5 MB baseline, 29 MB highest peak +pub const TASK_CLEANUP_MEMORY: i32 = 32; + +pub const RUN_MAIN_TASK_NAME: &str = "main"; +pub const RUN_CLEANUP_TASK_NAME: &str = "run-cleanup"; diff --git a/svc/pkg/ds/ops/server-create/tests/integration.rs b/svc/pkg/ds/ops/server-create/tests/integration.rs new file mode 100644 index 0000000000..b1fccd5ce0 --- /dev/null +++ b/svc/pkg/ds/ops/server-create/tests/integration.rs @@ -0,0 +1,152 @@ +use std::collections::HashMap; + +use chirp_worker::prelude::*; +use proto::backend::{ + self, + pkg::{dynamic_servers, token}, +}; +use rivet_api::{apis::*, models}; + +#[worker_test] +async fn create(ctx: TestCtx) { + let game_res = op!([ctx] faker_game { + ..Default::default() + }) + .await + .unwrap(); + let game_id = game_res.game_id.unwrap(); + + // Create token + let token_res = op!([ctx] token_create { + token_config: Some(token::create::request::TokenConfig { + ttl: util::duration::days(90), + }), + issuer: "test".to_owned(), + kind: Some(token::create::request::Kind::New( + token::create::request::KindNew { entitlements: vec![proto::claims::Entitlement { + kind: Some(proto::claims::entitlement::Kind::GameService( + proto::claims::entitlement::GameService { + game_id: Some(game_id), + } + )), + }]}, + )), + label: Some("game_service".to_owned()), + ..Default::default() + }) + .await + .unwrap(); + + tracing::info!("token_res for key: {:?}", token_res); + + // Pick an existing cluster + let cluster_id = op!([ctx] cluster_list {}) + .await + .unwrap() + .cluster_ids + .first() + .unwrap() + .to_owned(); + + // Pick an existing datacenter + let datacenter_id = op!([ctx] cluster_datacenter_list { + cluster_ids: vec![cluster_id], + }) + .await + .unwrap() + .clusters + .first() + .unwrap() + .datacenter_ids + .first() + .unwrap() + .to_owned(); + + let build_res: backend::pkg::faker::build::Response = op!([ctx] faker_build { + game_id: Some(game_id), + image: backend::faker::Image::DsEcho as i32, + }) + .await + .unwrap(); + + let faker_region = op!([ctx] faker_region {}).await.unwrap(); + + let env = vec![ + ("some_envkey_test".to_string(), "2134523".to_string()), + ( + "some_other_envkey_test".to_string(), + "4325234356".to_string(), + ), + ] + .into_iter() + .collect(); + + let ports = vec![( + "testing2".to_string(), + dynamic_servers::server_create::Port { + internal_port: Some(28234), + routing: Some(dynamic_servers::server_create::port::Routing::GameGuard( + backend::ds::GameGuardRouting { protocol: 0 }, + )), + }, + )] + // Collect into hashmap + .into_iter() + .collect(); + + let server = op!([ctx] ds_server_create { + game_id: Some(game_id), + cluster_id: Some(cluster_id), + datacenter_id: faker_region.region_id, + resources: Some(proto::backend::ds::ServerResources { cpu_millicores: 100, memory_mib: 200 }), + kill_timeout_ms: 0, + webhook_url: Some("https://rivettest.free.beeceptor.com".to_string()), + tags: vec![(String::from("test"), String::from("123"))] + .into_iter() + .collect(), + args: Vec::new(), + environment: env, + image_id: Some(build_res.build_id.unwrap()), + network_mode: 0, + network_ports: ports, + }) + .await + .unwrap() + .server + .unwrap(); + + // TODO: Switch this + // let hostname = format!( + // "{}-{}.server.{}.rivet.run", + // server.server_id.unwrap(), + // "1234", + // faker_region.region_id.unwrap() + // ); + + let hostname = format!( + "{}-{}.lobby.{}.{}", + server.server_id.unwrap(), + "testing2", + faker_region.region_id.unwrap(), + util::env::domain_job().unwrap(), + ); + + // Async sleep for 5 seconds + tokio::time::sleep(std::time::Duration::from_secs(30)).await; + + // Echo body + let random_body = Uuid::new_v4().to_string(); + let client = reqwest::Client::new(); + let res = client + .post(format!("http://{hostname}")) + .body(random_body.clone()) + .send() + .await + .unwrap() + .error_for_status() + .unwrap(); + let res_text = res.text().await.unwrap(); + assert_eq!(random_body, res_text, "echoed wrong response"); + + // assert_eq!(game_res.game_id.unwrap(), server.game_id.unwrap().as_uuid()); +} diff --git a/svc/pkg/ds/ops/server-create/tests/print_test_data.rs b/svc/pkg/ds/ops/server-create/tests/print_test_data.rs new file mode 100644 index 0000000000..a2f0dcb9bc --- /dev/null +++ b/svc/pkg/ds/ops/server-create/tests/print_test_data.rs @@ -0,0 +1,212 @@ +use chirp_worker::prelude::*; +use proto::backend::{self, pkg::token}; + +#[worker_test] +async fn print_test_data(ctx: TestCtx) { + let game_res = op!([ctx] faker_game { + ..Default::default() + }) + .await + .unwrap(); + let game_id = game_res.game_id.unwrap(); + + let user_res = op!([ctx] faker_user { + ..Default::default() + }) + .await + .unwrap(); + let user_id = user_res.user_id.unwrap(); + + // Pick an existing cluster + let cluster_id = op!([ctx] cluster_list {}) + .await + .unwrap() + .cluster_ids + .first() + .unwrap() + .to_owned(); + + // Pick an existing datacenter + let datacenter_id = op!([ctx] cluster_datacenter_list { + cluster_ids: vec![cluster_id], + }) + .await + .unwrap() + .clusters + .first() + .unwrap() + .datacenter_ids + .first() + .unwrap() + .to_owned(); + + let build_res: backend::pkg::faker::build::Response = op!([ctx] faker_build { + game_id: Some(game_id), + image: backend::faker::Image::DsEcho as i32, + }) + .await + .unwrap(); + + // Create token + let token_res = op!([ctx] token_create { + token_config: Some(token::create::request::TokenConfig { + ttl: util::duration::days(90), + }), + issuer: "test".to_owned(), + kind: Some(token::create::request::Kind::New( + token::create::request::KindNew { entitlements: vec![proto::claims::Entitlement { + kind: Some(proto::claims::entitlement::Kind::GameService( + proto::claims::entitlement::GameService { + game_id: Some(game_id), + } + )), + }]}, + )), + label: Some("game_service".to_owned()), + ..Default::default() + }) + .await + .unwrap(); + + // Create token + let cloud_token_res = op!([ctx] token_create { + token_config: Some(token::create::request::TokenConfig { + ttl: util::duration::days(90), + }), + issuer: "test".to_owned(), + kind: Some(token::create::request::Kind::New( + token::create::request::KindNew { entitlements: vec![proto::claims::Entitlement { + kind: Some(proto::claims::entitlement::Kind::GameCloud( + proto::claims::entitlement::GameCloud { + game_id: Some(game_id), + } + )), + },proto::claims::Entitlement { + kind: Some(proto::claims::entitlement::Kind::User( + proto::claims::entitlement::User { + user_id: Some(user_id), + } + )), + }]}, + )), + label: Some("game_service".to_owned()), + ..Default::default() + }) + .await + .unwrap(); + + // Invalid token + let invalid_token = op!([ctx] token_create { + token_config: Some(token::create::request::TokenConfig { + ttl: util::duration::days(90), + }), + issuer: "test".to_owned(), + kind: Some(token::create::request::Kind::New( + token::create::request::KindNew { entitlements: vec![proto::claims::Entitlement { + kind: Some(proto::claims::entitlement::Kind::User( + proto::claims::entitlement::User { + user_id: Some(user_id), + } + )), + }]}, + )), + label: Some("game_service".to_owned()), + ..Default::default() + }) + .await + .unwrap(); + + tracing::info!( + ?game_id, + ?datacenter_id, + service_token = ?token_res.token.clone().unwrap().token, + cloud_token = ?cloud_token_res.token.clone().unwrap().token, + invalid_token = ?invalid_token.token.clone().unwrap().token, + build_id = ?build_res.build_id.unwrap(), + game_id = ?game_id, + "test data"); + // + // let runtime = Some( + // proto::backend::pkg::dynamic_servers::server_create::request::Runtime::DockerRuntime( + // proto::backend::ds::DockerRuntime { + // args: Vec::new(), + // environment: HashMap::new(), + // image_id: Some(build_res.build_id.unwrap()), + // network: Some(proto::backend::ds::DockerNetwork { + // mode: 0, + // ports: vec![( + // "testing2".to_string(), + // backend::ds::DockerPort { + // port: Some(28234), + // routing: Some( + // backend::ds::docker_port::Routing::GameGuard( + // backend::ds::DockerGameGuardRouting { + // protocol: 0, + // }, + // ), + // ), + // }, + // )] + // // Collect into hashmap + // .into_iter() + // .collect(), + // }), + // }, + // ), + // ); + // + // let faker_region = op!([ctx] faker_region {}).await.unwrap(); + // + // tracing::info!(?game_id); + // + // let server = op!([ctx] ds_server_create { + // game_id: Some(game_id), + // cluster_id: Some(cluster_id), + // datacenter_id: faker_region.region_id, + // resources: Some(proto::backend::ds::ServerResources { cpu_millicores: 100, memory_mib: 200 }), + // kill_timeout_ms: 0, + // metadata: HashMap::new(), + // runtime: runtime, + // }) + // .await + // .unwrap() + // .server + // .unwrap(); + // + // // TODO: Switch this + // // let hostname = format!( + // // "{}-{}.server.{}.rivet.run", + // // server.server_id.unwrap(), + // // "1234", + // // faker_region.region_id.unwrap() + // // ); + // + // let hostname = format!( + // "{}-{}.lobby.{}.{}", + // server.server_id.unwrap(), + // "testing2", + // faker_region.region_id.unwrap(), + // util::env::domain_job().unwrap(), + // ); + // + // // Async sleep for 5 seconds + // tokio::time::sleep(std::time::Duration::from_secs(30)).await; + // + // tracing::info!(?hostname, "hostest mostest"); + // + // // Echo body + // let random_body = Uuid::new_v4().to_string(); + // let client = reqwest::Client::new(); + // let res = client + // .post(format!("http://{hostname}")) + // .body(random_body.clone()) + // .send() + // .await + // .unwrap() + // .error_for_status() + // .unwrap(); + // let res_text = res.text().await.unwrap(); + // assert_eq!(random_body, res_text, "echoed wrong response"); + // + // assert_eq!(game_res.game_id.unwrap(), server.game_id.unwrap().as_uuid()); +} diff --git a/svc/pkg/ds/ops/server-delete/Cargo.toml b/svc/pkg/ds/ops/server-delete/Cargo.toml new file mode 100644 index 0000000000..fcac44d68d --- /dev/null +++ b/svc/pkg/ds/ops/server-delete/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ds-server-delete" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } +chirp-client = { path = "../../../../../lib/chirp/client" } +rivet-operation = { path = "../../../../../lib/operation/core" } +nomad-util = { path = "../../../../../lib/nomad-util" } +util-job = { package = "rivet-util-job", path = "../../../job/util" } +reqwest = "0.11" + +region-get = { path = "../../../region/ops/get" } + +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false + +[dependencies.nomad_client] +package = "nomad_client" +git = "https://github.com/rivet-gg/nomad-client" +rev = "abb66bf0c30c7ff5b0c695dae952481c33e538b5" # pragma: allowlist secret + +[dev-dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } diff --git a/svc/pkg/ds/ops/server-delete/Service.toml b/svc/pkg/ds/ops/server-delete/Service.toml new file mode 100644 index 0000000000..5262383369 --- /dev/null +++ b/svc/pkg/ds/ops/server-delete/Service.toml @@ -0,0 +1,10 @@ +[service] +name = "ds-server-delete" + +[runtime] +kind = "rust" + +[operation] + +[databases] +db-dynamic-servers = {} diff --git a/svc/pkg/ds/ops/server-delete/src/lib.rs b/svc/pkg/ds/ops/server-delete/src/lib.rs new file mode 100644 index 0000000000..0a8175680e --- /dev/null +++ b/svc/pkg/ds/ops/server-delete/src/lib.rs @@ -0,0 +1,351 @@ +use chirp_worker::prelude::*; +use futures_util::FutureExt; +use proto::backend::pkg::*; +use tokio::task; + +#[derive(Debug, sqlx::FromRow)] +struct UpdatedServer { + ds_server_id: Uuid, + ds_datacenter_id: Uuid, + alloc_id: String, + dispatched_job_id: String, +} + +#[operation(name = "ds-server-delete")] +pub async fn handle( + ctx: OperationContext, +) -> GlobalResult { + let server_id = unwrap_ref!(ctx.server_id).as_uuid(); + + let dynamic_server = rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { + let ctx = ctx.clone(); + + async move { + let dynamic_server = sql_fetch_one!( + [ctx, UpdatedServer, @tx tx] + " + UPDATE db_dynamic_servers.servers + SET delete_ts = $2 + WHERE + server_id = $1 + AND delete_ts IS NULL + RETURNING + server_id, + datacenter_id + server_nomad.nomad_dispatched_job_id, + server_nomad.nomad_alloc_id, + FROM + db_dynamic_servers.servers + JOIN + db_dynamic_servers.server_nomad + ON + db_dynamic_servers.servers.server_id = db_dynamic_servers.server_nomad.server_id + ", + server_id, + ctx.ts(), + ) + .await?; + + Ok(dynamic_server) + } + .boxed() + }) + .await?; + + // // NOTE: Idempotent + + // let run_id = unwrap_ref!(ctx.run_id).as_uuid(); + + // // Cleanup the job ASAP. + // // + // // This will also be called in `job-run-cleanup`, but this is idempotent. + // // msg!([ctx] job_run::msg::cleanup(run_id) { + // // run_id: Some(run_id.into()), + // // ..Default::default() + // // }) + // // .await?; + + // let run_id = unwrap_ref!(ctx.run_id).as_uuid(); + + // #[derive(Debug, sqlx::FromRow)] + // struct RunRow { + // region_id: Uuid, + // create_ts: i64, + // cleanup_ts: Option, + // } + + // let Some((run_row, run_meta_nomad_row)) = + // rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { + // let run_row = sql_fetch_optional!( + // [ctx, RunRow, @tx tx] + // " + // SELECT region_id, create_ts, cleanup_ts + // FROM db_job_state.runs + // WHERE run_id = $1 + // FOR UPDATE + // ", + // run_id, + // ) + // .await?; + // tracing::info!(?run_row, "run row"); + + // let Some(run_row) = run_row else { + // return Ok(None); + // }; + + // let run_meta_nomad_row = sql_fetch_optional!( + // [ctx, RunMetaNomadRow, @tx tx] + // " + // SELECT dispatched_job_id, node_id + // FROM db_job_state.run_meta_nomad + // WHERE run_id = $1 + // FOR UPDATE + // ", + // run_id, + // ) + // .await?; + // tracing::info!(?run_meta_nomad_row, "run meta row"); + + // // Check if job has been dispatched already + // if let Some(run_meta_nomad) = &run_meta_nomad_row { + // if run_meta_nomad.dispatched_job_id.is_none() + // && now - run_row.create_ts < util::duration::seconds(75) + // { + // // If the job is new, then there may be a race condition with + // // submitting the job to Nomad and writing the dispatched job ID to + // // the database. + // // + // // In this case, we'll fail and retry this later. + // // + // // There is a situation where the Nomad API returns an error and the + // // job ID is never written to the database. + // retry_bail!("potential race condition with starting nomad job") + // } + // } + + // tracing::info!("deleting run"); + // if run_row.cleanup_ts.is_none() { + // sql_execute!( + // [ctx, @tx tx] + // "UPDATE db_job_state.runs SET cleanup_ts = $2 WHERE run_id = $1", + // run_id, + // now, + // ) + // .await?; + // } + // }) + // .await? + // else { + // if ctx.req_dt() > util::duration::minutes(5) { + // tracing::error!("discarding stale message"); + // return Ok(()); + // } else { + // retry_bail!("run not found, may be race condition with insertion"); + // } + // }; + + // tracing::info!("removing from cache"); + // if matches!( + // run_meta_nomad_row, + // Some(RunMetaNomadRow { + // node_id: Some(_), + // .. + // }) + // ) { + // ctx.redis_job() + // .await? + // .hdel( + // util_job::key::proxied_ports(run_row.region_id), + // run_id.to_string(), + // ) + // .await?; + // } + + // let Some((run_row, run_meta_nomad_row)) = + // rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { + // let run_row = sql_fetch_optional!( + // [ctx, RunRow, @tx tx] + // " + // SELECT region_id, create_ts, stop_ts + // FROM db_job_state.runs + // WHERE run_id = $1 + // FOR UPDATE + // ", + // run_id, + // ) + // .await?; + // tracing::info!(?run_row, "fetched run"); + + // let Some(run_row) = run_row else { + // return Ok(None); + // }; + + // let run_meta_nomad_row = sql_fetch_optional!( + // [ctx, RunMetaNomadRow, @tx tx] + // " + // SELECT alloc_id, dispatched_job_id + // FROM db_job_state.run_meta_nomad + // WHERE run_id = $1 + // FOR UPDATE + // ", + // run_id, + // ) + // .await?; + // tracing::info!(?run_meta_nomad_row, "fetched run meta nomad"); + + // // Check if job has been dispatched already + // if let Some(run_meta_nomad) = &run_meta_nomad_row { + // if run_meta_nomad.dispatched_job_id.is_none() + // && now - run_row.create_ts < util::duration::seconds(75) + // { + // // If the job is new, then there may be a race condition with + // // submitting the job to Nomad and writing the dispatched job ID to + // // the database. + // // + // // In this case, we'll fail and retry this later. + // // + // // There is a situation where the Nomad API returns an error and the + // // job ID is never written to the database. + // retry_bail!("potential race condition with starting nomad job") + // } + // } + + // // We can't assume that started has been called here, so we can't fetch the alloc ID. + + // if run_row.stop_ts.is_none() { + // sql_execute!( + // [ctx, @tx tx] + // "UPDATE db_job_state.runs SET stop_ts = $2 WHERE run_id = $1", + // run_id, + // now, + // ) + // .await?; + // } + // }) + // .await? + // else { + // if ctx.req_dt() > util::duration::minutes(5) { + // tracing::error!("discarding stale message"); + // return Ok(()); + // } else { + // retry_bail!("run not found, may be race condition with insertion"); + // } + // }; + + // // HACK: Remove from proxied ports early. This also gets removed in job-run-cleanup, but that + // // may not run correclty if the dispatched job id is not set correctly. + // ctx.redis_job() + // .await? + // .hdel( + // util_job::key::proxied_ports(run_row.region_id), + // run_id.to_string(), + // ) + // .await?; + + // Get the region + let region_res = op!([ctx] region_get { + region_ids: vec![dynamic_server.ds_datacenter_id.into()], + }) + .await?; + let region = unwrap!(region_res.regions.first()); + + // TODO: Handle 404 safely. See RIV-179 + // Stop the job. + // + // Setting purge to false will change the behavior of the create poll + // functionality if the job dies immediately. You can set it to false to + // debug lobbies, but it's preferred to extract metadata from the + // job-run-stop lifecycle event. + + match nomad_client::apis::jobs_api::delete_job( + &nomad_util::new_config_from_env().unwrap(), + &dynamic_server.dispatched_job_id, + Some(®ion.nomad_region), + None, + None, + None, + Some(false), // TODO: Maybe change back to true for performance? + None, + ) + .await + { + Ok(_) => { + tracing::info!("job stopped"); + + task::spawn(async move { + // tokio::time::sleep(util_job::JOB_STOP_TIMEOUT).await; + + // tracing::info!(?dynamic_server.alloc_id, "manually killing allocation"); + + // if let Err(err) = { + // let local_var_client = &configuration.client; + + // let local_var_uri_str = format!( + // "{}/client/allocation/{alloc_id}/signal", + // configuration.base_path, + // alloc_id = nomad_client::apis::urlencode(dynamic_server.alloc_id), + // ); + // let mut local_var_req_builder = + // local_var_client.post(local_var_uri_str.as_str()); + + // if let Some(ref local_var_str) = namespace { + // local_var_req_builder = local_var_req_builder + // .query(&[("namespace", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_str) = region { + // local_var_req_builder = local_var_req_builder + // .query(&[("region", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_str) = index { + // local_var_req_builder = local_var_req_builder + // .query(&[("index", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_str) = wait { + // local_var_req_builder = local_var_req_builder + // .query(&[("wait", &local_var_str.to_string())]); + // } + // if let Some(ref local_var_user_agent) = configuration.user_agent { + // local_var_req_builder = local_var_req_builder + // .header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + // } + // local_var_req_builder = local_var_req_builder.json(&alloc_signal_request); + + // let local_var_req = local_var_req_builder.build()?; + // let local_var_resp = local_var_client.execute(local_var_req).await?; + + // let local_var_status = local_var_resp.status(); + // let local_var_content = local_var_resp.text().await?; + + // if !local_var_status.is_client_error() + // && !local_var_status.is_server_error() + // { + // Ok(()) + // } else { + // let local_var_entity: Option< + // nomad_client::apis::allocations_api::SignalAllocationError, + // > = serde_json::from_str(&local_var_content).ok(); + // let local_var_error = nomad_client::apis::ResponseContent { + // status: local_var_status, + // content: local_var_content, + // entity: local_var_entity, + // }; + // Err(nomad_client::apis::Error::ResponseError(local_var_error)) + // } + // } { + // tracing::warn!( + // ?err, + // ?alloc_id, + // "error while trying to manually kill allocation" + // ); + // } + }); + } + Err(err) => { + tracing::warn!(?err, "error thrown while stopping job, probably a 404, will continue as if stopped normally"); + } + } + + Ok(dynamic_servers::server_delete::Response { + server_id: Some(server_id.into()), + }) +} diff --git a/svc/pkg/ds/ops/server-delete/tests/integration.rs b/svc/pkg/ds/ops/server-delete/tests/integration.rs new file mode 100644 index 0000000000..d7d641e21e --- /dev/null +++ b/svc/pkg/ds/ops/server-delete/tests/integration.rs @@ -0,0 +1,6 @@ +use chirp_worker::prelude::*; + +#[worker_test] +async fn basic(ctx: TestCtx) { + // TODO: +} diff --git a/svc/pkg/ds/ops/server-get/Cargo.toml b/svc/pkg/ds/ops/server-get/Cargo.toml new file mode 100644 index 0000000000..9d1e375df4 --- /dev/null +++ b/svc/pkg/ds/ops/server-get/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "ds-server-get" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +chirp-client = { path = "../../../../../lib/chirp/client" } +rivet-operation = { path = "../../../../../lib/operation/core" } +util-ds = { package = "rivet-util-ds", path = "../../util" } + +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false + +[dev-dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } + +ds-server-create = { path = "../server-create" } +cluster-list = { path = "../../../cluster/ops/list" } +faker-build = { path = "../../../faker/ops/build" } +faker-game = { path = "../../../faker/ops/game" } +faker-region = { path = "../../../faker/ops/region" } + diff --git a/svc/pkg/ds/ops/server-get/Service.toml b/svc/pkg/ds/ops/server-get/Service.toml new file mode 100644 index 0000000000..4ca136b6ad --- /dev/null +++ b/svc/pkg/ds/ops/server-get/Service.toml @@ -0,0 +1,7 @@ +[service] +name = "ds-server-get" + +[runtime] +kind = "rust" + +[operation] diff --git a/svc/pkg/ds/ops/server-get/src/lib.rs b/svc/pkg/ds/ops/server-get/src/lib.rs new file mode 100644 index 0000000000..4319feb183 --- /dev/null +++ b/svc/pkg/ds/ops/server-get/src/lib.rs @@ -0,0 +1,290 @@ +use futures_util::FutureExt; +use proto::backend::{self, pkg::*}; +use rivet_operation::prelude::*; +use std::collections::HashMap; + +#[derive(sqlx::FromRow)] +struct Server { + server_id: Uuid, + game_id: Uuid, + datacenter_id: Uuid, + cluster_id: Uuid, + tags: serde_json::Value, + resources_cpu_millicores: i64, + resources_memory_mib: i64, + kill_timeout_ms: i64, + webhook_url: Option, + create_ts: i64, + destroy_ts: Option, + image_id: Uuid, + args: Vec, + network_mode: i64, + environment: serde_json::Value, +} + +#[derive(sqlx::FromRow)] +struct DockerPortProtocolGameGuard { + server_id: Uuid, + port_name: String, + port_number: i64, + gg_port: i64, + protocol: i64, +} + +#[derive(sqlx::FromRow)] +struct DockerPortHost { + server_id: Uuid, + port_name: String, + port_number: i64, + protocol: i64, +} + +#[derive(sqlx::FromRow)] +struct ServerNomad { + server_id: Uuid, + nomad_dispatched_job_id: Option, + nomad_alloc_id: Option, + nomad_node_id: Option, + nomad_node_name: Option, + nomad_node_public_ipv4: Option, + nomad_node_vlan_ipv4: Option, + nomad_alloc_plan_ts: Option, +} + +#[derive(sqlx::FromRow)] +struct ServerPort { + server_id: Uuid, + nomad_label: String, + nomad_ip: String, + nomad_source: i64, +} + +#[operation(name = "ds-server-get")] +pub async fn handle( + ctx: OperationContext, +) -> GlobalResult { + let server_ids = ctx + .server_ids + .iter() + .map(common::Uuid::as_uuid) + .collect::>(); + + let (server_rows, port_gg_rows, port_host_rows, server_nomad_rows, internal_port_rows) = tokio::try_join!( + sql_fetch_all!( + [ctx, Server] + " + SELECT + server_id, + game_id, + datacenter_id, + cluster_id, + tags, + resources_cpu_millicores, + resources_memory_mib, + kill_timeout_ms, + webhook_url, + create_ts, + destroy_ts, + image_id, + args, + network_mode, + environment + FROM + db_dynamic_servers.servers + WHERE + server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, DockerPortProtocolGameGuard] + " + SELECT + server_id, + port_name, + port_number, + gg_port, + protocol + FROM + db_dynamic_servers.docker_ports_protocol_game_guard + WHERE + server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, DockerPortHost] + " + SELECT + server_id, + port_name, + port_number, + protocol + FROM + db_dynamic_servers.docker_ports_host + WHERE + server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, ServerNomad] + " + SELECT + server_id, + nomad_dispatched_job_id, + nomad_alloc_id, + nomad_node_id, + nomad_node_name, + nomad_node_public_ipv4, + nomad_node_vlan_ipv4, + nomad_alloc_plan_ts + FROM + db_dynamic_servers.server_nomad + WHERE + server_id = ANY($1) + ", + &server_ids, + ), + sql_fetch_all!( + [ctx, ServerPort] + " + SELECT + server_id, + nomad_label, + nomad_ip, + nomad_source + FROM + db_dynamic_servers.internal_ports + WHERE + server_id = ANY($1) + ", + &server_ids, + ), + )?; + + let servers_proto = server_rows + .into_iter() + .map(|server| { + let tags: std::collections::HashMap = + serde_json::from_value(server.tags)?; + let environment: std::collections::HashMap = + serde_json::from_value(server.environment)?; + + let server_nomad = unwrap!(server_nomad_rows + .iter() + .find(|x| x.server_id == server.server_id)); + + // TODO: Handle timeout to let Traefik pull config + let is_connectable = server_nomad.nomad_alloc_plan_ts.is_some(); + + let ports = port_gg_rows + .iter() + .filter(|p| p.server_id == server.server_id) + .map(|gg_port| { + GlobalResult::Ok(( + gg_port.port_name.clone(), + create_port_gg(is_connectable, gg_port, server.datacenter_id)?, + )) + }) + .chain( + port_host_rows + .iter() + .filter(|p| p.server_id == server.server_id) + .map(|host_port| { + let internal_port = internal_port_rows.iter().find(|x| { + x.server_id == server.server_id + && x.nomad_label + == util_ds::format_nomad_port_label(&host_port.port_name) + }); + GlobalResult::Ok(( + host_port.port_name.clone(), + create_port_host(is_connectable, host_port, internal_port)?, + )) + }), + ) + .collect::>>()?; + + let server_proto = backend::ds::Server { + server_id: Some(server.server_id.into()), + game_id: Some(server.game_id.into()), + datacenter_id: Some(server.datacenter_id.into()), + cluster_id: Some(server.cluster_id.into()), + tags, + resources: Some(backend::ds::ServerResources { + cpu_millicores: server.resources_cpu_millicores.try_into()?, + memory_mib: server.resources_memory_mib.try_into()?, + }), + kill_timeout_ms: server.kill_timeout_ms, + webhook_url: server.webhook_url, + args: server.args, + environment, + image_id: Some(server.image_id.into()), + network_mode: server.network_mode.try_into()?, + network_ports: ports, + create_ts: server.create_ts, + destroy_ts: server.destroy_ts, + }; + + Ok(server_proto) + }) + .collect::>>()?; + + Ok(dynamic_servers::server_get::Response { + servers: servers_proto, + }) +} + +fn create_port_gg( + is_connectable: bool, + gg_port: &DockerPortProtocolGameGuard, + datacenter_id: Uuid, +) -> GlobalResult { + Ok(backend::ds::Port { + internal_port: Some(gg_port.port_number.try_into()?), + public_hostname: if is_connectable { + Some(util_ds::build_ds_hostname( + gg_port.server_id, + &gg_port.port_name, + datacenter_id, + )?) + } else { + None + }, + public_port: if is_connectable { + Some(gg_port.gg_port.try_into()?) + } else { + None + }, + routing: Some(backend::ds::port::Routing::GameGuard( + backend::ds::GameGuardRouting { + protocol: gg_port.protocol.try_into()?, + }, + )), + }) +} + +fn create_port_host( + is_connectable: bool, + host_port: &DockerPortHost, + internal_port: Option<&ServerPort>, +) -> GlobalResult { + Ok(backend::ds::Port { + internal_port: Some(host_port.port_number.try_into()?), + public_hostname: if is_connectable { + internal_port.map(|x| x.nomad_ip.clone()) + } else { + None + }, + public_port: if is_connectable { + internal_port + .map(|x| x.nomad_source.try_into()) + .transpose()? + } else { + None + }, + routing: Some(backend::ds::port::Routing::Host(backend::ds::HostRouting { + protocol: host_port.protocol.try_into()?, + })), + }) +} diff --git a/svc/pkg/ds/ops/server-get/tests/integration.rs b/svc/pkg/ds/ops/server-get/tests/integration.rs new file mode 100644 index 0000000000..e254cdd251 --- /dev/null +++ b/svc/pkg/ds/ops/server-get/tests/integration.rs @@ -0,0 +1,79 @@ +use std::collections::HashMap; + +use chirp_worker::prelude::*; +use proto::backend::{self, pkg::dynamic_servers}; + +#[worker_test] +async fn server_get(ctx: TestCtx) { + let game_res = op!([ctx] faker_game { + ..Default::default() + }) + .await + .unwrap(); + let game_id = game_res.game_id.unwrap(); + + // Pick an existing cluster + let cluster_id = op!([ctx] cluster_list {}) + .await + .unwrap() + .cluster_ids + .first() + .unwrap() + .to_owned(); + + let build_res: backend::pkg::faker::build::Response = op!([ctx] faker_build { + game_id: Some(game_id), + image: backend::faker::Image::DsEcho as i32, + }) + .await + .unwrap(); + + let faker_region = op!([ctx] faker_region {}).await.unwrap(); + + let env = vec![ + ("some_envkey_test".to_string(), "2134523".to_string()), + ( + "some_other_envkey_test".to_string(), + "4325234356".to_string(), + ), + ] + .into_iter() + .collect(); + + let ports = vec![( + "testing2".to_string(), + dynamic_servers::server_create::Port { + internal_port: Some(28234), + routing: Some(dynamic_servers::server_create::port::Routing::GameGuard( + backend::ds::GameGuardRouting { protocol: 0 }, + )), + }, + )] + // Collect into hashmap + .into_iter() + .collect(); + + let server = op!([ctx] ds_server_create { + game_id: Some(game_id), + cluster_id: Some(cluster_id), + datacenter_id: faker_region.region_id, + resources: Some(proto::backend::ds::ServerResources { cpu_millicores: 100, memory_mib: 200 }), + kill_timeout_ms: 0, + tags: HashMap::new(), + args: Vec::new(), + environment: env, + image_id: Some(build_res.build_id.unwrap()), + network_mode: 0, + network_ports: ports, + }) + .await + .unwrap() + .server + .unwrap(); + + let server_res = op!([ctx] ds_server_get { + server_ids: vec![server.server_id.unwrap()], + }) + .await + .unwrap(); +} diff --git a/svc/pkg/ds/ops/server-list-for-game/Cargo.toml b/svc/pkg/ds/ops/server-list-for-game/Cargo.toml new file mode 100644 index 0000000000..ffc8038c71 --- /dev/null +++ b/svc/pkg/ds/ops/server-list-for-game/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "ds-server-list-for-game" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +chirp-client = { path = "../../../../../lib/chirp/client" } +rivet-operation = { path = "../../../../../lib/operation/core" } +util-ds = { package = "rivet-util-ds", path = "../../util" } + +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false + +[dev-dependencies] +chirp-worker = { path = "../../../../../lib/chirp/worker" } diff --git a/svc/pkg/ds/ops/server-list-for-game/Service.toml b/svc/pkg/ds/ops/server-list-for-game/Service.toml new file mode 100644 index 0000000000..011905a347 --- /dev/null +++ b/svc/pkg/ds/ops/server-list-for-game/Service.toml @@ -0,0 +1,7 @@ +[service] +name = "ds-server-list-for-game" + +[runtime] +kind = "rust" + +[operation] diff --git a/svc/pkg/ds/ops/server-list-for-game/src/lib.rs b/svc/pkg/ds/ops/server-list-for-game/src/lib.rs new file mode 100644 index 0000000000..14d0062d7c --- /dev/null +++ b/svc/pkg/ds/ops/server-list-for-game/src/lib.rs @@ -0,0 +1,31 @@ +use proto::backend::pkg::*; +use rivet_operation::prelude::*; + +#[operation(name = "server-list-for-game")] +async fn handle( + ctx: OperationContext, +) -> GlobalResult { + let game_id = unwrap_ref!(ctx.game_id).as_uuid(); + + let server_ids = sql_fetch_all!( + [ctx, (Uuid,)] + " + SELECT + server_id + FROM + db_dynamic_servers.servers + WHERE + game_id = $1 + AND + tags @> $2 + ", + game_id, + serde_json::to_value(&ctx.tags)? + ) + .await? + .into_iter() + .map(|(id,)| common::Uuid::from(id)) + .collect::>(); + + Ok(dynamic_servers::server_list_for_game::Response { server_ids }) +} diff --git a/svc/pkg/ds/ops/server-list-for-game/tests/integration.rs b/svc/pkg/ds/ops/server-list-for-game/tests/integration.rs new file mode 100644 index 0000000000..6fc7435bec --- /dev/null +++ b/svc/pkg/ds/ops/server-list-for-game/tests/integration.rs @@ -0,0 +1,4 @@ +use std::collections::HashMap; + +// #[worker_test] +// async fn server_get(ctx: TestCtx) {} diff --git a/svc/pkg/ds/types/msg/ds-nomad-monitor-alloc-plan.proto b/svc/pkg/ds/types/msg/ds-nomad-monitor-alloc-plan.proto new file mode 100644 index 0000000000..5823ca713d --- /dev/null +++ b/svc/pkg/ds/types/msg/ds-nomad-monitor-alloc-plan.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package rivet.backend.pkg.ds.msg.ds_nomad_monitor_alloc_plan; + +import "proto/common.proto"; + +/// name = "msg-ds-ds-nomad-monitor-alloc-plan" +/// parameters = [ +/// { name = "user_id" }, +/// ] +message Message { + +} diff --git a/svc/pkg/ds/types/server-create.proto b/svc/pkg/ds/types/server-create.proto new file mode 100644 index 0000000000..7aa0c89dc1 --- /dev/null +++ b/svc/pkg/ds/types/server-create.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package rivet.backend.pkg.dynamic_servers.server_create; + +import "proto/common.proto"; +import "proto/backend/ds.proto"; + +message Request { + rivet.common.Uuid game_id = 1; + rivet.common.Uuid datacenter_id = 2; + rivet.common.Uuid cluster_id = 3; + map tags = 5; + rivet.backend.ds.ServerResources resources = 6; + int64 kill_timeout_ms = 7; + optional string webhook_url = 8; + rivet.common.Uuid image_id = 9; + repeated string args = 10; + rivet.backend.ds.NetworkMode network_mode = 11; + map environment = 12; + map network_ports = 13; +} + +message Response { + rivet.backend.ds.Server server = 1; +} + +message Port { + // Null when using host networking since one is automatially assigned + optional int32 internal_port = 1; + + oneof routing { + rivet.backend.ds.GameGuardRouting game_guard = 101; + rivet.backend.ds.HostRouting host = 102; + } +} + diff --git a/svc/pkg/ds/types/server-delete.proto b/svc/pkg/ds/types/server-delete.proto new file mode 100644 index 0000000000..9212894cb1 --- /dev/null +++ b/svc/pkg/ds/types/server-delete.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package rivet.backend.pkg.dynamic_servers.server_delete; + +import "proto/common.proto"; + +message Request { + rivet.common.Uuid server_id = 1; + int64 override_kill_timeout_ms = 2; +} + +message Response { + rivet.common.Uuid server_id = 1; +} diff --git a/svc/pkg/ds/types/server-get.proto b/svc/pkg/ds/types/server-get.proto new file mode 100644 index 0000000000..f40b860a89 --- /dev/null +++ b/svc/pkg/ds/types/server-get.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package rivet.backend.pkg.dynamic_servers.server_get; + +import "proto/common.proto"; +import "proto/backend/ds.proto"; + +message Request { + repeated rivet.common.Uuid server_ids = 1; +} + +message Response { + repeated rivet.backend.ds.Server servers = 1; +} diff --git a/svc/pkg/ds/types/server-list-for-game.proto b/svc/pkg/ds/types/server-list-for-game.proto new file mode 100644 index 0000000000..70568e6848 --- /dev/null +++ b/svc/pkg/ds/types/server-list-for-game.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package rivet.backend.pkg.dynamic_servers.server_list_for_game; + +import "proto/common.proto"; +import "proto/backend/ds.proto"; + +message Request { + rivet.common.Uuid game_id = 1; + map tags = 2; // JSON +} + +message Response { + repeated rivet.common.Uuid server_ids = 1; +} diff --git a/svc/pkg/ds/util/Cargo.toml b/svc/pkg/ds/util/Cargo.toml new file mode 100644 index 0000000000..57949c5e44 --- /dev/null +++ b/svc/pkg/ds/util/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "rivet-util-ds" +version = "0.1.0" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +bit-vec = "0.6" +chirp-client = { path = "../../../../lib/chirp/client" } +heck = "0.3" +http = "0.2" +rivet-operation = { path = "../../../../lib/operation/core" } +rivet-util = { path = "../../../../lib/util/core" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +strum = { version = "0.24", features = ["derive"] } +uuid = { version = "1", features = ["v4", "serde"] } + +ip-info = { path = "../../ip/ops/info" } +mm-lobby-list-for-user-id = { path = "../../mm/ops/lobby-list-for-user-id" } +region-get = { path = "../../region/ops/get" } +user-identity-get = { path = "../../user-identity/ops/get" } diff --git a/svc/pkg/ds/util/src/consts.rs b/svc/pkg/ds/util/src/consts.rs new file mode 100644 index 0000000000..10d67ca4a0 --- /dev/null +++ b/svc/pkg/ds/util/src/consts.rs @@ -0,0 +1,39 @@ +use rivet_util as util; + +pub const LOBBY_READY_TIMEOUT: i64 = util::duration::minutes(5); +pub const PLAYER_READY_TIMEOUT: i64 = util::duration::minutes(2); +pub const PLAYER_AUTO_REMOVE_TIMEOUT: i64 = util::duration::hours(8); + +pub const MIN_HOST_PORT: u16 = 26000; +pub const MAX_HOST_PORT: u16 = 31999; + +/// Constants used for mocking responses when using dev tokens. +pub const DEV_REGION_ID: &str = "dev-lcl"; +pub const DEV_PROVIDER_NAME: &str = "Development"; +pub const DEV_REGION_NAME: &str = "Local"; + +// Also see svc/mm-lobby-create/src/nomad_job.rs +pub const DEFAULT_ENV_KEYS: &[&str] = &[ + "RIVET_API_ENDPOINT", + "RIVET_CHAT_API_URL", + "RIVET_GROUP_API_URL", + "RIVET_IDENTITY_API_URL", + "RIVET_KV_API_URL", + "RIVET_MATCHMAKER_API_URL", + "RIVET_NAMESPACE_NAME", + "RIVET_NAMESPACE_ID", + "RIVET_VERSION_NAME", + "RIVET_VERSION_ID", + "RIVET_GAME_MODE_ID", + "RIVET_GAME_MODE_NAME", + "RIVET_LOBBY_ID", + "RIVET_TOKEN", + "RIVET_REGION_ID", + "RIVET_REGION_NAME", + "RIVET_MAX_PLAYERS_NORMAL", + "RIVET_MAX_PLAYERS_DIRECT", + "RIVET_MAX_PLAYERS_PARTY", + "RIVET_LOBBY_TOKEN", + "RIVET_LOBBY_GROUP_ID", + "RIVET_LOBBY_GROUP_NAME", +]; diff --git a/svc/pkg/ds/util/src/lib.rs b/svc/pkg/ds/util/src/lib.rs new file mode 100644 index 0000000000..3437193c79 --- /dev/null +++ b/svc/pkg/ds/util/src/lib.rs @@ -0,0 +1,35 @@ +pub mod consts; +pub mod test; + +use rivet_operation::prelude::*; + +pub fn build_ds_hostname( + server_id: Uuid, + port_name: &str, + datacenter_id: Uuid, +) -> GlobalResult { + // TODO: Change lobby -> server + Ok(format!( + "{}-{}.lobby.{}.{}", + server_id, + port_name, + datacenter_id, + unwrap!(rivet_util::env::domain_job()), + )) +} + +/// Formats the port label to be used in Nomad. +/// +/// Prefixing this port ensure that the user defined port names don't interfere +/// with other ports. +/// +/// See also SQL `concat` in `svc/api/traefik-provider/src/route/game_guard/dynamic_servers.rs`. +pub fn format_nomad_port_label(port_label: &str) -> String { + let snake_port_label = heck::SnakeCase::to_snake_case(port_label); + format!("ds_{snake_port_label}") +} + +pub const RUNC_SETUP_CPU: i32 = 50; +pub const RUNC_SETUP_MEMORY: i32 = 32; +pub const RUNC_CLEANUP_CPU: i32 = 50; +pub const RUNC_CLEANUP_MEMORY: i32 = 32; diff --git a/svc/pkg/ds/util/src/test.rs b/svc/pkg/ds/util/src/test.rs new file mode 100644 index 0000000000..8de7466450 --- /dev/null +++ b/svc/pkg/ds/util/src/test.rs @@ -0,0 +1 @@ +pub const TIER_NAME_ID: &str = "basic-1d16"; diff --git a/svc/pkg/ds/worker/Cargo.toml b/svc/pkg/ds/worker/Cargo.toml new file mode 100644 index 0000000000..c6e0272a81 --- /dev/null +++ b/svc/pkg/ds/worker/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "ds-worker" +version = "0.0.1" +edition = "2021" +authors = ["Rivet Gaming, LLC "] +license = "Apache-2.0" + +[dependencies] +chirp-client = { path = "../../../../lib/chirp/client" } +chirp-worker = { path = "../../../../lib/chirp/worker" } +chrono = "0.4" +lazy_static = "1.4.0" +nomad-util = { path = "../../../../lib/nomad-util" } +rivet-convert = { path = "../../../../lib/convert" } +rivet-health-checks = { path = "../../../../lib/health-checks" } +rivet-metrics = { path = "../../../../lib/metrics" } +rivet-runtime = { path = "../../../../lib/runtime" } +serde = { version = "1.0", features = ["derive"] } +util-job = { package = "rivet-util-job", path = "../../job/util" } +reqwest = { version = "0.12", features = ["json"] } +region-get = { path = "../../region/ops/get" } +rivet-api = { path = "../../../../sdks/full/rust" } + + + +mm-lobby-get = { path = "../../mm/ops/lobby-get" } +job-run-get = { path = "../../job-run/ops/get" } +ds-server-get = { path = "../ops/server-get" } +mm-config-version-get = { path = "../../mm-config/ops/version-get" } + +[dev-dependencies] +chirp-worker = { path = "../../../../lib/chirp/worker" } + +[dependencies.sqlx] +git = "https://github.com/rivet-gg/sqlx" +rev = "08d6e61aa0572e7ec557abbedb72cebb96e1ac5b" +default-features = false + +[dependencies.nomad_client] +package = "nomad_client" +git = "https://github.com/rivet-gg/nomad-client" +rev = "abb66bf0c30c7ff5b0c695dae952481c33e538b5" # pragma: allowlist secret diff --git a/svc/pkg/ds/worker/Service.toml b/svc/pkg/ds/worker/Service.toml new file mode 100644 index 0000000000..1ac4bc9454 --- /dev/null +++ b/svc/pkg/ds/worker/Service.toml @@ -0,0 +1,8 @@ +[service] +name = "ds-worker" + +[runtime] +kind = "rust" + +[consumer] + diff --git a/svc/pkg/ds/worker/src/lib.rs b/svc/pkg/ds/worker/src/lib.rs new file mode 100644 index 0000000000..3719b10aa8 --- /dev/null +++ b/svc/pkg/ds/worker/src/lib.rs @@ -0,0 +1 @@ +pub mod workers; diff --git a/svc/pkg/ds/worker/src/workers/mod.rs b/svc/pkg/ds/worker/src/workers/mod.rs new file mode 100644 index 0000000000..0e6d4981bf --- /dev/null +++ b/svc/pkg/ds/worker/src/workers/mod.rs @@ -0,0 +1,104 @@ +use proto::backend::pkg::nomad; +use rivet_convert::ApiTryFrom; +use serde_json::Value; + +pub mod nomad_monitor_alloc_plan; +pub mod nomad_monitor_alloc_update; +pub mod nomad_monitor_eval_update; + +chirp_worker::workers![ + nomad_monitor_alloc_plan, + nomad_monitor_alloc_update, + nomad_monitor_eval_update +]; + +lazy_static::lazy_static! { + pub static ref NEW_NOMAD_CONFIG: nomad_client::apis::configuration::Configuration = + nomad_util::new_config_from_env().unwrap(); +} + +pub async fn webhook_call( + ctx: &OperationContext, + alloc_id: String, +) -> GlobalResult<()> { + let ctx = ctx.clone(); + tokio::spawn(async move { + // Get the server from the database. If it has a webhook_url, send all + // of the info about the server to it + tracing::error!(?alloc_id, "Checking Alloc ID"); + + let server_id = match sql_fetch_optional!( + [ctx, (Uuid,)] + " + SELECT + server_id + FROM + db_dynamic_servers.server_nomad + WHERE + nomad_alloc_id = $1 + ", + alloc_id, + ) + .await + { + Ok(Some(row)) => row, + Err(err) => { + tracing::error!(?err, "Could not find server from Nomad alloc"); + return; + } + _ => { + tracing::error!("Could not find server from Nomad alloc"); + return; + } + } + .0; + + let server = match op!([ctx] ds_server_get { + server_ids: vec![server_id.into()], + }) + .await + { + Ok(server_res) => match server_res.servers.first() { + Some(server) => server.to_owned(), + None => { + tracing::error!("Could not get server from database"); + return; + } + }, + Err(err) => { + tracing::error!(?err, "Could not get server from database"); + return; + } + }; + + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(15)) + .build() + .expect("Failed to build client"); + + let webhook_url = match &server.webhook_url { + Some(url) => url, + None => { + return; + } + }; + + // Example of a JSON payload + let payload = serde_json::json!({ + "message": match rivet_api::models::ServersServer::api_try_from(server.clone()) { + Ok(server) => server, + Err(err) => { + tracing::error!(?err, "Could not convert server to API"); + return; + } + }, + }); + + match client.post(webhook_url).json(&payload).send().await { + Ok(response) => tracing::info!(?response, "Sent webhook"), + Err(err) => tracing::warn!(?err, "Issue sending webhook"), + }; + }); + + Ok(()) +} diff --git a/svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_plan.rs b/svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_plan.rs new file mode 100644 index 0000000000..003a21a106 --- /dev/null +++ b/svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_plan.rs @@ -0,0 +1,266 @@ +use chirp_worker::prelude::*; +use proto::backend::{self, pkg::*}; +use redis::AsyncCommands; +use serde::Deserialize; + +use crate::workers::NEW_NOMAD_CONFIG; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct PlanResult { + allocation: nomad_client::models::Allocation, +} + +#[derive(Debug, sqlx::FromRow)] +struct RunRow { + server_id: Uuid, + datacenter_id: Uuid, + stop_ts: Option, + nomad_alloc_plan_ts: Option, // this was nomad_plan_ts +} + +#[derive(Debug, sqlx::FromRow)] +struct ProxiedPort { + target_nomad_port_label: Option, + ingress_port: i64, + ingress_hostnames: Vec, + proxy_protocol: i64, + ssl_domain_mode: i64, +} + +#[derive(Clone)] +struct RunData { + job_id: String, + alloc_id: String, + nomad_node_id: String, + nomad_node_name: String, + nomad_node_public_ipv4: String, + nomad_node_vlan_ipv4: String, + ports: Vec, +} + +#[worker(name = "ds-nomad-monitor-alloc-plan")] +async fn worker( + ctx: &OperationContext, +) -> GlobalResult<()> { + let PlanResult { allocation: alloc } = serde_json::from_str::(&ctx.payload_json)?; + tracing::info!(?alloc, "from noamddd"); + + let job_id = unwrap_ref!(alloc.job_id, "alloc has no job id"); + let alloc_id = unwrap_ref!(alloc.ID); + let nomad_node_id = unwrap_ref!(alloc.node_id, "alloc has no node id"); + let _nomad_node_name = unwrap_ref!(alloc.node_id, "alloc has no node name"); + + // Fetch node metadata + let node = nomad_client::apis::nodes_api::get_node( + &NEW_NOMAD_CONFIG, + nomad_node_id, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ) + .await?; + let mut meta = unwrap!(node.meta); + + // Read ports + let mut ports = Vec::new(); + let alloc_resources = unwrap_ref!(alloc.resources); + if let Some(networks) = &alloc_resources.networks { + for network in networks { + let network_ip = unwrap_ref!(network.IP); + + if let Some(dynamic_ports) = &network.dynamic_ports { + for port in dynamic_ports { + // Don't share connect proxy ports + let label = unwrap_ref!(port.label); + ports.push(backend::job::Port { + label: label.clone(), + source: *unwrap_ref!(port.value) as u32, + target: *unwrap_ref!(port.to) as u32, + ip: network_ip.clone(), + }); + } + } + } + } else { + tracing::info!("no network returned"); + } + + // This works + tracing::info!(?ports, "found protsadf"); + + // {"timestamp":"2024-06-28T01:43:24.930496Z","level":"INFO","fields":{"message":"found protsadf","ports":"[Port { label: \"game_testing2\", source: 20202, target: 0, ip: \"10.0.50.97\" }]"},"target":"ds_worker::workers::nomad_monitor_alloc_plan","spans":[{"ray_id":"1c8bfa81-3c80-4a2c-ab7c-2655f6c6a665","req_id":"a44227ad-4f1a-44b8-b4d0-7746dd8a622e","worker_name":"monolith-worker--ds-nomad-monitor-alloc-plan","name":"handle_req"},{"name":"ds-nomad-monitor-alloc-plan","tick_index":0,"name":"handle"}]} + + // Fetch the run + // + // Backoff mitigates race condition with job-run-create not having inserted + // the dispatched_job_id yet. + let run_data: RunData = RunData { + job_id: job_id.clone(), + alloc_id: alloc_id.clone(), + nomad_node_id: nomad_node_id.clone(), + nomad_node_name: unwrap!(node.name), + nomad_node_public_ipv4: unwrap!(meta.remove("network-public-ipv4")), + nomad_node_vlan_ipv4: unwrap!(meta.remove("network-vlan-ipv4")), + ports: ports.clone(), + }; + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + let db_output = rivet_pools::utils::crdb::tx(&ctx.crdb().await?, |tx| { + let ctx = ctx.clone(); + let now = ctx.ts(); + let run_data = run_data.clone(); + Box::pin(update_db(ctx, tx, now, run_data)) + }) + .await?; + + // Check if run found + let Some(DbOutput { + server_id, + datacenter_id, + stop_ts, + }) = db_output + else { + if ctx.req_dt() > util::duration::minutes(5) { + tracing::error!("discarding stale message"); + return Ok(()); + } else { + retry_bail!("run not found, may be race condition with insertion"); + } + }; + + tracing::info!(%job_id, %server_id, "updated run"); + + Ok(()) +} + +#[derive(Debug)] +struct DbOutput { + server_id: Uuid, + datacenter_id: Uuid, + stop_ts: Option, +} + +/// Returns `None` if the run could not be found. +#[tracing::instrument(skip_all)] +async fn update_db( + ctx: OperationContext, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + now: i64, + RunData { + job_id, + alloc_id, + nomad_node_id, + nomad_node_name, + nomad_node_public_ipv4, + nomad_node_vlan_ipv4, + ports, + }: RunData, +) -> GlobalResult> { + tracing::info!(?ports, "got the portdatda"); + + let run_row = sql_fetch_optional!( + [ctx, RunRow, @tx tx] + " + SELECT + servers.server_id, + servers.datacenter_id, + servers.stop_ts, + server_nomad.nomad_alloc_plan_ts + FROM + db_dynamic_servers.server_nomad + INNER JOIN + db_dynamic_servers.servers + ON + servers.server_id = server_nomad.server_id + WHERE + server_nomad.nomad_dispatched_job_id = $1 + FOR UPDATE OF + server_nomad + ", + &job_id, + ) + .await?; + tracing::info!(?job_id, "checking jobid"); + + tracing::info!(?run_row, "ayy event2a"); + + // Check if run found + let run_row = if let Some(run_row) = run_row { + run_row + } else { + tracing::info!("caught race condition with ds-server-create"); + return Ok(None); + }; + let server_id = run_row.server_id; + + tracing::info!("ayy event2b"); + + // Write run meta on first plan + if run_row.nomad_alloc_plan_ts.is_none() { + // Write alloc information + sql_execute!( + [ctx, @tx tx] + " + UPDATE + db_dynamic_servers.server_nomad + SET + nomad_alloc_id = $2, + nomad_alloc_plan_ts = $3, + nomad_node_id = $4, + nomad_node_name = $5, + nomad_node_public_ipv4 = $6, + nomad_node_vlan_ipv4 = $7 + WHERE + server_id = $1 + ", + server_id, + &alloc_id, + now, + &nomad_node_id, + &nomad_node_name, + &nomad_node_public_ipv4, + &nomad_node_vlan_ipv4, + ) + .await?; + + tracing::info!(?ports, "got ds ports"); + + // Save the ports to the db + for port in &ports { + tracing::info!(%server_id, label = %port.label, source = port.source, target = port.target, ip = %port.ip, "inserting ds port"); + sql_execute!( + [ctx, @tx tx] + " + INSERT INTO + db_dynamic_servers.server_ports ( + server_id, + nomad_label, + nomad_source, + nomad_ip + ) + VALUES + ($1, $2, $3, $4) + ", + server_id, + &port.label, + port.source as i64, + &port.ip, + ) + .await?; + } + } + + tracing::info!("ayy event2c"); + + Ok(Some(DbOutput { + server_id, + datacenter_id: run_row.datacenter_id, + stop_ts: run_row.stop_ts, + })) +} diff --git a/svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_update.rs b/svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_update.rs new file mode 100644 index 0000000000..fa183b90e3 --- /dev/null +++ b/svc/pkg/ds/worker/src/workers/nomad_monitor_alloc_update.rs @@ -0,0 +1,255 @@ +use chirp_worker::prelude::*; +use proto::backend::pkg::*; +use serde::Deserialize; +use sqlx; + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct AllocationUpdated { + allocation: nomad_client::models::Allocation, +} + +#[derive(Debug, Copy, Clone)] +enum TaskState { + Pending, + Running, + Dead, +} + +#[worker(name = "ds-run-nomad-monitor-alloc-update")] +async fn worker( + ctx: &OperationContext, +) -> GlobalResult<()> { + let _crdb = ctx.crdb().await?; + + let AllocationUpdated { allocation: alloc } = serde_json::from_str(&ctx.payload_json)?; + let alloc_state_json = serde_json::to_value(&alloc)?; + + let alloc_id = unwrap_ref!(alloc.ID); + let eval_id = unwrap_ref!(alloc.eval_id, "alloc has no eval"); + let job_id = unwrap_ref!(alloc.job_id); + let client_status = unwrap_ref!(alloc.client_status); + let task_states = unwrap_ref!(alloc.task_states); + + if !util_job::is_nomad_job_run(job_id) { + tracing::info!(%job_id, "disregarding event"); + return Ok(()); + } + + // Get the main task by finding the task that is not the run cleanup task + let main_task = task_states + .iter() + .filter(|(k, _)| k.as_str() == util_job::RUN_MAIN_TASK_NAME) + .map(|(_, v)| v) + .next(); + let main_task = unwrap!(main_task, "could not find main task"); + let main_task_state_raw = unwrap_ref!(main_task.state); + + tracing::info!( + ?client_status, + ?alloc_id, + ?eval_id, + ?job_id, + ?main_task_state_raw, + main_task_events = ?main_task.events, + "alloc updated" + ); + + let main_task_state = match (main_task_state_raw.as_str(), client_status.as_str()) { + ("pending", _) => TaskState::Pending, + ("running", _) => TaskState::Running, + ("dead", _) | (_, "failed" | "lost") => TaskState::Dead, + _ => { + tracing::error!(?main_task_state_raw, ?client_status, "unknown task state"); + return Ok(()); + } + }; + + match main_task_state { + TaskState::Pending => { + let run_row = sql_fetch_optional!( + [ctx, (Uuid,)] + " + UPDATE + db_dynamic_servers.server_nomad + SET + nomad_alloc_state = $2 + WHERE + nomad_dispatched_job_id = $1 RETURNING server_id + ", + job_id, + &alloc_state_json, + ) + .await?; + + if run_row.is_none() { + if ctx.req_dt() > util::duration::minutes(5) { + tracing::error!("discarding stale message"); + return Ok(()); + } else { + retry_bail!("run not found, may be race condition with insertion"); + } + }; + + crate::workers::webhook_call(ctx, alloc_id.to_string()).await?; + + Ok(()) + } + TaskState::Running => { + let run_row = sql_fetch_optional!( + [ctx, (Uuid, Option)] + " + WITH select_server AS ( + SELECT + servers.server_id, + servers.start_ts + FROM + db_dynamic_servers.server_nomad + INNER JOIN db_dynamic_servers.servers ON servers.server_id = server_nomad.server_id + WHERE + nomad_dispatched_job_id = $1 + ), + _update_servers AS ( + UPDATE + db_dynamic_servers.servers + SET + start_ts = $2 + FROM + select_server + WHERE + servers.server_id = select_server.server_id + AND servers.start_ts IS NULL RETURNING 1 + ), + _update_server_nomad AS ( + UPDATE + db_dynamic_servers.server_nomad + SET + nomad_alloc_state = $3 + FROM + select_server + WHERE + server_nomad.server_id = select_server.server_id RETURNING 1 + ) + SELECT + * + FROM + select_server + ", + job_id, + ctx.ts(), + &alloc_state_json, + ) + .await?; + + let Some((run_id, start_ts)) = run_row else { + if ctx.req_dt() > util::duration::minutes(5) { + tracing::error!("discarding stale message"); + return Ok(()); + } else { + retry_bail!("run not found, may be race condition with insertion"); + } + }; + + crate::workers::webhook_call(ctx, alloc_id.to_string()).await?; + + if start_ts.is_none() { + tracing::info!("run started"); + + msg!([ctx] job_run::msg::started(run_id) { + run_id: Some(run_id.into()), + }) + .await?; + + Ok(()) + } else { + tracing::info!("run already started"); + + Ok(()) + } + } + TaskState::Dead => { + let run_row = sql_fetch_optional!( + [ctx, (Uuid, Option)] + r#" + WITH select_server AS ( + SELECT + servers.server_id, + servers.finish_ts + FROM + db_dynamic_servers.server_nomad + INNER JOIN db_dynamic_servers.servers ON servers.server_id = server_nomad.server_id + WHERE + nomad_dispatched_job_id = $1 + ), + _update_servers AS ( + UPDATE + db_dynamic_servers.servers + SET + -- If the job stops immediately, the task state will never be "running" so we need to + -- make sure start_ts is set here as well + start_ts = COALESCE(start_ts, $2), + finish_ts = $2 + FROM + select_server + WHERE + servers.server_id = select_server.server_id + AND servers.finish_ts IS NULL RETURNING 1 + ), + _update_server_nomad AS ( + UPDATE + db_dynamic_servers.server_nomad + SET + nomad_alloc_state = $3 + FROM + select_server + WHERE + server_nomad.server_id = select_server.server_id RETURNING 1 + ) + SELECT + * + FROM + select_server + "#, + job_id, + ctx.ts(), + &alloc_state_json, + ) + .await?; + + let Some((run_id, finish_ts)) = run_row else { + if ctx.req_dt() > util::duration::minutes(5) { + tracing::error!("discarding stale message"); + return Ok(()); + } else { + retry_bail!("run not found, may be race condition with insertion"); + } + }; + + crate::workers::webhook_call(ctx, alloc_id.to_string()).await?; + + if finish_ts.is_none() { + tracing::info!("run finished"); + + // Publish message + // + // It's fine if this is called multiple times. The operation is + // idempotent and it's better to ensure the job gets cleaned up + // rather than forgotten. + msg!([ctx] job_run::msg::cleanup(run_id) { + run_id: Some(run_id.into()), + ..Default::default() + }) + .await?; + msg!([ctx] job_run::msg::finished(run_id) { + run_id: Some(run_id.into()), + }) + .await?; + + Ok(()) + } else { + tracing::info!("run already finished"); + Ok(()) + } + } + } +} diff --git a/svc/pkg/ds/worker/src/workers/nomad_monitor_eval_update.rs b/svc/pkg/ds/worker/src/workers/nomad_monitor_eval_update.rs new file mode 100644 index 0000000000..d64c4ae89d --- /dev/null +++ b/svc/pkg/ds/worker/src/workers/nomad_monitor_eval_update.rs @@ -0,0 +1,174 @@ +use self::sqlx; +use chirp_worker::prelude::*; +use proto::backend::pkg::*; +use serde::Deserialize; + +use crate::workers::NEW_NOMAD_CONFIG; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct PlanResult { + evaluation: nomad_client::models::Evaluation, +} + +#[derive(Debug, Copy, Clone)] +enum EvalStatus { + Failed, + Complete, +} + +#[derive(Debug, sqlx::FromRow)] +struct RunRow { + run_id: Uuid, + region_id: Uuid, + eval_plan_ts: Option, +} + +#[worker(name = "ds-run-nomad-monitor-eval-update")] +async fn worker( + ctx: &OperationContext, +) -> GlobalResult<()> { + let _crdb = ctx.crdb().await?; + + let payload_value = serde_json::from_str::(&ctx.payload_json)?; + let PlanResult { evaluation: eval } = serde_json::from_str::(&ctx.payload_json)?; + + let job_id = unwrap_ref!(eval.job_id, "eval has no job id"); + let eval_status_raw = unwrap_ref!(eval.status).as_str(); + + // We can't decode this with serde, so manually deserialize the response + let eval_value = unwrap!(payload_value.get("Evaluation")); + + if !util_job::is_nomad_job_run(job_id) { + tracing::info!(%job_id, "disregarding event"); + return Ok(()); + } + + // HACK: Serde isn't deserializing this correctly for some reason so + // we use raw JSON + // Filter out data we need from the event. Ignore events we don't care about + // before we touch the database. + let failed_tg_allocs = eval_value.get("FailedTGAllocs").and_then(|x| x.as_object()); + let eval_status = match (eval_status_raw, &failed_tg_allocs) { + ("complete", Some(failed_tg_allocs)) if !failed_tg_allocs.is_empty() => { + let failed_tg_allocs_str = + serde_json::to_string(&failed_tg_allocs).unwrap_or("?".to_string()); + tracing::warn!(%job_id, failed_tg_allocs = %failed_tg_allocs_str, "eval failed"); + + EvalStatus::Failed + } + ("complete", _) => EvalStatus::Complete, + _ => { + tracing::info!( + %job_id, + ?eval_status_raw, + ?failed_tg_allocs, + "ignoring status" + ); + return Ok(()); + } + }; + + // Fetch and update the run + let run_row = sql_fetch_optional!( + [ctx, RunRow] + " + WITH + select_run AS ( + SELECT runs.run_id, runs.region_id, run_meta_nomad.eval_plan_ts + FROM db_job_state.run_meta_nomad + INNER JOIN db_job_state.runs ON runs.run_id = run_meta_nomad.run_id + WHERE dispatched_job_id = $1 + ), + _update AS ( + UPDATE db_job_state.run_meta_nomad + SET eval_plan_ts = $2 + FROM select_run + WHERE + run_meta_nomad.run_id = select_run.run_id AND + run_meta_nomad.eval_plan_ts IS NULL + RETURNING 1 + ) + SELECT * FROM select_run + ", + job_id, + ctx.ts(), + ) + .await?; + + // Check if run found + let Some(run_row) = run_row else { + if ctx.req_dt() > util::duration::minutes(5) { + tracing::error!("discarding stale message"); + return Ok(()); + } else { + retry_bail!("run not found, may be race condition with insertion"); + } + }; + let run_id = run_row.run_id; + + if let Some(eval_plan_ts) = run_row.eval_plan_ts { + tracing::info!(?eval_plan_ts, "eval already planned"); + return Ok(()); + } + + tracing::info!(%job_id, %run_id, ?eval_status, "updated run"); + + match eval_status { + EvalStatus::Failed => { + tracing::info!(%run_id, "eval failed"); + + let error_code = job_run::msg::fail::ErrorCode::NomadEvalPlanFailed; + tracing::warn!(%run_id, ?error_code, "job run fail"); + msg!([ctx] job_run::msg::fail(run_id) { + run_id: Some(run_id.into()), + error_code: error_code as i32, + }) + .await?; + + // Get the region + let region_res = op!([ctx] region_get { + region_ids: vec![run_row.region_id.into()], + }) + .await?; + let region = unwrap!(region_res.regions.first()); + + // Stop the job from attempting to run on another node. This will + // be called in job-run-stop too, but we want to catch this earlier. + match nomad_client::apis::jobs_api::delete_job( + &NEW_NOMAD_CONFIG, + job_id, + Some(®ion.nomad_region), + None, + None, + None, + Some(false), + None, + ) + .await + { + Ok(_) => tracing::info!("job stopped"), + Err(err) => { + tracing::warn!(?err, "error thrown while stopping job, probably a 404, will continue as if stopped normally"); + } + } + + // Cleanup the job + msg!([ctx] job_run::msg::stop(run_id) { + run_id: Some(run_id.into()), + ..Default::default() + }) + .await?; + } + EvalStatus::Complete => { + tracing::info!(%run_id, "eval complete"); + + msg!([ctx] job_run::msg::eval_complete(run_id) { + run_id: Some(run_id.into()), + }) + .await?; + } + } + + Ok(()) +} diff --git a/svc/pkg/ds/worker/tests/common.rs b/svc/pkg/ds/worker/tests/common.rs new file mode 100644 index 0000000000..26b8b28190 --- /dev/null +++ b/svc/pkg/ds/worker/tests/common.rs @@ -0,0 +1,265 @@ +// use std::collections::HashMap; + +// use chirp_worker::prelude::*; +// use proto::backend::{self, pkg::*}; + +// pub struct Setup { +// pub namespace_id: Uuid, +// pub lobby_group_id_bridge: Uuid, +// pub lobby_group_id_host: Uuid, +// pub region_id: Uuid, +// pub region: backend::region::Region, +// pub host_port_http: u16, +// pub host_port_tcp: u16, +// pub host_port_udp: u16, +// } + +// impl Setup { +// pub async fn init(ctx: &TestCtx) -> Self { +// let region_res = op!([ctx] faker_region {}).await.unwrap(); +// let region_id = region_res.region_id.as_ref().unwrap().as_uuid(); + +// let game_res = op!([ctx] faker_game { +// skip_namespaces_and_versions: true, +// ..Default::default() +// }) +// .await +// .unwrap(); + +// let build_res = op!([ctx] faker_build { +// game_id: game_res.game_id, +// image: backend::faker::Image::MmLobbyEcho as i32, +// }) +// .await +// .unwrap(); + +// // Pick host ports to connect to +// let host_port_http = rand::thread_rng().gen_range(26000..27000); +// let host_port_tcp = rand::thread_rng().gen_range(26000..27000); +// let host_port_udp = rand::thread_rng().gen_range(26000..27000); + +// let game_version_res = op!([ctx] faker_game_version { +// game_id: game_res.game_id, +// override_lobby_groups: Some(faker::game_version::request::OverrideLobbyGroups { +// lobby_groups: vec![backend::matchmaker::LobbyGroup { +// name_id: "test-1".into(), + +// regions: vec![backend::matchmaker::lobby_group::Region { +// region_id: Some(region_id.into()), +// tier_name_id: util_ds::test::TIER_NAME_ID.to_owned(), +// idle_lobbies: Some(backend::matchmaker::lobby_group::IdleLobbies { +// min_idle_lobbies: 0, +// // Don't auto-destroy lobbies from tests +// max_idle_lobbies: 32, +// }), +// }], +// max_players_normal: 8, +// max_players_direct: 10, +// max_players_party: 12, +// listable: true, +// taggable: false, +// allow_dynamic_max_players: false, + +// runtime: Some(backend::matchmaker::lobby_runtime::Docker { +// build_id: build_res.build_id, +// args: Vec::new(), +// env_vars: vec![ +// backend::matchmaker::lobby_runtime::EnvVar { +// key: "HELLO".into(), +// value: "world".into(), +// }, +// ], +// network_mode: backend::matchmaker::lobby_runtime::NetworkMode::Bridge as i32, +// ports: vec![ +// backend::matchmaker::lobby_runtime::Port { +// label: "test-http".into(), +// target_port: Some(8001), +// port_range: None, +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Http as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, +// }, +// backend::matchmaker::lobby_runtime::Port { +// label: "test-tcp".into(), +// target_port: Some(8002), +// port_range: None, +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Tcp as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, +// }, +// backend::matchmaker::lobby_runtime::Port { +// label: "test-udp".into(), +// target_port: Some(8003), +// port_range: None, +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Udp as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, +// }, +// ], + +// }.into()), + +// actions: None, +// }, +// backend::matchmaker::LobbyGroup { +// name_id: "test-2".into(), + +// regions: vec![backend::matchmaker::lobby_group::Region { +// region_id: Some(region_id.into()), +// tier_name_id: util_ds::test::TIER_NAME_ID.to_owned(), +// idle_lobbies: Some(backend::matchmaker::lobby_group::IdleLobbies { +// min_idle_lobbies: 0, +// // See above +// max_idle_lobbies: 32, +// }), +// }], +// max_players_normal: 8, +// max_players_direct: 10, +// max_players_party: 12, +// listable: true, +// taggable: false, +// allow_dynamic_max_players: false, + +// runtime: Some(backend::matchmaker::lobby_runtime::Docker { +// build_id: build_res.build_id, +// args: Vec::new(), +// env_vars: vec![ +// backend::matchmaker::lobby_runtime::EnvVar { +// key: "HELLO".into(), +// value: "world".into(), +// }, +// backend::matchmaker::lobby_runtime::EnvVar { +// key: "HOST_PORT_HTTP".into(), +// value: host_port_http.to_string(), +// }, +// backend::matchmaker::lobby_runtime::EnvVar { +// key: "HOST_PORT_TCP".into(), +// value: host_port_tcp.to_string(), +// }, +// backend::matchmaker::lobby_runtime::EnvVar { +// key: "HOST_PORT_UDP".into(), +// value: host_port_udp.to_string(), +// }, +// ], +// network_mode: backend::matchmaker::lobby_runtime::NetworkMode::Host as i32, +// ports: vec![ +// // Game Guard +// backend::matchmaker::lobby_runtime::Port { +// label: "test-http".into(), +// target_port: Some(8001), +// port_range: None, +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Http as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, +// }, +// backend::matchmaker::lobby_runtime::Port { +// label: "test-tcp".into(), +// target_port: Some(8002), +// port_range: None, +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Tcp as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, +// }, +// backend::matchmaker::lobby_runtime::Port { +// label: "test-udp".into(), +// target_port: Some(8003), +// port_range: None, +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Udp as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, +// }, + +// // Host +// backend::matchmaker::lobby_runtime::Port { +// label: "test-range-tcp".into(), +// target_port: None, +// port_range: Some(backend::matchmaker::lobby_runtime::PortRange { +// min: 26000, +// max: 27000, +// }), +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Tcp as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::None as i32, +// }, +// backend::matchmaker::lobby_runtime::Port { +// label: "test-range-udp".into(), +// target_port: None, +// port_range: Some(backend::matchmaker::lobby_runtime::PortRange { +// min: 26000, +// max: 27000, +// }), +// proxy_protocol: backend::matchmaker::lobby_runtime::ProxyProtocol::Udp as i32, +// proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::None as i32, +// }, +// ], + +// }.into()), + +// actions: None, +// }], +// }), +// ..Default::default() +// }) +// .await +// .unwrap(); + +// let version_get_res = op!([ctx] mm_config_version_get { +// version_ids: vec![game_version_res.version_id.unwrap()], +// }) +// .await +// .unwrap(); +// let lobby_groups = &version_get_res +// .versions +// .first() +// .unwrap() +// .config_meta +// .as_ref() +// .unwrap() +// .lobby_groups; + +// let ns_create_res = op!([ctx] faker_game_namespace { +// game_id: game_res.game_id, +// version_id: game_version_res.version_id, +// override_name_id: "prod".to_owned(), +// ..Default::default() +// }) +// .await +// .unwrap(); +// let namespace_id = ns_create_res.namespace_id.unwrap().as_uuid(); + +// Setup { +// namespace_id, +// lobby_group_id_bridge: lobby_groups[0].lobby_group_id.as_ref().unwrap().as_uuid(), +// lobby_group_id_host: lobby_groups[1].lobby_group_id.as_ref().unwrap().as_uuid(), +// region_id, +// region: region_res.region.clone().unwrap(), +// host_port_http, +// host_port_tcp, +// host_port_udp, +// } +// } + +// /// Create bridge lobby +// pub async fn create_lobby(&self, ctx: &TestCtx) -> Uuid { +// self.create_lobby_with_lgi(ctx, self.lobby_group_id_bridge) +// .await +// } + +// /// Create lobby with LGI +// pub async fn create_lobby_with_lgi(&self, ctx: &TestCtx, lgi: Uuid) -> Uuid { +// let lobby_id = Uuid::new_v4(); +// msg!([ctx] @notrace mm::msg::lobby_create(lobby_id) -> mm::msg::lobby_ready_complete(lobby_id) { +// lobby_id: Some(lobby_id.into()), +// namespace_id: Some(self.namespace_id.into()), +// lobby_group_id: Some(lgi.into()), +// region_id: Some(self.region_id.into()), +// create_ray_id: None, +// preemptively_created: false, + +// creator_user_id: None, +// is_custom: false, +// publicity: None, +// lobby_config_json: None, +// tags: HashMap::new(), +// dynamic_max_players: None, +// parameters: util::env::test_id_param(), +// }) +// .await +// .unwrap(); + +// lobby_id +// } +// } diff --git a/svc/pkg/ds/worker/tests/ds_nomad_monitor_alloc_plan.rs b/svc/pkg/ds/worker/tests/ds_nomad_monitor_alloc_plan.rs new file mode 100644 index 0000000000..1b03565b6c --- /dev/null +++ b/svc/pkg/ds/worker/tests/ds_nomad_monitor_alloc_plan.rs @@ -0,0 +1,13 @@ +use chirp_worker::prelude::*; +use proto::backend::pkg::*; + +#[worker_test] +async fn ds_nomad_monitor_alloc_plan(ctx: TestCtx) { + // msg!([ctx] ds::msg::ds_nomad_monitor_alloc_plan() { + + // }) + // .await + // .unwrap(); + + todo!(); +} diff --git a/svc/pkg/ds/worker/tests/lobby_connectivity.rs b/svc/pkg/ds/worker/tests/lobby_connectivity.rs new file mode 100644 index 0000000000..1b5af4a995 --- /dev/null +++ b/svc/pkg/ds/worker/tests/lobby_connectivity.rs @@ -0,0 +1,310 @@ +// mod common; + +// use std::{ +// io::{BufRead, BufReader, Write}, +// net::{TcpStream, UdpSocket}, +// }; + +// use chirp_worker::prelude::*; +// use common::*; +// use proto::backend; + +// #[worker_test] +// async fn lobby_connectivity_http_normal(ctx: TestCtx) { +// if !util::feature::job_run() { +// return; +// } + +// let setup = Setup::init(&ctx).await; + +// let lobby_id = setup.create_lobby(&ctx).await; + +// let (hostname, _) = get_lobby_addr(&ctx, lobby_id, "test-http").await; +// tracing::info!("testing http to {}", hostname); + +// // Echo body +// let random_body = Uuid::new_v4().to_string(); +// let client = reqwest::Client::new(); +// let res = client +// .post(format!("http://{hostname}")) +// .body(random_body.clone()) +// .send() +// .await +// .unwrap() +// .error_for_status() +// .unwrap(); +// let res_text = res.text().await.unwrap(); +// assert_eq!(random_body, res_text, "echoed wrong response"); +// } + +// // #[worker_test] +// // async fn lobby_connectivity_http_host(ctx: TestCtx) { +// // if !util::feature::job_run() { +// // return; +// // } + +// // let setup = Setup::init(&ctx).await; + +// // let lobby_id = setup +// // .create_lobby_with_lgi(&ctx, setup.lobby_group_id_host) +// // .await; + +// // // Echo body (bridge) +// // { +// // let (hostname, _) = get_lobby_addr(&ctx, lobby_id, "test-http").await; +// // tracing::info!("testing http to {}", hostname); + +// // let random_body = Uuid::new_v4().to_string(); +// // let client = reqwest::Client::new(); +// // let res = client +// // .post(format!("http://{hostname}")) +// // .body(random_body.clone()) +// // .send() +// // .await +// // .unwrap() +// // .error_for_status() +// // .unwrap(); +// // let res_text = res.text().await.unwrap(); +// // assert_eq!(random_body, res_text, "echoed wrong response"); +// // } + +// // // Echo body (host) +// // { +// // let host_ip = get_lobby_host_ip(&ctx, lobby_id).await; +// // tracing::info!("testing http to {}:{}", host_ip, setup.host_port_http); + +// // let random_body = Uuid::new_v4().to_string(); +// // let client = reqwest::Client::new(); +// // let res = client +// // .post(format!("http://{host_ip}:{}", setup.host_port_http)) +// // .body(random_body.clone()) +// // .send() +// // .await +// // .unwrap() +// // .error_for_status() +// // .unwrap(); +// // let res_text = res.text().await.unwrap(); +// // assert_eq!(random_body, res_text, "echoed wrong response"); +// // } +// // } + +// // #[worker_test] +// // async fn lobby_connectivity_tcp(ctx: TestCtx) { +// // if !util::feature::job_run() { +// // return; +// // } + +// // let setup = Setup::init(&ctx).await; + +// // let lobby_id = setup.create_lobby(&ctx).await; + +// // let (hostname, port) = get_lobby_addr(&ctx, lobby_id, "test-tcp").await; +// // tracing::info!("testing tcp to {}:{}", hostname, port); + +// // // Echo body +// // let random_body = Uuid::new_v4().to_string(); +// // let mut stream = TcpStream::connect((hostname, port)).unwrap(); + +// // stream.write_all(random_body.as_ref()).unwrap(); +// // stream.write_all(b"\n").unwrap(); +// // stream.flush().unwrap(); + +// // let mut reader = BufReader::new(&stream); +// // let mut response = String::new(); +// // reader.read_line(&mut response).expect("read line"); + +// // assert_eq!( +// // &random_body, +// // &response[..response.len() - 1], +// // "echoed wrong response" +// // ); +// // } + +// // #[worker_test] +// // async fn lobby_connectivity_tcp_host(ctx: TestCtx) { +// // if !util::feature::job_run() { +// // return; +// // } + +// // let setup = Setup::init(&ctx).await; + +// // let lobby_id = setup +// // .create_lobby_with_lgi(&ctx, setup.lobby_group_id_host) +// // .await; + +// // // Echo body (bridge) +// // { +// // let (hostname, port) = get_lobby_addr(&ctx, lobby_id, "test-tcp").await; +// // tracing::info!("testing tcp to {}:{}", hostname, port); + +// // // Echo body +// // let random_body = Uuid::new_v4().to_string(); +// // let mut stream = TcpStream::connect((hostname, port)).unwrap(); + +// // stream.write_all(random_body.as_ref()).unwrap(); +// // stream.write_all(b"\n").unwrap(); +// // stream.flush().unwrap(); + +// // let mut reader = BufReader::new(&stream); +// // let mut response = String::new(); +// // reader.read_line(&mut response).expect("read line"); + +// // assert_eq!( +// // &random_body, +// // &response[..response.len() - 1], +// // "echoed wrong response" +// // ); +// // } + +// // // Echo body (host) +// // { +// // let host_ip = get_lobby_host_ip(&ctx, lobby_id).await; +// // tracing::info!("testing tcp to {}:{}", host_ip, setup.host_port_tcp); + +// // let random_body = Uuid::new_v4().to_string(); +// // let mut stream = TcpStream::connect((host_ip, setup.host_port_tcp)).unwrap(); + +// // stream.write_all(random_body.as_ref()).unwrap(); +// // stream.write_all(b"\n").unwrap(); +// // stream.flush().unwrap(); + +// // let mut reader = BufReader::new(&stream); +// // let mut response = String::new(); +// // reader.read_line(&mut response).expect("read line"); + +// // assert_eq!( +// // &random_body, +// // &response[..response.len() - 1], +// // "echoed wrong response" +// // ); +// // } +// // } + +// // #[worker_test] +// // async fn lobby_connectivity_udp(ctx: TestCtx) { +// // if !util::feature::job_run() { +// // return; +// // } + +// // let setup = Setup::init(&ctx).await; + +// // let lobby_id = setup.create_lobby(&ctx).await; + +// // let (hostname, port) = get_lobby_addr(&ctx, lobby_id, "test-udp").await; +// // tracing::info!("testing udp to {}:{}", hostname, port); + +// // // Echo body +// // let random_body = Uuid::new_v4(); +// // let socket = UdpSocket::bind(("0.0.0.0", 0)).unwrap(); +// // socket.connect((hostname, port)).unwrap(); +// // socket.send(random_body.as_ref()).unwrap(); + +// // let mut response = [0; 2048]; +// // let recv_len = socket.recv(&mut response).unwrap(); + +// // assert_eq!( +// // random_body.as_bytes(), +// // &response[..recv_len], +// // "echoed wrong response" +// // ); +// // } + +// // #[worker_test] +// // async fn lobby_connectivity_udp_host(ctx: TestCtx) { +// // if !util::feature::job_run() { +// // return; +// // } + +// // let setup = Setup::init(&ctx).await; + +// // let lobby_id = setup +// // .create_lobby_with_lgi(&ctx, setup.lobby_group_id_host) +// // .await; + +// // let host_ip = get_lobby_host_ip(&ctx, lobby_id).await; + +// // // Echo body (bridge) +// // { +// // let (hostname, port) = get_lobby_addr(&ctx, lobby_id, "test-udp").await; +// // tracing::info!("testing udp to {}:{}", hostname, port); + +// // let random_body = Uuid::new_v4(); +// // let socket = UdpSocket::bind(("0.0.0.0", 0)).unwrap(); +// // socket.connect((hostname, port)).unwrap(); +// // socket.send(random_body.as_ref()).unwrap(); + +// // let mut response = [0; 2048]; +// // let recv_len = socket.recv(&mut response).unwrap(); + +// // assert_eq!( +// // random_body.as_bytes(), +// // &response[..recv_len], +// // "echoed wrong response" +// // ); +// // } + +// // // Echo body (host) +// // { +// // tracing::info!("testing udp to {}:{}", host_ip, setup.host_port_udp); + +// // let random_body = Uuid::new_v4(); +// // let socket = UdpSocket::bind(("0.0.0.0", 0)).unwrap(); +// // socket.connect((host_ip, setup.host_port_udp)).unwrap(); +// // socket.send(random_body.as_ref()).unwrap(); + +// // let mut response = [0; 2048]; +// // let recv_len = socket.recv(&mut response).unwrap(); + +// // assert_eq!( +// // random_body.as_bytes(), +// // &response[..recv_len], +// // "echoed wrong response" +// // ); +// // } +// // } + +// /// Fetches the address to get the lobby from. +// async fn get_lobby_addr(ctx: &TestCtx, lobby_id: Uuid, port: &str) -> (String, u16) { +// let lobby_res = op!([ctx] mm_lobby_get { lobby_ids: vec![lobby_id.into()] }) +// .await +// .unwrap(); +// let lobby = lobby_res.lobbies.first().unwrap(); +// let run_id = lobby.run_id.unwrap(); + +// let run_res = op!([ctx] job_run_get { run_ids: vec![run_id] }) +// .await +// .unwrap(); +// let run = run_res.runs.first().unwrap(); + +// let port = run +// .proxied_ports +// .iter() +// .find(|x| x.target_nomad_port_label == Some(util_ds::format_nomad_port_label(port))) +// .unwrap(); + +// ( +// port.ingress_hostnames.first().unwrap().clone(), +// port.ingress_port as u16, +// ) +// } + +// /// Fetches the address to get the lobby from for host networking. +// async fn get_lobby_host_ip(ctx: &TestCtx, lobby_id: Uuid) -> String { +// let lobby_res = op!([ctx] mm_lobby_get { lobby_ids: vec![lobby_id.into()] }) +// .await +// .unwrap(); +// let lobby = lobby_res.lobbies.first().unwrap(); +// let run_id = lobby.run_id.unwrap(); + +// let run_res = op!([ctx] job_run_get { run_ids: vec![run_id] }) +// .await +// .unwrap(); +// let run = run_res.runs.first().unwrap(); + +// let run_meta = run.run_meta.as_ref().unwrap(); +// let Some(backend::job::run_meta::Kind::Nomad(run_meta_nomad)) = &run_meta.kind else { +// panic!() +// }; + +// run_meta_nomad.node_public_ipv4.clone().unwrap() +// } diff --git a/svc/pkg/faker/ops/build/src/lib.rs b/svc/pkg/faker/ops/build/src/lib.rs index 7fb9698c49..62b67b6ee4 100644 --- a/svc/pkg/faker/ops/build/src/lib.rs +++ b/svc/pkg/faker/ops/build/src/lib.rs @@ -16,6 +16,7 @@ async fn handle( backend::faker::Image::MmLobbyAutoReady => "test-mm-lobby-ready", backend::faker::Image::MmLobbyEcho => "test-mm-lobby-echo", backend::faker::Image::MmPlayerConnect => "test-mm-player-connect", + backend::faker::Image::DsEcho => "test-ds-echo", }; let create_res = op!([ctx] build_create { diff --git a/svc/pkg/monolith/standalone/worker/Cargo.toml b/svc/pkg/monolith/standalone/worker/Cargo.toml index 047dd4eadb..f8cd7f71fc 100644 --- a/svc/pkg/monolith/standalone/worker/Cargo.toml +++ b/svc/pkg/monolith/standalone/worker/Cargo.toml @@ -24,6 +24,7 @@ cdn-worker = { path = "../../../cdn/worker" } cf-custom-hostname-worker = { path = "../../../cf-custom-hostname/worker" } cloud-worker = { path = "../../../cloud/worker" } cluster-worker = { path = "../../../cluster/worker" } +ds-worker = { path = "../../../ds/worker" } external-worker = { path = "../../../external/worker" } game-user-worker = { path = "../../../game-user/worker" } job-log-worker = { path = "../../../job-log/worker" } diff --git a/svc/pkg/monolith/standalone/worker/src/lib.rs b/svc/pkg/monolith/standalone/worker/src/lib.rs index 4cb67dfb01..884ffea5d2 100644 --- a/svc/pkg/monolith/standalone/worker/src/lib.rs +++ b/svc/pkg/monolith/standalone/worker/src/lib.rs @@ -26,6 +26,7 @@ pub async fn run_from_env(pools: rivet_pools::Pools) -> GlobalResult<()> { cf_custom_hostname_worker, cloud_worker, cluster_worker, + ds_worker, external_worker, game_user_worker, job_log_worker,