Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade sentry to 0.8.0 and log out errors #65

Merged
merged 1 commit into from
Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 140 additions & 20 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ rusoto_core = "0.32.0"
rusoto_credential = "0.11.0"
rusoto_dynamodb = "0.32.0"
# XXX: pinned until server side's upgraded
sentry = "0.2.0"
serde = "1.0.70"
serde_derive = "1.0.70"
serde_dynamodb = "0.1.2"
Expand All @@ -71,3 +70,8 @@ woothee = "0.7.3"
[dependencies.config]
git = "https://github.com/mehcode/config-rs"
rev = "e8fa9fee96185ddd18ebcef8a925c75459111edb"

[dependencies.sentry]
features = ["with_error_chain"]
git = "https://github.com/getsentry/sentry-rust/"
rev = "9656b8b9bd1fd65f3d8be744ede1c9089019e087"
16 changes: 11 additions & 5 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ use futures::AsyncSink;
use futures::{Async, Future, Poll, Sink, Stream};
use reqwest::unstable::async::Client as AsyncClient;
use rusoto_dynamodb::UpdateItemOutput;
use sentry::integrations::error_chain::capture_error_chain;
use state_machine_future::RentToOwn;
use tokio_core::reactor::Timeout;
use tungstenite::error::Error as ConnectionError;
use uuid::Uuid;
use woothee::parser::Parser;

Expand Down Expand Up @@ -455,10 +455,16 @@ where
) -> Poll<AfterAwaitSessionComplete, Error> {
let error = {
match session_complete.auth_state_machine.poll() {
Ok(Async::Ready(_)) => None,
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(Error(ErrorKind::Ws(ConnectionError::ConnectionClosed(_)), _)) => None,
Err(e) => Some(e.display_chain().to_string()),
Ok(Async::Ready(_))
| Err(Error(ErrorKind::Ws(_), _))
| Err(Error(ErrorKind::Io(_), _))
| Err(Error(ErrorKind::PongTimeout, _))
| Err(Error(ErrorKind::RepeatUaidDisconnect, _)) => None,
Err(e) => {
capture_error_chain(&e);
Some(e.display_chain().to_string())
}
}
};

Expand Down Expand Up @@ -923,7 +929,7 @@ where
}
Either::B(ServerNotification::Disconnect) => {
debug!("Got told to disconnect, connecting client has our uaid");
Err("Repeat UAID disconnect".into())
Err(ErrorKind::RepeatUaidDisconnect.into())
}
_ => Err("Invalid message".into()),
}
Expand Down
10 changes: 8 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ use cadence;
use config;
use futures::Future;
use httparse;
use sentry;
use serde_json;
use tungstenite;
use uuid;
Expand All @@ -50,7 +49,6 @@ error_chain! {
Json(serde_json::Error);
Httparse(httparse::Error);
MetricError(cadence::MetricError);
SentryError(sentry::Error);
UuidParseError(uuid::ParseError);
ParseIntError(num::ParseIntError);
ConfigError(config::ConfigError);
Expand All @@ -60,6 +58,14 @@ error_chain! {
Thread(payload: Box<Any + Send>) {
description("thread panicked")
}

PongTimeout {
description("websocket pong timeout")
}

RepeatUaidDisconnect {
description("repeat uaid disconnected")
}
}
}

Expand Down
38 changes: 11 additions & 27 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::default::Default;
use std::env;
use std::io;
use std::net::SocketAddr;
use std::panic;
use std::panic::PanicInfo;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
Expand All @@ -25,6 +23,7 @@ use openssl::hash;
use openssl::ssl::SslAcceptor;
use reqwest;
use sentry;
use sentry::integrations::panic::register_panic_handler;
use serde_json;
use time;
use tokio_core::net::TcpListener;
Expand Down Expand Up @@ -216,9 +215,7 @@ impl Server {
/// be used to interact with it (e.g. shut it down).
fn start(opts: &Arc<ServerOptions>) -> Result<Vec<ShutdownHandle>> {
let mut shutdown_handles = vec![];
if let Some(handle) = Server::start_sentry()? {
shutdown_handles.push(handle);
}
let _guard = Server::start_sentry();

let (inittx, initrx) = oneshot::channel();
let (donetx, donerx) = oneshot::channel();
Expand Down Expand Up @@ -252,7 +249,6 @@ impl Server {
Ok(())
}));
}

core.run(donerx).expect("Main Core run error");
});

Expand All @@ -267,26 +263,14 @@ impl Server {
}

