Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,97 @@ TEST_F(GameLiftServerStateTest, GIVEN_retriable_failure_WHEN_sendMessage_THEN_re
// THEN
ASSERT_TRUE(outcome.IsSuccess());
}

TEST_F(GameLiftServerStateTest, GIVEN_nonContainerComputeType_WHEN_listContainersNetworkInfo_THEN_returnsUnsupportedError) {
// GIVEN
#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "EC2");
#else
setenv("GAMELIFT_COMPUTE_TYPE", "EC2", 1);
#endif

// WHEN
auto outcome = serverState->ListContainersNetworkInfo();

// THEN
EXPECT_FALSE(outcome.IsSuccess());
EXPECT_EQ(outcome.GetError().GetErrorType(), GAMELIFT_ERROR_TYPE::UNSUPPORTED_COMPUTE_TYPE_EXCEPTION);

#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "");
#else
unsetenv("GAMELIFT_COMPUTE_TYPE");
#endif
}

TEST_F(GameLiftServerStateTest, GIVEN_noComputeType_WHEN_listContainersNetworkInfo_THEN_returnsUnsupportedError) {
// GIVEN
#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "");
#else
unsetenv("GAMELIFT_COMPUTE_TYPE");
#endif

// WHEN
auto outcome = serverState->ListContainersNetworkInfo();

// THEN
EXPECT_FALSE(outcome.IsSuccess());
EXPECT_EQ(outcome.GetError().GetErrorType(), GAMELIFT_ERROR_TYPE::UNSUPPORTED_COMPUTE_TYPE_EXCEPTION);
}

TEST_F(GameLiftServerStateTest, GIVEN_containerTypeNoEndpointNoMetadata_WHEN_listContainersNetworkInfo_THEN_returnsServiceError) {
// GIVEN
#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "CONTAINER");
_putenv_s("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT", "");
_putenv_s("ECS_CONTAINER_METADATA_URI_V4", "");
#else
setenv("GAMELIFT_COMPUTE_TYPE", "CONTAINER", 1);
unsetenv("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT");
unsetenv("ECS_CONTAINER_METADATA_URI_V4");
#endif

// WHEN
auto outcome = serverState->ListContainersNetworkInfo();

// THEN - cannot resolve endpoint from env var or metadata
EXPECT_FALSE(outcome.IsSuccess());
EXPECT_EQ(outcome.GetError().GetErrorType(), GAMELIFT_ERROR_TYPE::INTERNAL_SERVICE_EXCEPTION);

#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "");
#else
unsetenv("GAMELIFT_COMPUTE_TYPE");
#endif
}

