Skip to content

Commit 06ffbd3

Browse files
markmandelRobert Bailey
authored andcommitted
Implement block on connect with Rust SDK (agones-dev#953)
- Uses a simple polling mechanism to confirm the connection is live - Add `ready` to the SDK documentation. - Implements and documents a sdk.connect() function - Tweaks the conformance tests to add a random delay to ensure future testing of SDK blocking. Fixes agones-dev#938
1 parent 565431f commit 06ffbd3

9 files changed

Lines changed: 108 additions & 13 deletions

File tree

‎build/includes/sdk.mk‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,17 @@ run-sdk-conformance-local: ensure-agones-sdk-image
122122
-e "TEST=$(TESTS)" -e "TIMEOUT=$(TIMEOUT)" $(sidecar_tag)
123123

124124
# Run SDK conformance test, previously built, for a specific SDK_FOLDER
125+
# Sleeps the start of the sidecar to test that the SDK blocks on connection correctly
125126
run-sdk-conformance-no-build: TIMEOUT ?= 30
127+
run-sdk-conformance-no-build: RANDOM := $(shell bash -c 'echo $$RANDOM')
128+
run-sdk-conformance-no-build: DELAY ?= $(shell bash -c "echo $$[ ($(RANDOM) % 5 ) + 1 ]s")
126129
run-sdk-conformance-no-build: TESTS ?= ready,allocate,setlabel,setannotation,gameserver,health,shutdown,watch
127130
run-sdk-conformance-no-build: ensure-agones-sdk-image
128131
run-sdk-conformance-no-build: ensure-build-sdk-image
129-
sleep 2s && DOCKER_RUN_ARGS="--network=host $(DOCKER_RUN_ARGS)" COMMAND=sdktest $(MAKE) run-sdk-command & \
130-
docker run -p 59357:59357 -e "ADDRESS=" \
132+
DOCKER_RUN_ARGS="--network=host $(DOCKER_RUN_ARGS)" COMMAND=sdktest $(MAKE) run-sdk-command & \
133+
sleep $(DELAY) && docker run -p 59357:59357 -e "ADDRESS=" \
131134
-e "TEST=$(TESTS)" -e "TIMEOUT=$(TIMEOUT)" --net=host $(sidecar_tag)
132135

133-
134136
# Run SDK conformance test for a specific SDK_FOLDER
135137
run-sdk-conformance-test:
136138
$(MAKE) run-sdk-command COMMAND=build-sdk-test

‎sdks/nodejs/spec/agonesSDK.spec.js‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ describe('agones', () => {
2626
agonesSDK = new AgonesSDK();
2727
});
2828

29+
describe('connect', () => {
30+
it('calls the server and handles success', async () => {
31+
spyOn(agonesSDK.client, 'waitForReady').and.callFake((deadline, callback) => {
32+
let result = new messages.Empty();
33+
callback(undefined, result);
34+
});
35+
let result = await agonesSDK.connect();
36+
expect(agonesSDK.client.waitForReady).toHaveBeenCalled();
37+
expect(result).toEqual(undefined);
38+
});
39+
40+
it('calls the server and handles failure', async () => {
41+
spyOn(agonesSDK.client, 'waitForReady').and.callFake((deadline, callback) => {
42+
callback('error', undefined);
43+
});
44+
try {
45+
await agonesSDK.connect();
46+
fail();
47+
} catch (error) {
48+
expect(agonesSDK.client.waitForReady).toHaveBeenCalled();
49+
expect(error).toEqual('error');
50+
}
51+
});
52+
});
53+
2954
describe('allocate', () => {
3055
it('calls the server and handles success', async () => {
3156
spyOn(agonesSDK.client, 'allocate').and.callFake((request, callback) => {

‎sdks/nodejs/src/agonesSDK.js‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ class AgonesSDK {
2424
this.emitters = [];
2525
}
2626

27+
async connect() {
28+
return new Promise((resolve, reject) => {
29+
this.client.waitForReady(Date.now() + 30000, (error) => {
30+
if (error) {
31+
reject(error);
32+
} else {
33+
resolve();
34+
}
35+
})
36+
});
37+
}
38+
2739
async close() {
2840
if (this.healthStream !== undefined) {
2941
this.healthStream.destroy();

‎sdks/rust/src/sdk.rs‎

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use futures::{Future, Sink, Stream};
16-
use grpcio;
1715
use std::sync::{Arc, Mutex};
16+
use std::thread::sleep;
1817
use std::time::Duration;
1918

19+
use futures::{Future, Sink, Stream};
20+
use grpcio;
21+
use grpcio::CallOption;
22+
use protobuf::Message;
23+
2024
use errors::*;
2125
use grpc::sdk;
2226
use grpc::sdk_grpc;
23-
use protobuf::Message;
2427
use types::*;
2528

2629
const PORT: i32 = 59357;
@@ -34,7 +37,7 @@ pub struct Sdk {
3437
impl Sdk {
3538
/// Starts a new SDK instance, and connects to localhost on port 59357.
3639
/// Blocks until connection and handshake are made.
37-
/// Times out after 30 seconds.
40+
/// Times out after ~30 seconds.
3841
pub fn new() -> Result<Sdk> {
3942
let addr = format!("localhost:{}", PORT);
4043
let env = Arc::new(grpcio::EnvBuilder::new().build());
@@ -43,7 +46,24 @@ impl Sdk {
4346
.connect(&addr);
4447
let cli = sdk_grpc::SdkClient::new(ch);
4548
let req = sdk::Empty::new();
46-
let _ = cli.ready(&req).map(Box::new)?;
49+
50+
// Unfortunately there isn't a native way to block until connected
51+
// so we had to roll our own.
52+
let mut counter = 0;
53+
loop {
54+
counter += 1;
55+
match cli.get_game_server(&req) {
56+
Ok(_) => break,
57+
Err(e) => {
58+
if counter > 30 {
59+
return Err(ErrorKind::Grpc(e).into());
60+
}
61+
sleep(Duration::from_secs(1));
62+
continue;
63+
}
64+
}
65+
}
66+
4767
let (sender, _) = cli.health()?;
4868
Ok(Sdk {
4969
client: Arc::new(cli),
@@ -58,15 +78,13 @@ impl Sdk {
5878
Ok(res)
5979
}
6080

61-
62-
/// Allocate the Game Server
81+
/// Allocate the Game Server
6382
pub fn allocate(&self) -> Result<()> {
6483
let req = sdk::Empty::default_instance();
6584
let res = self.client.allocate(req).map(|_| ())?;
6685
Ok(res)
6786
}
6887

69-
7088
/// Marks the Game Server as ready to shutdown
7189
pub fn shutdown(&self) -> Result<()> {
7290
let req = sdk::Empty::default_instance();

‎site/content/en/docs/Guides/Client SDKs/nodejs.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,24 @@ Add the agones dependency to `package.json`, replacing with the download locatio
2828
}
2929
```
3030

31-
To begin working with the SDK, create an instance of it. This will open a connection to the SDK server.
31+
To begin working with the SDK, create an instance of it. {{< feature expiryVersion="0.12.0" >}}This will open a connection to the SDK server.{{< /feature >}}
3232

3333
```javascript
3434
const AgonesSDK = require('agones');
3535

3636
let agonesSDK = new AgonesSDK();
3737
```
3838

39+
{{% feature publishVersion="0.12.0" %}}
40+
41+
To connect to the SDK server, either local or when running on Agones, run the `async` method `sdk.connect()`, which will
42+
`resolve` once connected or `reject` on error or if no connection can be made after 30 seconds.
43+
44+
```javascript
45+
await agonesSDK.connect();
46+
```
47+
{{% /feature %}}
48+
3949
To send a [health check]({{< relref "_index.md#health" >}}) ping call `health()`.
4050

4151
```javascript

‎site/content/en/docs/Guides/Client SDKs/rust.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ if sdk.health().is_ok() {
4646
}
4747
```
4848

49+
{{% feature publishVersion="0.12.0" %}}
50+
51+
To mark the [game session as ready]({{< relref "_index.md#ready" >}}) call `sdk.ready()`.
52+
53+
```rust
54+
sdk.ready()?;
55+
```
56+
{{% /feature %}}
57+
4958
To mark that the [game session is completed]({{< relref "_index.md#shutdown" >}}) and the game server should be shut down call `sdk.shutdown()`.
5059

5160
```rust

‎test/sdk/nodejs/testSDKClient.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const agonesSDK = new AgonesSDK();
1818
const connect = async () => {
1919
let UID = '';
2020
try {
21+
console.log("attempting to connect");
22+
await agonesSDK.connect();
23+
console.log("connected!");
2124
let once = true;
2225
agonesSDK.watchGameServer((result) => {
2326
console.log('watch', result);

‎test/sdk/rust/.gitignore‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright 2019 Google LLC All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
/target/
16+
Cargo.lock

‎test/sdk/rust/src/main.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ fn run() -> Result<(), String> {
111111
thread::sleep(Duration::from_secs(5));
112112
}
113113

114-
println!("Shutting down after 20 seconds...");
114+
println!("Shutting down...");
115115
sdk.shutdown()
116116
.map_err(|e| format!("Could not run Shutdown: {}. Exiting!", e))?;
117117
println!("...marked for Shutdown");

0 commit comments

Comments
 (0)