/// Setup Sentry logging if a SENTRY_DSN exists
fn start_sentry() -> Result<Option<ShutdownHandle>> {
let creds = match env::var("SENTRY_DSN") {
Ok(dsn) => dsn.parse::<sentry::SentryCredential>()?,
Err(_) => return Ok(None),
};

// Spin up a new thread with a new reactor core for the sentry handler
let (donetx, donerx) = oneshot::channel();
let thread = thread::spawn(move || {
let mut core = Core::new().expect("Unable to create core");
let sentry = sentry::Sentry::from_settings(core.handle(), Default::default(), creds);
// Get the prior panic hook
let hook = panic::take_hook();
sentry.register_panic_handler(Some(move |info: &PanicInfo| -> () {
hook(info);
}));
core.run(donerx).expect("Sentry Core run error");
});

Ok(Some(ShutdownHandle(donetx, thread)))
fn start_sentry() -> Option<sentry::internals::ClientInitGuard> {
if let Ok(dsn) = env::var("SENTRY_DSN") {
let guard = sentry::init(dsn);
register_panic_handler();
Some(guard)
} else {
None
}
}

fn new(opts: &Arc<ServerOptions>) -> Result<(Rc<Server>, Core)> {
Expand Down Expand Up @@ -891,7 +875,7 @@ where
// elapsed (set above) then this is where we start
// triggering errors.
if self.ws_pong_timeout {
return Err("failed to receive a ws pong in time".into());
return Err(ErrorKind::PongTimeout.into());
}
return Ok(Async::NotReady);
}
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
bottle==0.12.13
coverage
ecdsa==0.13
factory_boy==2.8.1
Expand Down
47 changes: 45 additions & 2 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from queue import Queue, Empty
from unittest.case import SkipTest

import bottle
import ecdsa
import psutil
import requests
Expand Down Expand Up @@ -76,6 +77,7 @@
CN_SERVER = None
CN_MP_SERVER = None
MOCK_SERVER_THREAD = None
MOCK_SENTRY_THREAD = None
CN_QUEUES = []


Expand All @@ -88,6 +90,8 @@ def get_free_port():


MOCK_MP_SERVER_PORT = get_free_port()
MOCK_SENTRY_PORT = get_free_port()
MOCK_SENTRY_QUEUE = Queue()


def enqueue_output(out, queue):
Expand All @@ -97,7 +101,8 @@ def enqueue_output(out, queue):


def setup_module():
global CN_SERVER, CN_QUEUES, CN_MP_SERVER, MOCK_SERVER_THREAD
global CN_SERVER, CN_QUEUES, CN_MP_SERVER, MOCK_SERVER_THREAD, \
MOCK_SENTRY_THREAD
ap_tests.ddb_jar = os.path.join(root_dir, "ddb", "DynamoDBLocal.jar")
ap_tests.ddb_lib_dir = os.path.join(root_dir, "ddb", "DynamoDBLocal_lib")
ap_tests.setUp()
Expand Down Expand Up @@ -151,9 +156,22 @@ def setup_module():
t.start()
CN_QUEUES.extend([out_q, err_q])

# Sentry API mock
os.environ["SENTRY_DSN"] = 'http://foo:bar@localhost:{}/1'.format(
MOCK_SENTRY_PORT
)
MOCK_SENTRY_THREAD = Thread(
target=bottle.run,
kwargs=dict(
port=MOCK_SENTRY_PORT, debug=True
))
MOCK_SENTRY_THREAD.setDaemon(True)
MOCK_SENTRY_THREAD.start()

# Megaphone API mock
MockMegaphoneRequestHandler.services = {}
MockMegaphoneRequestHandler.polled.clear()

mock_server = HTTPServer(('localhost', MOCK_MP_SERVER_PORT),
MockMegaphoneRequestHandler)
MOCK_SERVER_THREAD = Thread(target=mock_server.serve_forever)
Expand Down Expand Up @@ -183,7 +201,7 @@ def setup_module():

cmd = [rust_bin]
CN_MP_SERVER = subprocess.Popen(cmd, shell=True, env=os.environ)
time.sleep(1)
time.sleep(5)


def teardown_module():
Expand Down Expand Up @@ -223,6 +241,17 @@ def do_GET(self):
return


@bottle.route("<path:re:.*>")
def sentry_handler(path):
global MOCK_SENTRY_QUEUE
log.critical("Got a request!")
content = bottle.request.json
MOCK_SENTRY_QUEUE.put(content)
return {
"id": "fc6d8c0c43fc4630ad850ee518f1b9d0"
}


class TestRustWebPush(unittest.TestCase):
_endpoint_defaults = dict(
hostname='localhost',
Expand Down Expand Up @@ -267,6 +296,8 @@ def tearDown(self):
is_empty = True
else:
print(line)
while not MOCK_SENTRY_QUEUE.empty():
MOCK_SENTRY_QUEUE.get_nowait()

def endpoint_kwargs(self):
return self._endpoint_defaults
Expand Down Expand Up @@ -295,6 +326,18 @@ def legacy_endpoint(self):
def _ws_url(self):
return "ws://localhost:{}/".format(CONNECTION_PORT)

@inlineCallbacks
def test_sentry_output(self):
raise SkipTest("Skip until we know why locally sentry fails")
client = Client(self._ws_url)
yield client.connect()
yield client.hello()
# Send unsolicited ack and drop
yield client.ack("garbage", "data")
yield self.shut_down(client)
data = MOCK_SENTRY_QUEUE.get(timeout=1)
assert data == "Fred"

@inlineCallbacks
def test_hello_echo(self):
client = Client(self._ws_url)
Expand Down