1mod agones_game_server_state;
2mod agones_server;
3mod yaml_server;
4
5pub mod sdk {
6 tonic::include_proto!("agones.dev.sdk");
7}
8
9use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
10
11pub use agones_server::AgonesServer;
12pub use agones_server::DEFAULT_AGONES_PORT;
13use hyper::server::conn::AddrIncoming;
14
15impl yaml_server::Store<sdk::GameServer> for AgonesServer {
16 fn get(&self) -> sdk::GameServer {
17 self.game_server()
18 }
19
20 fn set(&self, value: sdk::GameServer) {
21 self.set_game_server(value);
22 }
23}
24
25pub struct Server {
26 make_service: hybrid_service::HybridMakeService<
27 axum::routing::IntoMakeService<axum::Router>,
28 tonic::transport::server::Routes,
29 >,
30}
31
32impl Server {
33 pub async fn serve(&self) -> Result<(), hyper::Error> {
35 let incoming = AddrIncoming::bind(&SocketAddr::V4(SocketAddrV4::new(
36 Ipv4Addr::LOCALHOST,
37 DEFAULT_AGONES_PORT,
38 )))?;
39 self.serve_with_incoming(incoming).await
40 }
41
42 pub async fn spawn_on_random_port(&self) -> Result<u16, hyper::Error> {
44 let incoming =
45 AddrIncoming::bind(&SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)))?;
46 let port = incoming.local_addr().port();
47 let make_service = self.make_service.clone();
48 tokio::spawn(hyper::Server::builder(incoming).serve(make_service));
49 Ok(port)
50 }
51
52 pub async fn serve_with_incoming(&self, incoming: AddrIncoming) -> Result<(), hyper::Error> {
54 hyper::Server::builder(incoming)
55 .serve(self.make_service.clone())
56 .await
57 }
58}
59
60impl Default for Server {
61 fn default() -> Self {
62 let agones_server = AgonesServer::default();
63 let make_service = hybrid_service::hybrid(
64 yaml_server::create_service(agones_server.clone()),
65 agones_server.into_service(),
66 );
67 Self { make_service }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use std::sync::Arc;
74
75 use parking_lot::RwLock;
76 use tokio_stream::StreamExt;
77
78 use crate::agones_game_server_state::GameServerState;
79
80 use super::*;
81
82 struct TestServer {
83 port: u16,
84 sdk: agones::Sdk,
85 }
86
87 impl TestServer {
88 async fn new() -> Self {
89 let port = Server::default().spawn_on_random_port().await.unwrap();
90 let sdk = agones::Sdk::new(port.into(), None).await.unwrap();
91 Self { port, sdk }
92 }
93
94 async fn http_get(&self) -> reqwest::Response {
95 let client = reqwest::Client::new();
96 client
97 .get(format!("http://localhost:{}", self.port))
98 .send()
99 .await
100 .unwrap()
101 }
102
103 async fn get(&self) -> sdk::GameServer {
104 let response = self.http_get().await;
105 serde_yaml::from_str(&response.text().await.unwrap()).unwrap()
106 }
107
108 async fn post(&self, gs: &sdk::GameServer) -> sdk::GameServer {
109 let response = reqwest::Client::new()
110 .post(format!("http://localhost:{}", self.port))
111 .header(reqwest::header::CONTENT_TYPE, "application/yaml")
112 .body(serde_yaml::to_vec(gs).unwrap())
113 .send()
114 .await
115 .unwrap()
116 .error_for_status()
117 .unwrap();
118 serde_yaml::from_str(&response.text().await.unwrap()).unwrap()
119 }
120 }
121
122 #[tokio::test]
123 async fn should_have_default_game_server_after_start_in_http_interface() {
124 let test_server = TestServer::new().await;
126
127 let gs = test_server.get().await;
131 assert_eq!(gs, Default::default());
132 }
133
134 #[tokio::test]
135 async fn should_have_default_game_server_after_start_in_agones_sdk() {
136 let mut test_server = TestServer::new().await;
138
139 let gs = test_server.sdk.get_gameserver().await.unwrap();
143 assert_eq!(gs, Default::default());
144 }
145
146 #[tokio::test]
147 async fn sdk_ready_should_be_visible_in_http_interface() {
148 let mut test_server = TestServer::new().await;
150
151 test_server.sdk.ready().await.unwrap();
153
154 let gs = test_server.get().await;
156 assert_eq!(gs.status.unwrap().state, GameServerState::Ready.to_string());
157 }
158
159 #[tokio::test]
160 async fn updates_via_http_should_be_visible_in_agones_sdk() {
161 let mut test_server = TestServer::new().await;
163
164 test_server
166 .post(&sdk::GameServer {
167 status: Some(sdk::game_server::Status {
168 state: GameServerState::Ready.to_string(),
169 ..Default::default()
170 }),
171 ..Default::default()
172 })
173 .await;
174
175 let gs = test_server.sdk.get_gameserver().await.unwrap();
177 assert_eq!(gs.status.unwrap().state, GameServerState::Ready.to_string());
178 }
179
180 #[tokio::test]
181 async fn updates_via_http_should_be_visible_in_agones_sdk_stream() {
182 let mut test_server = TestServer::new().await;
184 let last_game_server = Arc::new(RwLock::new(None));
185
186 let mut stream = test_server.sdk.watch_gameserver().await.unwrap();
188 tokio::spawn({
189 let last_game_server = last_game_server.clone();
190 async move {
191 while let Some(gs) = stream.next().await {
192 if let Ok(gs) = gs {
193 *last_game_server.write() = Some(gs);
194 }
195 }
196 }
197 });
198 test_server
199 .post(&sdk::GameServer {
200 status: Some(sdk::game_server::Status {
201 state: GameServerState::Ready.to_string(),
202 ..Default::default()
203 }),
204 ..Default::default()
205 })
206 .await;
207 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
208
209 let gs = last_game_server.read().clone().unwrap();
211 assert_eq!(gs.status.unwrap().state, GameServerState::Ready.to_string());
212 }
213
214 #[tokio::test]
215 async fn updates_via_http_should_return_updated_game_server() {
216 let test_server = TestServer::new().await;
218 let game_server = sdk::GameServer {
219 status: Some(sdk::game_server::Status {
220 state: GameServerState::Ready.to_string(),
221 ..Default::default()
222 }),
223 ..Default::default()
224 };
225
226 let returned_game_server = test_server.post(&game_server).await;
228
229 assert_eq!(game_server, returned_game_server);
231 }
232}