Skip to content

Commit

Permalink
Implement lacking functions in Rust SDK
Browse files Browse the repository at this point in the history
This implements new functions below in Rust SDK and updates related documents and examples.

- SetLabel(key, value)
- SetAnnotation(key, value)
- GameServer()
- WatchGameServer(function(gameserver){...})
  • Loading branch information
thara authored and markmandel committed Apr 14, 2019
1 parent 2dad5fd commit bafcfea
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 33 deletions.
37 changes: 32 additions & 5 deletions examples/rust-simple/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,19 @@ fn main() {
Ok(_) => {
println!("Rust Game Server finished.");
0
},
}
Err(msg) => {
println!("{}", msg);
1
}
});
}

fn run() -> Result<(), String>{

fn run() -> Result<(), String> {
println!("Creating SDK instance");
let sdk = agones::Sdk::new().map_err(|_| "Could not connect to the sidecar. Exiting!")?;

let _t = thread::spawn(enclose!{(sdk) move || {
let _health = thread::spawn(enclose! {(sdk) move || {
loop {
match sdk.health() {
(s, Ok(_)) => {
Expand All @@ -63,7 +62,34 @@ fn run() -> Result<(), String>{
}
}});

let _watch = thread::spawn(enclose! {(sdk) move || {
println!("Starting to watch GameServer updates...");
let _ = sdk.watch_gameserver(|gameserver| {
println!("GameServer Update, name: {}", gameserver.object_meta.unwrap().name);
println!("GameServer Update, state: {}", gameserver.status.unwrap().state);
});
}});

println!("Setting a label");
sdk.set_label("test-label", "test-value")
.map_err(|e| format!("Could not run SetLabel(): {}. Exiting!", e))?;

println!("Setting an annotation");
sdk.set_annotation("test-annotation", "test value")
.map_err(|e| format!("Could not run SetAnnotation(): {}. Exiting!", e))?;

println!("Marking server as ready...");
sdk.ready()
.map_err(|e| format!("Could not run Ready(): {}. Exiting!", e))?;

println!("...marked Ready");

println!("Getting GameServer details...");
let gameserver = sdk
.get_gameserver()
.map_err(|e| format!("Could not run GameServer(): {}. Exiting!", e))?;

println!("GameServer name: {}", gameserver.object_meta.unwrap().name);

for i in 0..10 {
let time = i * 10;
Expand All @@ -73,7 +99,8 @@ fn run() -> Result<(), String>{

if i == 5 {
println!("Shutting down after 60 seconds...");
sdk.shutdown().map_err(|e| format!("Could not run Shutdown: {}. Exiting!", e))?;
sdk.shutdown()
.map_err(|e| format!("Could not run Shutdown: {}. Exiting!", e))?;
println!("...marked for Shutdown");
}
}
Expand Down
3 changes: 0 additions & 3 deletions sdks/rust/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Rust Game Server Client SDK

> The Rust SDK has not been actively maintained, and doesn't have all the SDK functionality, although it _should_ still work.
Pull Requests and updates are welcome.

This is the Rust version of the Agones Game Server Client SDK.

See the [Rust SDK Documentation](https://agones.dev/site/docs/guides/client-sdks/rust/) for details
Expand Down
3 changes: 1 addition & 2 deletions sdks/rust/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.


// Wrap in a new error
error_chain!{
error_chain! {
foreign_links {
Grpc(::grpcio::Error);
}
Expand Down
5 changes: 3 additions & 2 deletions sdks/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
//! the Rust game server SDK
#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate grpcio;
extern crate grpcio_proto;
extern crate protobuf;
extern crate futures;

pub mod errors;
mod grpc;
mod sdk;
pub mod errors;
pub mod types;

pub use sdk::Sdk;
97 changes: 81 additions & 16 deletions sdks/rust/src/sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,43 @@
// See the License for the specific language governing permissions and
// limitations under the License.


use futures::{Future, Sink, Stream};
use grpcio;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use grpcio;
use futures::{Future, Sink};

use errors::*;
use grpc::sdk as sdk;
use grpc::sdk_grpc as sdk_grpc;
use grpc::sdk;
use grpc::sdk_grpc;
use protobuf::Message;
use types::*;

const PORT: i32 = 59357;

/// SDK is an instance of the Agones SDK
pub struct Sdk {
client : Arc<sdk_grpc::SdkClient>,
health : Arc<Mutex<Option<grpcio::ClientCStreamSender<sdk::Empty>>>>,
client: Arc<sdk_grpc::SdkClient>,
health: Arc<Mutex<Option<grpcio::ClientCStreamSender<sdk::Empty>>>>,
}

impl Sdk {

/// Starts a new SDK instance, and connects to localhost on port 59357.
/// Blocks until connection and handshake are made.
/// Times out after 30 seconds.
pub fn new() -> Result<Sdk> {
let addr = format!("localhost:{}", PORT);
let env = Arc::new(grpcio::EnvBuilder::new().build());
let ch = grpcio::ChannelBuilder::new(env).keepalive_timeout(Duration::new(30, 0)).connect(&addr);
let ch = grpcio::ChannelBuilder::new(env)
.keepalive_timeout(Duration::new(30, 0))
.connect(&addr);
let cli = sdk_grpc::SdkClient::new(ch);
let req = sdk::Empty::new();
let _ = cli.ready(&req).map(Box::new)?;
let (sender, _) = cli.health()?;
Ok(Sdk{client: Arc::new(cli), health: Arc::new(Mutex::new(Some(sender)))})
Ok(Sdk {
client: Arc::new(cli),
health: Arc::new(Mutex::new(Some(sender))),
})
}

/// Marks the Game Server as ready to receive connections
Expand All @@ -66,20 +70,81 @@ impl Sdk {
// Avoid `cannot move out of borrowed content` compile error for self.health
let h = self.health.lock().unwrap().take();
if h.is_none() {
return (self, Err(ErrorKind::HealthPingConnectionFailure("failed to hold client stream for health ping".to_string()).into()));
return (
self,
Err(ErrorKind::HealthPingConnectionFailure(
"failed to hold client stream for health ping".to_string(),
)
.into()),
);
}
let h : grpcio::ClientCStreamSender<sdk::Empty> = h.unwrap().into();
let h: grpcio::ClientCStreamSender<sdk::Empty> = h.unwrap().into();

let req = sdk::Empty::new();
match h.send((req, grpcio::WriteFlags::default())).wait() {
Ok(h) => {
self.health = Arc::new(Mutex::new(Some(h)));
(self, Ok(()))
},
Err(e) => {
(self, Err(ErrorKind::Grpc(e).into()))
},
}
Err(e) => (self, Err(ErrorKind::Grpc(e).into())),
}
}

/// Set a Label value on the backing GameServer record that is stored in Kubernetes
pub fn set_label<S>(&self, key: S, value: S) -> Result<()>
where
S: Into<String>,
{
let mut kv = sdk::KeyValue::new();
kv.set_key(key.into());
kv.set_value(value.into());
let res = self.client.set_label(&kv).map(|_| ())?;
Ok(res)
}

/// Set a Annotation value on the backing Gameserver record that is stored in Kubernetes
pub fn set_annotation<S>(&self, key: S, value: S) -> Result<()>
where
S: Into<String>,
{
let mut kv = sdk::KeyValue::new();
kv.set_key(key.into());
kv.set_value(value.into());
let res = self.client.set_annotation(&kv).map(|_| ())?;
Ok(res)
}

/// Returns most of the backing GameServer configuration and Status
pub fn get_gameserver(&self) -> Result<GameServer> {
let req = sdk::Empty::new();
let res = self
.client
.get_game_server(&req)
.map(|e| GameServer::from_message(e))?;
Ok(res)
}

/// Watch the backing GameServer configuration on updated
pub fn watch_gameserver<F>(&self, mut watcher: F) -> Result<()>
where
F: FnMut(GameServer) -> (),
{
let req = sdk::Empty::new();
let mut receiver = self
.client
.watch_game_server(&req)?
.map(|e| GameServer::from_message(e));
loop {
match receiver.into_future().wait() {
Ok((Some(game_server), r)) => {
watcher(game_server);
receiver = r;
}
Ok((None, _)) => break,
Err((e, _)) => return Err(e.into()),
}
}
Ok(())
}
}

Expand Down
144 changes: 144 additions & 0 deletions sdks/rust/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::HashMap;

use grpc::sdk;

#[derive(PartialEq, Clone, Default)]
pub struct GameServer {
// message fields
pub object_meta: Option<GameServerObjectMeta>,
pub spec: Option<GameServerSpec>,
pub status: Option<GameServerStatus>,
}

impl GameServer {
pub fn from_message(msg: sdk::GameServer) -> GameServer {
GameServer {
object_meta: msg
.object_meta
.into_option()
.map(|e| GameServerObjectMeta::from_message(e)),
spec: msg
.spec
.into_option()
.map(|e| GameServerSpec::from_message(e)),
status: msg
.status
.into_option()
.map(|e| GameServerStatus::from_message(e)),
}
}
}

#[derive(PartialEq, Clone, Default)]
pub struct GameServerObjectMeta {
pub name: String,
pub namespace: String,
pub uid: String,
pub resource_version: String,
pub generation: i64,
pub creation_timestamp: i64,
pub deletion_timestamp: i64,
pub annotations: HashMap<String, String>,
pub labels: HashMap<String, String>,
}

impl GameServerObjectMeta {
pub fn from_message(msg: sdk::GameServer_ObjectMeta) -> GameServerObjectMeta {
GameServerObjectMeta {
name: msg.name,
namespace: msg.namespace,
uid: msg.uid,
resource_version: msg.resource_version,
generation: msg.generation,
creation_timestamp: msg.creation_timestamp,
deletion_timestamp: msg.deletion_timestamp,
annotations: msg.annotations,
labels: msg.labels,
}
}
}

#[derive(PartialEq, Clone, Default)]
pub struct GameServerSpec {
pub health: Option<GameServerSpecHealth>,
}

impl GameServerSpec {
pub fn from_message(msg: sdk::GameServer_Spec) -> GameServerSpec {
GameServerSpec {
health: msg
.health
.into_option()
.map(|e| GameServerSpecHealth::from_message(e)),
}
}
}

#[derive(PartialEq, Clone, Default)]
pub struct GameServerSpecHealth {
// message fields
pub disabled: bool,
pub period_seconds: i32,
pub failure_threshold: i32,
pub initial_delay_seconds: i32,
}

impl GameServerSpecHealth {
pub fn from_message(msg: sdk::GameServer_Spec_Health) -> GameServerSpecHealth {
GameServerSpecHealth {
disabled: msg.Disabled,
period_seconds: msg.PeriodSeconds,
failure_threshold: msg.FailureThreshold,
initial_delay_seconds: msg.InitialDelaySeconds,
}
}
}

#[derive(PartialEq, Clone, Default)]
pub struct GameServerStatus {
pub state: String,
pub address: String,
pub ports: Vec<GameServerStatusPort>,
}

impl GameServerStatus {
pub fn from_message(msg: sdk::GameServer_Status) -> GameServerStatus {
GameServerStatus {
state: msg.state,
address: msg.address,
ports: msg
.ports
.into_iter()
.map(|e| GameServerStatusPort::from_message(e))
.collect(),
}
}
}

#[derive(PartialEq, Clone, Default)]
pub struct GameServerStatusPort {
pub name: String,
pub port: i32,
}

impl GameServerStatusPort {
pub fn from_message(msg: sdk::GameServer_Status_Port) -> GameServerStatusPort {
GameServerStatusPort {
name: msg.name,
port: msg.port,
}
}
}
Loading

0 comments on commit bafcfea

Please sign in to comment.