TEST_F(GameLiftServerStateTest, GIVEN_containerTypeWithUnreachableEndpoint_WHEN_listContainersNetworkInfo_THEN_returnsServiceError) {
// GIVEN
#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "CONTAINER");
_putenv_s("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT", "http://192.0.2.1:9999");
#else
setenv("GAMELIFT_COMPUTE_TYPE", "CONTAINER", 1);
setenv("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT", "http://192.0.2.1:9999", 1);
#endif

// WHEN
auto outcome = serverState->ListContainersNetworkInfo();

// THEN - connection fails to unreachable address
EXPECT_FALSE(outcome.IsSuccess());
EXPECT_EQ(outcome.GetError().GetErrorType(), GAMELIFT_ERROR_TYPE::INTERNAL_SERVICE_EXCEPTION);

#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "");
_putenv_s("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT", "");
#else
unsetenv("GAMELIFT_COMPUTE_TYPE");
unsetenv("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT");
#endif
}

} // namespace Test
} // namespace Internal
} // namespace GameLift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace GameLift {
namespace Internal {
namespace Test {

static const std::string sdkVersion = "5.4.1";
static const std::string sdkVersion = "5.5.0";
// private constants copied over for testing purposes
static constexpr const char *PID_KEY = "pID";
static constexpr const char *SDK_VERSION_KEY = "sdkVersion";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace GameLift {
namespace Server {
namespace Test {

static const std::string sdkVersion = "5.4.1";
static const std::string sdkVersion = "5.5.0";

TEST(GameLiftServerAPITest, GIVEN_SdkVersion_WHEN_GetSdkVersion_THEN_success) {
// GIVEN
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/

#include <gtest/gtest.h>
#include <cstring>
#include <aws/gamelift/server/GameLiftServerAPI.h>
#include <aws/gamelift/common/GameLiftErrors.h>
#include <aws/gamelift/common/Outcome.h>
#include <aws/gamelift/server/model/ListContainersNetworkInfoResult.h>

using namespace Aws::GameLift;
using namespace Aws::GameLift::Server::Model;

namespace Aws {
namespace GameLift {
namespace Internal {
namespace Test {

class ListContainersNetworkInfoTest : public ::testing::Test {
protected:
void SetUp() override {
}

void TearDown() override {
#ifdef _WIN32
_putenv_s("GAMELIFT_COMPUTE_TYPE", "");
_putenv_s("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT", "");
_putenv_s("ECS_CONTAINER_METADATA_URI_V4", "");
#else
unsetenv("GAMELIFT_COMPUTE_TYPE");
unsetenv("GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT");
unsetenv("ECS_CONTAINER_METADATA_URI_V4");
#endif
}
};

TEST_F(ListContainersNetworkInfoTest, GIVEN_sdkNotInitialized_WHEN_listContainersNetworkInfo_THEN_returnsError) {
// GIVEN - SDK not initialized

// WHEN
auto outcome = Server::ListContainersNetworkInfo();

// THEN
EXPECT_FALSE(outcome.IsSuccess());
EXPECT_EQ(outcome.GetError().GetErrorType(), GAMELIFT_ERROR_TYPE::NOT_INITIALIZED);
}

// Model tests
TEST(ContainerNetworkInfoModelTest, GIVEN_defaultConstructor_WHEN_created_THEN_hasDefaultValues) {
ContainerNetworkInfo info;
EXPECT_EQ(info.GetContainerGroupType(), ContainerGroupType::GAME_SERVER);
}

TEST(ContainerNetworkInfoModelTest, GIVEN_values_WHEN_setAndGet_THEN_returnsCorrectValues) {
ContainerNetworkInfo info;
info.SetContainerName("otel-collector");
info.SetContainerId("abc123def456");
info.SetIpAddress("172.17.0.2");
info.SetContainerGroupType(ContainerGroupType::PER_INSTANCE);

#ifdef GAMELIFT_USE_STD
EXPECT_EQ(info.GetContainerName(), "otel-collector");
EXPECT_EQ(info.GetContainerId(), "abc123def456");
EXPECT_EQ(info.GetIpAddress(), "172.17.0.2");
#else
EXPECT_STREQ(info.GetContainerName(), "otel-collector");
EXPECT_STREQ(info.GetContainerId(), "abc123def456");
EXPECT_STREQ(info.GetIpAddress(), "172.17.0.2");
#endif
EXPECT_EQ(info.GetContainerGroupType(), ContainerGroupType::PER_INSTANCE);
}

// The discovery server returns the full 64-char Docker SHA-256 container ID rather than the
// truncated 12-char short form. Verify the buffer holds it without truncation.
TEST(ContainerNetworkInfoModelTest, GIVEN_fullDockerContainerId_WHEN_setAndGet_THEN_preservesAll64Chars) {
const char *fullContainerId =
"9d97c0b58d18a3f2e4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7";
ASSERT_EQ(strlen(fullContainerId), 64u);

ContainerNetworkInfo info;
info.SetContainerId(fullContainerId);

#ifdef GAMELIFT_USE_STD
EXPECT_EQ(info.GetContainerId(), fullContainerId);
EXPECT_EQ(info.GetContainerId().size(), 64u);
#else
EXPECT_STREQ(info.GetContainerId(), fullContainerId);
EXPECT_EQ(strlen(info.GetContainerId()), 64u);
#endif
}

TEST(ContainerNetworkInfoModelTest, GIVEN_result_WHEN_addContainerNetworkInfo_THEN_containsEntry) {
ListContainersNetworkInfoResult result;

ContainerNetworkInfo info;
info.SetContainerName("game-server");
info.SetContainerId("9d97c0b58d18");
info.SetIpAddress("172.17.0.4");
info.SetContainerGroupType(ContainerGroupType::GAME_SERVER);

result.AddContainerNetworkInfo(info);

#ifdef GAMELIFT_USE_STD
EXPECT_EQ(result.GetContainersNetworkInfo().size(), 1);
EXPECT_EQ(result.GetContainersNetworkInfo()[0].GetContainerName(), "game-server");
EXPECT_EQ(result.GetContainersNetworkInfo()[0].GetIpAddress(), "172.17.0.4");
#else
EXPECT_EQ(result.GetContainersNetworkInfoCount(), 1);
EXPECT_STREQ(result.GetContainersNetworkInfo()[0].GetContainerName(), "game-server");
EXPECT_STREQ(result.GetContainersNetworkInfo()[0].GetIpAddress(), "172.17.0.4");
#endif
}

} // namespace Test
} // namespace Internal
} // namespace GameLift
} // namespace Aws
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ enum class AWS_GAMELIFT_API GAMELIFT_ERROR_TYPE {
WEBSOCKET_RETRIABLE_SEND_MESSAGE_FAILURE, // Retriable failure to send message to the Amazon GameLift Servers
// Service WebSocket
WEBSOCKET_SEND_MESSAGE_FAILURE, // Failure to send message to the Amazon GameLift Servers WebSocket
VALIDATION_EXCEPTION // Client-side error when invalid parameters are passed.
VALIDATION_EXCEPTION, // Client-side error when invalid parameters are passed.
UNSUPPORTED_COMPUTE_TYPE_EXCEPTION // API called on an unsupported compute type.
};

class AWS_GAMELIFT_API GameLiftError {
Expand Down Expand Up @@ -260,6 +261,8 @@ class AWS_GAMELIFT_API GameLiftError {
return "WebSocket Send Message Failed.";
case GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION:
return "Validation exception.";
case GAMELIFT_ERROR_TYPE::UNSUPPORTED_COMPUTE_TYPE_EXCEPTION:
return "Unsupported compute type.";
default:
return "Unknown Error";
}
Expand Down Expand Up @@ -350,6 +353,8 @@ class AWS_GAMELIFT_API GameLiftError {
return "Sending Message to the Amazon GameLift Servers WebSocket has failed.";
case GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION:
return "The input is invalid.";
case GAMELIFT_ERROR_TYPE::UNSUPPORTED_COMPUTE_TYPE_EXCEPTION:
return "This API is not supported on the current compute type.";
default:
return "An unexpected error has occurred.";
}
Expand Down
2 changes: 2 additions & 0 deletions gamelift-server-sdk/include/aws/gamelift/common/Outcome.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <aws/gamelift/common/GameLiftErrors.h>
#include <aws/gamelift/server/model/DescribePlayerSessionsResult.h>
#include <aws/gamelift/server/model/GetComputeCertificateResult.h>
#include <aws/gamelift/server/model/ListContainersNetworkInfoResult.h>
#include <aws/gamelift/server/model/GetFleetRoleCredentialsResult.h>
#include <aws/gamelift/server/model/StartMatchBackfillResult.h>
#include <future>
Expand Down Expand Up @@ -124,6 +125,7 @@ typedef Outcome<long, GameLiftError> AwsLongOutcome;
typedef Outcome<Aws::GameLift::Server::Model::DescribePlayerSessionsResult, GameLiftError> DescribePlayerSessionsOutcome;
typedef Outcome<Aws::GameLift::Server::Model::StartMatchBackfillResult, GameLiftError> StartMatchBackfillOutcome;
typedef Outcome<Aws::GameLift::Server::Model::GetComputeCertificateResult, GameLiftError> GetComputeCertificateOutcome;
typedef Outcome<Aws::GameLift::Server::Model::ListContainersNetworkInfoResult, GameLiftError> ListContainersNetworkInfoOutcome;
typedef Outcome<Aws::GameLift::Server::Model::GetFleetRoleCredentialsResult, GameLiftError> GetFleetRoleCredentialsOutcome;
} // namespace GameLift
} // namespace Aws
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <aws/gamelift/internal/network/GameLiftWebSocketClientManager.h>
#include <aws/gamelift/internal/network/IGameLiftMessageHandler.h>
#include <aws/gamelift/internal/network/IWebSocketClientWrapper.h>
#include <aws/gamelift/internal/util/HttpClient.h>
#include <aws/gamelift/metrics/IMetricsProcessor.h>
#include <aws/gamelift/internal/network/callback/CreateGameSessionCallback.h>
#include <aws/gamelift/internal/network/callback/DescribePlayerSessionsCallback.h>
Expand Down Expand Up @@ -60,6 +61,9 @@ class GameLiftServerState : public GameLiftCommonState, public IGameLiftMessageH
static constexpr const char *ENV_VAR_SDK_TOOL_VERSION = "GAMELIFT_SDK_TOOL_VERSION";
static constexpr const char *COMPUTE_TYPE_CONTAINER = "CONTAINER";
static constexpr const char *AGENTLESS_CONTAINER_PROCESS_ID = "ManagedResource";
static constexpr const char *ENV_VAR_CONTAINER_DISCOVERY_SERVER_ENDPOINT = "GAMELIFT_CONTAINER_DISCOVERY_SERVER_ENDPOINT";
static constexpr const char *ENV_VAR_CONTAINER_METADATA_URI = "ECS_CONTAINER_METADATA_URI_V4";
static constexpr const int DISCOVERY_SERVER_PORT = 4092;

static constexpr const int HEALTHCHECK_INTERVAL_MILLIS = 60 * 1000;
static constexpr const int HEALTHCHECK_MAX_JITTER_MILLIS = 10 * 1000;
Expand Down Expand Up @@ -208,6 +212,8 @@ class GameLiftServerState : public GameLiftCommonState, public IGameLiftMessageH
public:
GetFleetRoleCredentialsOutcome GetFleetRoleCredentials(const Aws::GameLift::Server::Model::GetFleetRoleCredentialsRequest &request);

ListContainersNetworkInfoOutcome ListContainersNetworkInfo();

void SetGlobalProcessor(Aws::GameLift::Metrics::IMetricsProcessor* processor);

// When within 15 minutes of expiration we retrieve new instance role credentials
Expand All @@ -216,6 +222,9 @@ class GameLiftServerState : public GameLiftCommonState, public IGameLiftMessageH
private:
bool AssertNetworkInitialized();
void SetUpCallbacks();
std::string ResolveDiscoveryEndpointFromMetadata(HttpClient &httpClient);
Outcome<HttpResponse, std::string> FetchDiscoveryServerResponse(HttpClient &httpClient);
ListContainersNetworkInfoOutcome ParseDiscoveryServerResponse(HttpResponse &response);
static void DetectGameLiftTools();

bool m_processReady;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,13 @@ AWS_GAMELIFT_API GetComputeCertificateOutcome GetComputeCertificate();
*/
AWS_GAMELIFT_API GetFleetRoleCredentialsOutcome GetFleetRoleCredentials(const Aws::GameLift::Server::Model::GetFleetRoleCredentialsRequest &request);

/**
Queries the container discovery server and returns network information for all containers
running on the same instance. Only supported on container fleets.
@return ListContainersNetworkInfoOutcome containing container names, IPs, IDs, and group types.
*/
AWS_GAMELIFT_API ListContainersNetworkInfoOutcome ListContainersNetworkInfo();

} // namespace Server
} // namespace GameLift
} // namespace Aws
Loading