<commit_before>pub fn valid_nick(nick: &String) -> bool {

	// TODO: Validate nick name

	false
}

pub fn pass(pass: &String) -> String {
	format!("PASS {}", pass)
}

pub fn nick(nick: &String) -> String {
	format!("NICK {}", nick)
}

pub fn user(user: &String, mode: &i32, realname: &String) -> String {
	format!("USER {} {} * :{}", user, mode.to_string(), realname)
}

pub fn oper(name: &String, pass: &String) -> String {
	format!("OPER {} {}", name, pass)
}

pub fn privmsg(dest: &String, message: &String) -> String {
	format!("PRIVMSG {} :{}", dest, message)
}

pub fn notice(dest: &String, message: &String) -> String {
	format!("NOTICE {} :{}", dest, message)
}

pub fn join(channel: &String) -> String {
	format!("JOIN {}", channel)
}

pub fn join_multi(channels: &[String]) -> String {
	format!("JOIN {}", channels.to_vec().join(","))
}

pub fn join_key(channel: &String, key: &String) -> String {
	format!("JOIN {} {}", channel, key)
}

pub fn join_key_multi(channels: &[String], keys: &[String]) -> String {
	format!("JOIN {} {}", channels.to_vec().join(","), keys.to_vec().join(","))
}

pub fn part(channel: &String) -> String {
	format!("PART {}", channel)
}

pub fn part_multi(channels: &[String]) -> String {
	format!("PART {}", channels.to_vec().join(","))
}

pub fn part_msg(channel: &String, message: &String) -> String {
	format!("PART {} :{}", channel, message)
}

pub fn part_msg_multi(channels: &[String], message: &String) -> String {
	format!("PART {} :{}", channels.to_vec().join(","), message)
}

pub fn kick(channel: &String, nick: &String) -> String {
	format!("KICK {} {}", channel, nick)
}

pub fn kick_comment(channel: &String, nick: &String, comment: &String) -> String {
	format!("KICK {} {} :{}", channel, nick, comment)
}

pub fn kick_multi(channels: &[String], nick: &String) -> String {
	format!("KICK {} {}", channels.to_vec().join(","), nick)
}

pub fn kick_comment_multi(channels: &[String], nick: &String, comment: &String) -> String {
	format!("KICK {} {} :{}", channels.to_vec().join(","), nick, comment)
}

pub fn kick_multi_nick(channel: &String, nicks: &[String]) -> String {
	format!("KICK {} {}", channel, nicks.to_vec().join(","))
}

pub fn kick_comment_multi_nick(channel: &String, nicks: &[String], comment: &String) -> String {
	format!("KICK {} {} :{}", channel, nicks.to_vec().join(","), comment)
}

pub fn kick_mutlti_nick_channel(channels: &[String], nicks: &[String]) -> String {
	format!("KICK {} {}", channels.to_vec().join(","), nicks.to_vec().join(","))
}

pub fn kick_comment_multi_nick_channel(
	channels: &[String],
	nicks: &[String],
	comment: &String
) -> String {

	format!("KICK {} {} :{}", channels.to_vec().join(","), nicks.to_vec().join(","), comment)
}

pub fn motd() -> String {
	String::from("MOTD")
}

pub fn motd_target(target: &String) -> String {
	format!("MOTD {}", target)
}

pub fn lusers() -> String {
	String::from("LUSERS")
}

pub fn lusers_mask(mask: &String) -> String {
	format!("LUSER {}", mask)
}

pub fn lusers_target(mask: &String, target: &String) -> String {
	format!("LUSER {} {}", mask, target)
}

pub fn version() -> String {
	String::from("VERSION")
}

pub fn version_target(target: &String) -> String {
	format!("VERSION {}", target)
}

pub fn stats() -> String {
	String::from("STATS")
}

pub fn stats_query(query: &String) -> String {
	format!("STATS {}", query)
}

pub fn stats_target(query: &String, target: &String) -> String {
	format!("STATS {} {}", query, target)
}

pub fn links() -> String {
	String::from("LINKS")
}

pub fn links_mask(mask: &String) -> String {
	format!("LINKS {}", mask)
}

pub fn links_remote(remote: &String, mask: &String) -> String {
	format!("LINKS {} {}", remote, mask)
}

pub fn time() -> String {
	String::from("TIME")
}

pub fn time_target(target: &String) -> String {
	format!("TIME {}", target)
}

pub fn connect(server: &String, port: &String) -> String {
	format!("CONNECT {} {}", server, port)
}

pub fn connect_remote(server: &String, port: &String, remote: &String) -> String {
	format!("CONNECT {} {} {}", server, port, remote)
}

pub fn trace() -> String {
	String::from("TRACE")
}

pub fn trace_target(target: &String) -> String {
	format!("TRACE {}", target)
}

pub fn admin() -> String {
	String::from("ADMIN")
}

pub fn admin_target(target: &String) -> String {
	format!("ADMIN {}", target)
}

pub fn info() -> String {
	String::from("INFO")
}

pub fn info_target(target: &String) -> String {
	format!("INFO {}", target)
}

pub fn serv_list() -> String {
	String::from("SERVLIST")
}

pub fn serv_list_mask(mask: &String) -> String {
	format!("SERVLIST {}", mask)
}

pub fn serv_list_mask_type(mask: &String, masktype: &String) -> String {
	format!("SERVLIST {} {}", mask, masktype)
}

pub fn squery(name: &String, text: &String) -> String {
	format!("SQUERY {} :{}", name, text)
}

pub fn list() -> String {
	String::from("LIST")
}

pub fn list_channel(channel: &String) -> String {
	format!("LIST {}", channel)
}

pub fn list_channels(channels: &[String]) -> String {
	format!("LIST {}", channels.to_vec().join(","))
}

pub fn list_channel_target(channel: &String, target: &String) -> String {
	format!("LIST {} {}", channel, target)
}

pub fn list_channels_target(channels: &[String], target: &String) -> String {
	format!("LIST {} {}", channels.to_vec().join(","), target)
}

pub fn names() -> String {
	String::from("NAMES")
}

pub fn names_channel(channel: &String) -> String {
	format!("NAMES {}", channel)
}

pub fn names_channels(channels: &[String]) -> String {
	format!("NAMES {}", channels.to_vec().join(","))
}

pub fn names_channel_target(channel: &String, target: &String) -> String {
	format!("NAMES {} {}", channel, target)
}

pub fn names_channels_target(channels: &[String], target: &String) -> String {
	format!("NAMES {} {}", channels.to_vec().join(","), target)
}

pub fn topic(channel: &String) -> String {
	format!("TOPIC {}", channel)
}

pub fn topic_new(channel: &String, newtopic: &String) -> String {
	format!("TOPIC {} :{}", channel, newtopic)
}

pub fn mode(target: &String) -> String {
	format!("MODE {}", target)
}

pub fn mode_new(target: &String, newmode: &String) -> String {
	format!("MODE {} {}", target, newmode)
}

pub fn mode_new_params(target: &String, modes: &[String], params: &[String]) -> String {
	// TODO: Parse params and modes

	unimplemented!();
}

pub fn service(nick: &String, distro: &String, info: &String) -> String {
	format!("SERVICE {} * {} * * :{}", nick, distro, info)
}

pub fn invite(nick: &String, channel: &String) -> String {
	format!("INVITE {} {}", nick, channel)
}

pub fn who() -> String {
	String::from("WHO")
}

pub fn who_mask(mask: &String) -> String {
	format!("WHO {}", mask)
}

pub fn who_mask_ircop(mask: &String, ircop: &bool) -> String {
	if ircop {
		format!("WHO {} o", mask)
	}
	format!("WHO {}", mask)
}

pub fn whois_mask(mask: &String) -> String {
	format!("WHOIS {}", mask)
}

pub fn whois_masks(masks: &[String]) -> String {
	format!("WHOIS {}", masks.to_vec().join(","))
}

pub fn whois_target_mask(target: &String, mask: &String) -> String {
	format!("WHOIS {} {}", target, mask)
}

pub fn whois_target_masks(target: &String, masks: &[String]) -> String {
	format!("WHOIS {} {}", target, masks.to_vec().join(","))
}

pub fn whowas_nick(nick: &String) -> String {
	format!("WHOWAS {}", nick)
}

// TODO: define all whowas signatures
/*
TODO: define all:
	kill, ping, pong, away, rehash, die, restart, summon, users, wallops, userhost,
	ison, quit, squit
*/
<commit_msg>Completed implementation of RFC IRC API<commit_after>pub fn valid_nick(nick: &String) -> bool {

	// TODO: Validate nick name

	false
}

pub fn pass(pass: &String) -> String {
	format!("PASS {}", pass)
}

pub fn nick(nick: &String) -> String {
	format!("NICK {}", nick)
}

pub fn user(user: &String, mode: &i32, realname: &String) -> String {
	format!("USER {} {} * :{}", user, mode.to_string(), realname)
}

pub fn oper(name: &String, pass: &String) -> String {
	format!("OPER {} {}", name, pass)
}

pub fn privmsg(dest: &String, message: &String) -> String {
	format!("PRIVMSG {} :{}", dest, message)
}

pub fn notice(dest: &String, message: &String) -> String {
	format!("NOTICE {} :{}", dest, message)
}

pub fn join_channel(channel: &String) -> String {
	format!("JOIN {}", channel)
}

pub fn join_channels(channels: &[String]) -> String {
	format!("JOIN {}", channels.to_vec().join(","))
}

pub fn join_channel_key(channel: &String, key: &String) -> String {
	format!("JOIN {} {}", channel, key)
}

pub fn join_channels_keys(channels: &[String], keys: &[String]) -> String {
	format!("JOIN {} {}", channels.to_vec().join(","), keys.to_vec().join(","))
}

pub fn part_channel(channel: &String) -> String {
	format!("PART {}", channel)
}

pub fn part_channels(channels: &[String]) -> String {
	format!("PART {}", channels.to_vec().join(","))
}

pub fn part_channel_message(channel: &String, message: &String) -> String {
	format!("PART {} :{}", channel, message)
}

pub fn part_channels_message(channels: &[String], message: &String) -> String {
	format!("PART {} :{}", channels.to_vec().join(","), message)
}

pub fn kick_channel(channel: &String, nick: &String) -> String {
	format!("KICK {} {}", channel, nick)
}

pub fn kick_channel_comment(channel: &String, nick: &String, comment: &String) -> String {
	format!("KICK {} {} :{}", channel, nick, comment)
}

pub fn kick_channels(channels: &[String], nick: &String) -> String {
	format!("KICK {} {}", channels.to_vec().join(","), nick)
}

pub fn kick_channels_nick_comment(channels: &[String], nick: &String, comment: &String) -> String {
	format!("KICK {} {} :{}", channels.to_vec().join(","), nick, comment)
}

pub fn kick_channel_nicks(channel: &String, nicks: &[String]) -> String {
	format!("KICK {} {}", channel, nicks.to_vec().join(","))
}

pub fn kick_channel_nicks_comment(channel: &String, nicks: &[String], comment: &String) -> String {
	format!("KICK {} {} :{}", channel, nicks.to_vec().join(","), comment)
}

pub fn kick_channels_nicks(channels: &[String], nicks: &[String]) -> String {
	format!("KICK {} {}", channels.to_vec().join(","), nicks.to_vec().join(","))
}

pub fn kick_channels_nicks_comment(
	channels: &[String],
	nicks: &[String],
	comment: &String
) -> String {

	format!("KICK {} {} :{}", channels.to_vec().join(","), nicks.to_vec().join(","), comment)
}

pub fn motd() -> String {
	String::from("MOTD")
}

pub fn motd_target(target: &String) -> String {
	format!("MOTD {}", target)
}

pub fn lusers() -> String {
	String::from("LUSERS")
}

pub fn lusers_mask(mask: &String) -> String {
	format!("LUSER {}", mask)
}

pub fn lusers_target(mask: &String, target: &String) -> String {
	format!("LUSER {} {}", mask, target)
}

pub fn version() -> String {
	String::from("VERSION")
}

pub fn version_target(target: &String) -> String {
	format!("VERSION {}", target)
}

pub fn stats() -> String {
	String::from("STATS")
}

pub fn stats_query(query: &String) -> String {
	format!("STATS {}", query)
}

pub fn stats_target(query: &String, target: &String) -> String {
	format!("STATS {} {}", query, target)
}

pub fn links() -> String {
	String::from("LINKS")
}

pub fn links_mask(mask: &String) -> String {
	format!("LINKS {}", mask)
}

pub fn links_remote(remote: &String, mask: &String) -> String {
	format!("LINKS {} {}", remote, mask)
}

pub fn time() -> String {
	String::from("TIME")
}

pub fn time_target(target: &String) -> String {
	format!("TIME {}", target)
}

pub fn connect(server: &String, port: &String) -> String {
	format!("CONNECT {} {}", server, port)
}

pub fn connect_remote(server: &String, port: &String, remote: &String) -> String {
	format!("CONNECT {} {} {}", server, port, remote)
}

pub fn trace() -> String {
	String::from("TRACE")
}

pub fn trace_target(target: &String) -> String {
	format!("TRACE {}", target)
}

pub fn admin() -> String {
	String::from("ADMIN")
}

pub fn admin_target(target: &String) -> String {
	format!("ADMIN {}", target)
}

pub fn info() -> String {
	String::from("INFO")
}

pub fn info_target(target: &String) -> String {
	format!("INFO {}", target)
}

pub fn serv_list() -> String {
	String::from("SERVLIST")
}

pub fn serv_list_mask(mask: &String) -> String {
	format!("SERVLIST {}", mask)
}

pub fn serv_list_mask_type(mask: &String, masktype: &String) -> String {
	format!("SERVLIST {} {}", mask, masktype)
}

pub fn squery(name: &String, text: &String) -> String {
	format!("SQUERY {} :{}", name, text)
}

pub fn list() -> String {
	String::from("LIST")
}

pub fn list_channel(channel: &String) -> String {
	format!("LIST {}", channel)
}

pub fn list_channels(channels: &[String]) -> String {
	format!("LIST {}", channels.to_vec().join(","))
}

pub fn list_channel_target(channel: &String, target: &String) -> String {
	format!("LIST {} {}", channel, target)
}

pub fn list_channels_target(channels: &[String], target: &String) -> String {
	format!("LIST {} {}", channels.to_vec().join(","), target)
}

pub fn names() -> String {
	String::from("NAMES")
}

pub fn names_channel(channel: &String) -> String {
	format!("NAMES {}", channel)
}

pub fn names_channels(channels: &[String]) -> String {
	format!("NAMES {}", channels.to_vec().join(","))
}

pub fn names_channel_target(channel: &String, target: &String) -> String {
	format!("NAMES {} {}", channel, target)
}

pub fn names_channels_target(channels: &[String], target: &String) -> String {
	format!("NAMES {} {}", channels.to_vec().join(","), target)
}

pub fn topic(channel: &String) -> String {
	format!("TOPIC {}", channel)
}

pub fn topic_new(channel: &String, newtopic: &String) -> String {
	format!("TOPIC {} :{}", channel, newtopic)
}

pub fn mode(target: &String) -> String {
	format!("MODE {}", target)
}

pub fn mode_new(target: &String, newmode: &String) -> String {
	format!("MODE {} {}", target, newmode)
}

pub fn mode_new_params(target: &String, modes: &[String], params: &[String]) -> String {
	// TODO: Parse params and modes

	unimplemented!();
}

pub fn service(nick: &String, distro: &String, info: &String) -> String {
	format!("SERVICE {} * {} * * :{}", nick, distro, info)
}

pub fn invite(nick: &String, channel: &String) -> String {
	format!("INVITE {} {}", nick, channel)
}

pub fn who() -> String {
	String::from("WHO")
}

pub fn who_mask(mask: &String) -> String {
	format!("WHO {}", mask)
}

pub fn who_mask_ircop(mask: &String, ircop: &bool) -> String {
	if *ircop {
		return format!("WHO {} o", mask)
	}
	format!("WHO {}", mask)
}

pub fn whois_mask(mask: &String) -> String {
	format!("WHOIS {}", mask)
}

pub fn whois_masks(masks: &[String]) -> String {
	format!("WHOIS {}", masks.to_vec().join(","))
}

pub fn whois_target_mask(target: &String, mask: &String) -> String {
	format!("WHOIS {} {}", target, mask)
}

pub fn whois_target_masks(target: &String, masks: &[String]) -> String {
	format!("WHOIS {} {}", target, masks.to_vec().join(","))
}

pub fn whowas_nick(nick: &String) -> String {
	format!("WHOWAS {}", nick)
}

pub fn whowas_nicks(nicks: &[String]) -> String {
	format!("WHOWAS {}", nicks.to_vec().join(","))
}

pub fn whowas_nick_count(nick: &String, count: &String) -> String {
	format!("WHOWAS {} {}", nick, count)
}

pub fn whowas_nicks_count(nicks: &[String], count: &String) -> String {
	format!("WHOWAS {} {}", nicks.to_vec().join(","), count)
}

pub fn whowas_nick_count_target(nick: &String, count: &String, target: &String) -> String {
	format!("WHOWAS {} {} {}", nick, count, target)
}

pub fn whowas_nicks_count_target(nicks: &[String], count: &String, target: &String) -> String {
	format!("WHOWAS {} {} {}", nicks.to_vec().join(","), count, target)
}

pub fn kill(nick: &String, comment: &String) -> String {
	format!("KILL {} :{}", nick, comment)
}

pub fn ping_one(server: &String) -> String {
	format!("PING {}", server)
}

pub fn ping_two(server: &String, server2: &String) -> String {
	format!("PING {} {}", server, server2)
}

pub fn pong_one(server: &String) -> String {
	format!("PONG {}", server)
}

pub fn pong_two(server: &String, server2: &String) -> String {
	format!("PONG {} {}", server, server2)
}

pub fn error(message: &String) -> String {
	format!("ERROR :{}", message)
}

pub fn away() -> String {
	String::from("AWAY")
}

pub fn away_text(text: &String) -> String {
	format!("AWAY :{}", text)
}

pub fn rehash() -> String {
	String::from("REHASH")
}

pub fn die() -> String {
	String::from("DIE")
}

pub fn restart() -> String {
	String::from("RESTART")
}

pub fn summon_user(user: &String) -> String {
	format!("SUMMON {}", user)
}

pub fn summon_user_target(user: &String, target: &String) -> String {
	format!("SUMMON {} {}", user, target)
}

pub fn summon_user_target_channel(user: &String, target: &String, channel: &String) -> String {
	format!("SUMMON {} {} {}", user, target, channel)
}

pub fn users() -> String {
	String::from("USERS")
}

pub fn users_target(target: &String) -> String {
	format!("USERS {}", target)
}

pub fn wallops(text: &String) -> String {
	format!("WALLOPS :{}", text)
}

pub fn userhost_nick(nick: &String) -> String {
	format!("USERHOST {}", nick)
}

pub fn userhost_nicks(nicks: &[String]) -> String {
	format!("USERHOST {}", nicks.to_vec().join(","))
}

pub fn ison_nick(nick: &String) -> String {
	format!("ISON {}", nick)
}

pub fn ison_nicks(nicks: &[String]) -> String {
	format!("ISON {}", nicks.to_vec().join(","))
}

pub fn quit() -> String {
	String::from("QUIT")
}

pub fn quit_message(message: &String) -> String {
	format!("QUIT {}", message)
}

pub fn squit(server: &String, comment: &String) -> String {
	format!("SQUIT {} :{}", server, comment)
}
<|endoftext|>


<commit_before>// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! jsonrpc server over win named pipes
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate json_ipc_server;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use json_ipc_server::Server;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let server = Server::new("/tmp/json-ipc-test.ipc", &Arc::new(io)).unwrap();
//!     ::std::thread::spawn(move || server.run());
//! }
//! ```

//! Named pipes library

use miow::pipe::{NamedPipe, NamedPipeBuilder};
use std;
use std::io;
use std::io::{Read, Write};
use std::sync::atomic::*;
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use validator;

pub type Result<T> = std::result::Result<T, Error>;

const MAX_REQUEST_LEN: u32 = 65536;
const REQUEST_READ_BATCH: usize = 4096;

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    NotStarted,
    AlreadyStopping,
    NotStopped,
    IsStopping,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(io_error: std::io::Error) -> Error {
        Error::Io(io_error)
    }
}

pub struct PipeHandler {
    waiting_pipe: NamedPipe,
    io_handler: Arc<IoHandler>,
}

impl PipeHandler {
    /// start ipc rpc server (blocking)
    pub fn start(addr: &str, io_handler: &Arc<IoHandler>) -> Result<PipeHandler> {
        Ok(PipeHandler {
            waiting_pipe: try!(
                NamedPipeBuilder::new(addr)
                    .first(true)
                    .accept_remote(true)
                    .max_instances(255)
                    .inbound(true)
                    .outbound(true)
                    .out_buffer_size(MAX_REQUEST_LEN)
                    .in_buffer_size(MAX_REQUEST_LEN)
                    .create()
            ),
            io_handler: io_handler.clone(),
        })
    }

    fn handle_incoming(&mut self, addr: &str, stop: Arc<AtomicBool>) -> io::Result<()> {
        trace!(target: "ipc", "Waiting for client: [{}]", addr);
		try!(self.waiting_pipe.connect());
		trace!(target: "ipc", "Received connection to address [{}]", addr);
        if stop.load(Ordering::Relaxed) {
            trace!(target: "ipc", "Stopped listening sequence [{}]", addr);
            return Ok(())
        }

        let mut connected_pipe = std::mem::replace::<NamedPipe>(&mut self.waiting_pipe,
            try!(NamedPipeBuilder::new(addr)
                .first(false)
                .accept_remote(true)
                .inbound(true)
                .outbound(true)
                .out_buffer_size(MAX_REQUEST_LEN)
                .in_buffer_size(MAX_REQUEST_LEN)
                .create()));

        let thread_handler = self.io_handler.clone();
        std::thread::spawn(move || {
            let mut buf = vec![0u8; MAX_REQUEST_LEN as usize];
            let mut fin = REQUEST_READ_BATCH;
            loop {
                let start = fin - REQUEST_READ_BATCH;
                trace!(target: "ipc", "Reading {} - {} of the buffer", start, fin);
                match connected_pipe.read(&mut buf[start..fin]) {
                    Ok(size) => {
                        let (requests, last_index) = {
                            let effective = &buf[0..start + size];
                            fin = fin + size;
                            trace!(target: "ipc", "Received rpc request: {} bytes", effective.len());

                            validator::extract_requests(effective)
                        };

                        if requests.len() > 0 {
                            let mut response_buf = Vec::new();
                            for rpc_msg in requests  {
                                let response: Option<String> = thread_handler.handle_request(&rpc_msg);

                                if let Some(response_str) = response {
                                    trace!(target: "ipc", "Response: {}", &response_str);
                                    response_buf.extend(response_str.into_bytes());
                                }
                            }

                            if let Err(write_err) = connected_pipe.write_all(&response_buf[..]).and_then(|_| connected_pipe.flush()) {
                                trace!(target: "ipc", "Response write error: {:?}", write_err);
                            }
                            else {
                                trace!(target: "ipc", "Sent rpc response: {} bytes", response_buf.len());
                            }

                            let leftover_len = start + size - (last_index + 1);
                            if leftover_len > 0 {
                                let leftover = buf[last_index + 1..start + size].to_vec();
                                buf[0..leftover_len].copy_from_slice(&leftover[..]);
                            }
                            fin = leftover_len + REQUEST_READ_BATCH;
                        }
                        else { continue; }
                    },
                    Err(e) => {
                        // closed connection
                        trace!(target: "ipc", "Dropped connection {:?}", e);
                        break;
                    }
                }
            }
        });

        Ok(())
    }
}

pub struct Server {
    is_stopping: Arc<AtomicBool>,
    is_stopped: Arc<AtomicBool>,
    addr: String,
    io_handler: Arc<IoHandler>,
}

impl Server {
    /// New server
    pub fn new(socket_addr: &str, io_handler: &Arc<IoHandler>) -> Result<Server> {
        Ok(Server {
            io_handler: io_handler.clone(),
            is_stopping: Arc::new(AtomicBool::new(false)),
            is_stopped: Arc::new(AtomicBool::new(true)),
            addr: socket_addr.to_owned(),
        })
    }

    /// Run server (in this thread)
    pub fn run(&self) -> Result<()> {
        let mut pipe_handler = try!(PipeHandler::start(&self.addr, &self.io_handler));
        loop  {
            try!(pipe_handler.handle_incoming(&self.addr, Arc::new(AtomicBool::new(false))));
        }
    }

    /// Run server (in separate thread)
    pub fn run_async(&self) -> Result<()> {
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::IsStopping) }
        if !self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStopped) }

        trace!(target: "ipc", "Started named pipes server [{}]", self.addr);

        let thread_stopping = self.is_stopping.clone();
        let thread_stopped = self.is_stopped.clone();
        let thread_handler = self.io_handler.clone();
        let addr = self.addr.clone();
        std::thread::spawn(move || {
            let mut pipe_handler = PipeHandler::start(&addr, &thread_handler).unwrap();
            while !thread_stopping.load(Ordering::Relaxed) {
				trace!(target: "ipc", "Accepting pipe connection");
                if let Err(pipe_listener_error) = pipe_handler.handle_incoming(&addr, thread_stopping.clone()) {
                    trace!(target: "ipc", "Pipe listening error: {:?}", pipe_listener_error);
                }
            }
            thread_stopped.store(true, Ordering::Relaxed);
        });

        self.is_stopped.store(false, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop_async(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        while !self.is_stopped.load(Ordering::Relaxed) { std::thread::park_timeout(std::time::Duration::new(0, 50)); }
        Ok(())
    }
}

impl Drop for Server {
    fn drop(&mut self) {
        self.stop_async().unwrap_or_else(|_| {}); // ignore error - can be stopped already
        // todo : no stable logging for windows?
        trace!(target: "ipc", "IPC Server : shutdown");
    }
}
<commit_msg>add request trace<commit_after>// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

//! jsonrpc server over win named pipes
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate json_ipc_server;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use json_ipc_server::Server;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let server = Server::new("/tmp/json-ipc-test.ipc", &Arc::new(io)).unwrap();
//!     ::std::thread::spawn(move || server.run());
//! }
//! ```

//! Named pipes library

use miow::pipe::{NamedPipe, NamedPipeBuilder};
use std;
use std::io;
use std::io::{Read, Write};
use std::sync::atomic::*;
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use validator;

pub type Result<T> = std::result::Result<T, Error>;

const MAX_REQUEST_LEN: u32 = 65536;
const REQUEST_READ_BATCH: usize = 4096;

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    NotStarted,
    AlreadyStopping,
    NotStopped,
    IsStopping,
}

impl std::convert::From<std::io::Error> for Error {
    fn from(io_error: std::io::Error) -> Error {
        Error::Io(io_error)
    }
}

pub struct PipeHandler {
    waiting_pipe: NamedPipe,
    io_handler: Arc<IoHandler>,
}

impl PipeHandler {
    /// start ipc rpc server (blocking)
    pub fn start(addr: &str, io_handler: &Arc<IoHandler>) -> Result<PipeHandler> {
        Ok(PipeHandler {
            waiting_pipe: try!(
                NamedPipeBuilder::new(addr)
                    .first(true)
                    .accept_remote(true)
                    .max_instances(255)
                    .inbound(true)
                    .outbound(true)
                    .out_buffer_size(MAX_REQUEST_LEN)
                    .in_buffer_size(MAX_REQUEST_LEN)
                    .create()
            ),
            io_handler: io_handler.clone(),
        })
    }

    fn handle_incoming(&mut self, addr: &str, stop: Arc<AtomicBool>) -> io::Result<()> {
        trace!(target: "ipc", "Waiting for client: [{}]", addr);
		try!(self.waiting_pipe.connect());
		trace!(target: "ipc", "Received connection to address [{}]", addr);
        if stop.load(Ordering::Relaxed) {
            trace!(target: "ipc", "Stopped listening sequence [{}]", addr);
            return Ok(())
        }

        let mut connected_pipe = std::mem::replace::<NamedPipe>(&mut self.waiting_pipe,
            try!(NamedPipeBuilder::new(addr)
                .first(false)
                .accept_remote(true)
                .inbound(true)
                .outbound(true)
                .out_buffer_size(MAX_REQUEST_LEN)
                .in_buffer_size(MAX_REQUEST_LEN)
                .create()));

        let thread_handler = self.io_handler.clone();
        std::thread::spawn(move || {
            let mut buf = vec![0u8; MAX_REQUEST_LEN as usize];
            let mut fin = REQUEST_READ_BATCH;
            loop {
                let start = fin - REQUEST_READ_BATCH;
                trace!(target: "ipc", "Reading {} - {} of the buffer", start, fin);
                match connected_pipe.read(&mut buf[start..fin]) {
                    Ok(size) => {
                        let (requests, last_index) = {
                            let effective = &buf[0..start + size];
                            fin = fin + size;
                            trace!(target: "ipc", "Received rpc data: {} bytes", effective.len());

                            validator::extract_requests(effective)
                        };
                        if requests.len() > 0 {
                            let mut response_buf = Vec::new();
                            for rpc_msg in requests  {
                                trace!(target: "ipc", "Request: {}", rpc_msg);

                                let response: Option<String> = thread_handler.handle_request(&rpc_msg);

                                if let Some(response_str) = response {
                                    trace!(target: "ipc", "Response: {}", &response_str);
                                    response_buf.extend(response_str.into_bytes());
                                }
                            }

                            if let Err(write_err) = connected_pipe.write_all(&response_buf[..]).and_then(|_| connected_pipe.flush()) {
                                trace!(target: "ipc", "Response write error: {:?}", write_err);
                            }
                            else {
                                trace!(target: "ipc", "Sent rpc response: {} bytes", response_buf.len());
                            }

                            let leftover_len = start + size - (last_index + 1);
                            if leftover_len > 0 {
                                let leftover = buf[last_index + 1..start + size].to_vec();
                                buf[0..leftover_len].copy_from_slice(&leftover[..]);
                            }
                            fin = leftover_len + REQUEST_READ_BATCH;
                        }
                        else { continue; }
                    },
                    Err(e) => {
                        // closed connection
                        trace!(target: "ipc", "Dropped connection {:?}", e);
                        break;
                    }
                }
            }
        });

        Ok(())
    }
}

pub struct Server {
    is_stopping: Arc<AtomicBool>,
    is_stopped: Arc<AtomicBool>,
    addr: String,
    io_handler: Arc<IoHandler>,
}

impl Server {
    /// New server
    pub fn new(socket_addr: &str, io_handler: &Arc<IoHandler>) -> Result<Server> {
        Ok(Server {
            io_handler: io_handler.clone(),
            is_stopping: Arc::new(AtomicBool::new(false)),
            is_stopped: Arc::new(AtomicBool::new(true)),
            addr: socket_addr.to_owned(),
        })
    }

    /// Run server (in this thread)
    pub fn run(&self) -> Result<()> {
        let mut pipe_handler = try!(PipeHandler::start(&self.addr, &self.io_handler));
        loop  {
            try!(pipe_handler.handle_incoming(&self.addr, Arc::new(AtomicBool::new(false))));
        }
    }

    /// Run server (in separate thread)
    pub fn run_async(&self) -> Result<()> {
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::IsStopping) }
        if !self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStopped) }

        trace!(target: "ipc", "Started named pipes server [{}]", self.addr);

        let thread_stopping = self.is_stopping.clone();
        let thread_stopped = self.is_stopped.clone();
        let thread_handler = self.io_handler.clone();
        let addr = self.addr.clone();
        std::thread::spawn(move || {
            let mut pipe_handler = PipeHandler::start(&addr, &thread_handler).unwrap();
            while !thread_stopping.load(Ordering::Relaxed) {
				trace!(target: "ipc", "Accepting pipe connection");
                if let Err(pipe_listener_error) = pipe_handler.handle_incoming(&addr, thread_stopping.clone()) {
                    trace!(target: "ipc", "Pipe listening error: {:?}", pipe_listener_error);
                }
            }
            thread_stopped.store(true, Ordering::Relaxed);
        });

        self.is_stopped.store(false, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop_async(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        Ok(())
    }

    pub fn stop(&self) -> Result<()> {
        if self.is_stopped.load(Ordering::Relaxed) { return Err(Error::NotStarted) }
        if self.is_stopping.load(Ordering::Relaxed) { return Err(Error::AlreadyStopping)}
        self.is_stopping.store(true, Ordering::Relaxed);
        while !self.is_stopped.load(Ordering::Relaxed) { std::thread::park_timeout(std::time::Duration::new(0, 50)); }
        Ok(())
    }
}

impl Drop for Server {
    fn drop(&mut self) {
        self.stop_async().unwrap_or_else(|_| {}); // ignore error - can be stopped already
        // todo : no stable logging for windows?
        trace!(target: "ipc", "IPC Server : shutdown");
    }
}
<|endoftext|>


<commit_before>extern mod extra;
extern mod rusthttpserver;

use rusthttpserver::server::{Server, Config, ServerUtil};
use rusthttpserver::request::Request;
use rusthttpserver::response::ResponseWriter;
use std::rt::io::net::ip::Ipv4;
use std::rt::io::Writer;

struct MyServer;

impl Server for MyServer {
	pub fn get_config(&self) -> Config {
		Config {
			bind_address: Ipv4(127, 0, 0, 1, 8001),
		}
	}

    fn handle_request(&self, request: Request, mut r: ResponseWriter) {
        //r.headers.insert(~"Content-Length", ~"15");
        //r.headers.insert(~"Content-Type", ~"text/plain; charset=UTF-8");
        //r.headers.insert(~"Server", ~"Example");
        //r.headers.insert(~"Date", ~"Wed, 17 Apr 2013 12:00:00 GMT");
        //r.write(bytes!("Hello, World!"));

        r.headers.insert(~"Date", ~"Tue, 16 Jul 2013 03:43:34 GMT");
        r.headers.insert(~"Server", ~"Apache/2.2.22 (Ubuntu)");
        r.headers.insert(~"Last-Modified", ~"Thu, 05 May 2011 11:46:42 GMT");
        r.headers.insert(~"ETag", ~"\"501b29-b1-4a285ed47404a\"");
        r.headers.insert(~"Accept-Ranges", ~"bytes");
        r.headers.insert(~"Content-Length", ~"177");
        r.headers.insert(~"Vary", ~"Accept-Encoding");
        r.headers.insert(~"Content-Type", ~"text/html");
        r.headers.insert(~"X-Pad", ~"avoid browser bug");

        r.write(bytes!("
It works!

This is the default web page for this server.

The web server software is running but no content has been added, yet.

\n"));
    }

	/*fn handle_request(&self, request: Request, mut r: ResponseWriter) {
        r.headers.insert(~"Content-Type", ~"text/html");
        r.headers.insert(~"Server", ~"Rust Thingummy/0.0-pre");
        r.write(bytes!("<!DOCTYPE html><title>Rust HTTP server</title>"));

		r.write(bytes!("
Request
"));
        let s = fmt!("

Method

%s

Path

%s

HTTP version

%?

Close connection

%?

",
            request.method.to_str(),
            request.path,
            request.version,
            request.close_connection);
        r.write(s.as_bytes().to_owned());
        r.write(bytes!("
Headers
"));
        r.write(bytes!("

Name	Value

"));
        for request.headers.iter().advance |(k, v)| {
            let line = fmt!("
%s	%s
", *k, *v);
            r.write(line.as_bytes().to_owned());
        }
		r.write(bytes!("

"));
        r.write(bytes!("
Body

"));
        r.write(request.body.as_bytes().to_owned());
        r.write(bytes!("
"));

		r.write(bytes!("
Response
"));
        let s = fmt!("

Status

%s

", r.status.to_str());
		r.write(s.as_bytes().to_owned());
		r.write(bytes!("
Headers
"));
        r.write(bytes!("

Name	Value

"));
        {
            let h = copy r.headers;
            for h.iter().advance |(k, v)| {
                let line = fmt!("
%s	%s
", *k, *v);
                r.write(line.as_bytes().to_owned());
            }
        }
		r.write(bytes!("

"));
	}*/
}

fn main() {
	println(fmt!("Serve finished: %?", MyServer.serve_forever()));
}
<commit_msg>Tidy sample server up and add some more forms of server.<commit_after>extern mod extra;
extern mod rusthttpserver;

use rusthttpserver::server::serve_forever;
use rusthttpserver::request::Request;
use rusthttpserver::response::ResponseWriter;
use std::rt::io::net::ip::Ipv4;
use std::rt::io::Writer;

/// A copy of a request from Apache's default thingummy
fn apache_fake_handler(_r: Request, mut w: ResponseWriter) {
    w.headers.insert(~"Date", ~"Tue, 16 Jul 2013 03:43:34 GMT");
    w.headers.insert(~"Server", ~"Apache/2.2.22 (Ubuntu)");
    w.headers.insert(~"Last-Modified", ~"Thu, 05 May 2011 11:46:42 GMT");
    w.headers.insert(~"ETag", ~"\"501b29-b1-4a285ed47404a\"");
    w.headers.insert(~"Accept-Ranges", ~"bytes");
    w.headers.insert(~"Content-Length", ~"177");
    w.headers.insert(~"Vary", ~"Accept-Encoding");
    w.headers.insert(~"Content-Type", ~"text/html");
    w.headers.insert(~"X-Pad", ~"avoid browser bug");

    w.write(bytes!("
It works!

This is the default web page for this server.

The web server software is running but no content has been added, yet.

\n"));
}

fn main() {
    serve_forever(Ipv4(127, 0, 0, 1, 8001), apache_fake_handler);

    do serve_forever(Ipv4(127, 0, 0, 1, 8001)) |r, mut w| {
        w.headers.insert(~"Content-Type", ~"text/html");
        w.headers.insert(~"Server", ~"Rust Thingummy/0.0-pre");
        w.write(bytes!("<!DOCTYPE html><title>Rust HTTP server</title>"));

        w.write(bytes!("
Request
"));
        let s = fmt!("

Method

%s

Path

%s

HTTP version

%?

Close connection

%?

",
            r.method.to_str(),
            r.path,
            r.version,
            r.close_connection);
        w.write(s.as_bytes().to_owned());
        w.write(bytes!("
Headers
"));
        w.write(bytes!("

Name	Value

"));
        for r.headers.iter().advance |(k, v)| {
            let line = fmt!("
%s	%s
", *k, *v);
            w.write(line.as_bytes().to_owned());
        }
        w.write(bytes!("

"));
        w.write(bytes!("
Body

"));
        w.write(r.body.as_bytes().to_owned());
        w.write(bytes!("
"));

        w.write(bytes!("
Response
"));
        let s = fmt!("

Status

%s

", w.status.to_str());
        w.write(s.as_bytes().to_owned());
        w.write(bytes!("
Headers
"));
        w.write(bytes!("

Name	Value

"));
        {
            let h = copy w.headers;
            for h.iter().advance |(k, v)| {
                let line = fmt!("
%s	%s
", *k, *v);
                w.write(line.as_bytes().to_owned());
            }
        }
        w.write(bytes!("

"));
    }

    do serve_forever(Ipv4(127, 0, 0, 1, 8001)) |_r, mut w| {
        w.headers.insert(~"Content-Length", ~"15");
        w.headers.insert(~"Content-Type", ~"text/plain; charset=UTF-8");
        w.headers.insert(~"Server", ~"Example");
        w.headers.insert(~"Date", ~"Wed, 17 Apr 2013 12:00:00 GMT");
        w.write(bytes!("Hello, World!"));
    }
}
<|endoftext|>


<commit_before>use crate::{aws::Aws, cluster::Cluster};
use failure::prelude::{bail, format_err};
use rusoto_ecr::{
    BatchGetImageRequest, DescribeImagesRequest, Ecr, ImageIdentifier, PutImageRequest,
};
use rusoto_ecs::{Ecs, UpdateServiceRequest};
use std::{env, fs, io::ErrorKind, thread, time::Duration};

#[derive(Clone)]
pub struct DeploymentManager {
    aws: Aws,
    cluster: Cluster,

    last_deployed_digest: Option<String>,
}

const LAST_DEPLOYED_FILE: &str = ".last_deployed_digest";
const REPOSITORY_NAME: &str = "libra_e2e";
pub const SOURCE_TAG: &str = "nightly";
pub const RUNNING_TAG: &str = "cluster_test";
pub const TESTED_TAG: &str = "nightly_tested";

impl DeploymentManager {
    pub fn new(aws: Aws, cluster: Cluster) -> Self {
        let last_deployed_digest = match fs::read_to_string(LAST_DEPLOYED_FILE) {
            Ok(v) => {
                println!("Read last deployed digest: {}", v);
                Some(v)
            }
            Err(e) => {
                if e.kind() == ErrorKind::NotFound {
                    None
                } else {
                    panic!("Failed to read .last_deployed_digest: {:?}", e);
                }
            }
        };

        Self {
            aws,
            cluster,
            last_deployed_digest,
        }
    }

    pub fn latest_hash_changed(&self) -> Option<String> {
        let hash = self.latest_nightly_image_digest();
        if let Some(last) = &self.last_deployed_digest {
            if last == &hash {
                println!(
                    "Last deployed digest matches latest digest we expect, not doing redeploy"
                );
                return None;
            }
        } else {
            println!("Last deployed digest unknown, re-deploying anyway");
        }
        Some(hash)
    }

    pub fn redeploy(&mut self, hash: String) -> failure::Result<bool> {
        if env::var("ALLOW_DEPLOY") != Ok("yes".to_string()) {
            println!("Deploying is disabled. Run with ALLOW_DEPLOY=yes to enable deploy");
            return Ok(false);
        }
        println!("Will deploy with digest {}", hash);
        self.tag_image(RUNNING_TAG.to_string(), hash)?;
        let _ignore = fs::remove_file(LAST_DEPLOYED_FILE);
        self.update_all_services()?;
        Ok(true)
    }

    fn update_all_services(&self) -> failure::Result<()> {
        for instance in self.cluster.instances() {
            let mut request = UpdateServiceRequest::default();
            request.cluster = Some(self.aws.workplace().clone());
            request.force_new_deployment = Some(true);
            request.service = format!(
                "{w}/{w}-validator-{hash}",
                w = self.aws.workplace(),
                hash = instance.short_hash()
            );

            self.aws
                .ecs()
                .update_service(request)
                .sync()
                .map_err(|e| format_err!("Failed to update {}: {:?}", instance, e))?;
            thread::sleep(Duration::from_millis(100));
        }
        Ok(())
    }

    fn latest_nightly_image_digest(&self) -> String {
        let mut request = DescribeImagesRequest::default();
        request.repository_name = REPOSITORY_NAME.into();
        request.image_ids = Some(vec![Self::nightly_image_id()]);
        let result = self
            .aws
            .ecr()
            .describe_images(request)
            .sync()
            .expect("Failed to find latest nightly image");
        let images = result
            .image_details
            .expect("No image_details in ECR response");
        if images.len() != 1 {
            panic!("Ecr returned {} images for libra_e2e:nightly", images.len());
        }
        let image = images.into_iter().next().unwrap();
        image.image_digest.expect("No image_digest")
    }

    fn nightly_image_id() -> ImageIdentifier {
        ImageIdentifier {
            image_digest: None,
            image_tag: Some(SOURCE_TAG.to_string()),
        }
    }

    pub fn tag_tested_image(&mut self, hash: String) -> failure::Result<()> {
        self.tag_image(TESTED_TAG.to_string(), hash.clone())?;
        fs::write(LAST_DEPLOYED_FILE, &hash).expect("Failed to write .last_deployed_digest");
        self.last_deployed_digest = Some(hash);
        Ok(())
    }

    fn tag_image(&self, tag: String, hash: String) -> failure::Result<()> {
        let mut get_request = BatchGetImageRequest::default();
        get_request.repository_name = REPOSITORY_NAME.to_string();
        get_request.image_ids = vec![ImageIdentifier {
            image_digest: Some(hash.clone()),
            image_tag: None,
        }];
        let response = self
            .aws
            .ecr()
            .batch_get_image(get_request)
            .sync()
            .map_err(|e| format_err!("Failed to get image {}: {:?}", hash, e))?;
        let images = response
            .images
            .expect("No images in batch_get_image response");
        if images.is_empty() {
            bail!("batch_get_image returned {} images", images.len());
        }
        let image = images.into_iter().next().unwrap();
        let manifest = image
            .image_manifest
            .expect("no manifest in batch_get_image response");
        let mut put_request = PutImageRequest::default();
        put_request.image_manifest = manifest;
        put_request.repository_name = REPOSITORY_NAME.to_string();
        put_request.image_tag = Some(tag);
        self.aws
            .ecr()
            .put_image(put_request)
            .sync()
            .map_err(|e| format_err!("Failed to tag image: {:?}", e))?;
        Ok(())
    }
}
<commit_msg>[cluster_test] Ignore ImageExists error when tagging image<commit_after>use crate::{aws::Aws, cluster::Cluster};
use failure::prelude::{bail, format_err};
use rusoto_core::RusotoError;
use rusoto_ecr::{
    BatchGetImageRequest, DescribeImagesRequest, Ecr, ImageIdentifier, PutImageError,
    PutImageRequest,
};
use rusoto_ecs::{Ecs, UpdateServiceRequest};
use std::{env, fs, io::ErrorKind, thread, time::Duration};

#[derive(Clone)]
pub struct DeploymentManager {
    aws: Aws,
    cluster: Cluster,

    last_deployed_digest: Option<String>,
}

const LAST_DEPLOYED_FILE: &str = ".last_deployed_digest";
const REPOSITORY_NAME: &str = "libra_e2e";
pub const SOURCE_TAG: &str = "nightly";
pub const RUNNING_TAG: &str = "cluster_test";
pub const TESTED_TAG: &str = "nightly_tested";

impl DeploymentManager {
    pub fn new(aws: Aws, cluster: Cluster) -> Self {
        let last_deployed_digest = match fs::read_to_string(LAST_DEPLOYED_FILE) {
            Ok(v) => {
                println!("Read last deployed digest: {}", v);
                Some(v)
            }
            Err(e) => {
                if e.kind() == ErrorKind::NotFound {
                    None
                } else {
                    panic!("Failed to read .last_deployed_digest: {:?}", e);
                }
            }
        };

        Self {
            aws,
            cluster,
            last_deployed_digest,
        }
    }

    pub fn latest_hash_changed(&self) -> Option<String> {
        let hash = self.latest_nightly_image_digest();
        if let Some(last) = &self.last_deployed_digest {
            if last == &hash {
                println!(
                    "Last deployed digest matches latest digest we expect, not doing redeploy"
                );
                return None;
            }
        } else {
            println!("Last deployed digest unknown, re-deploying anyway");
        }
        Some(hash)
    }

    pub fn redeploy(&mut self, hash: String) -> failure::Result<bool> {
        if env::var("ALLOW_DEPLOY") != Ok("yes".to_string()) {
            println!("Deploying is disabled. Run with ALLOW_DEPLOY=yes to enable deploy");
            return Ok(false);
        }
        println!("Will deploy with digest {}", hash);
        self.tag_image(RUNNING_TAG.to_string(), hash)?;
        let _ignore = fs::remove_file(LAST_DEPLOYED_FILE);
        self.update_all_services()?;
        Ok(true)
    }

    fn update_all_services(&self) -> failure::Result<()> {
        for instance in self.cluster.instances() {
            let mut request = UpdateServiceRequest::default();
            request.cluster = Some(self.aws.workplace().clone());
            request.force_new_deployment = Some(true);
            request.service = format!(
                "{w}/{w}-validator-{hash}",
                w = self.aws.workplace(),
                hash = instance.short_hash()
            );

            self.aws
                .ecs()
                .update_service(request)
                .sync()
                .map_err(|e| format_err!("Failed to update {}: {:?}", instance, e))?;
            thread::sleep(Duration::from_millis(100));
        }
        Ok(())
    }

    fn latest_nightly_image_digest(&self) -> String {
        let mut request = DescribeImagesRequest::default();
        request.repository_name = REPOSITORY_NAME.into();
        request.image_ids = Some(vec![Self::nightly_image_id()]);
        let result = self
            .aws
            .ecr()
            .describe_images(request)
            .sync()
            .expect("Failed to find latest nightly image");
        let images = result
            .image_details
            .expect("No image_details in ECR response");
        if images.len() != 1 {
            panic!("Ecr returned {} images for libra_e2e:nightly", images.len());
        }
        let image = images.into_iter().next().unwrap();
        image.image_digest.expect("No image_digest")
    }

    fn nightly_image_id() -> ImageIdentifier {
        ImageIdentifier {
            image_digest: None,
            image_tag: Some(SOURCE_TAG.to_string()),
        }
    }

    pub fn tag_tested_image(&mut self, hash: String) -> failure::Result<()> {
        self.tag_image(TESTED_TAG.to_string(), hash.clone())?;
        fs::write(LAST_DEPLOYED_FILE, &hash).expect("Failed to write .last_deployed_digest");
        self.last_deployed_digest = Some(hash);
        Ok(())
    }

    fn tag_image(&self, tag: String, hash: String) -> failure::Result<()> {
        let mut get_request = BatchGetImageRequest::default();
        get_request.repository_name = REPOSITORY_NAME.to_string();
        get_request.image_ids = vec![ImageIdentifier {
            image_digest: Some(hash.clone()),
            image_tag: None,
        }];
        let response = self
            .aws
            .ecr()
            .batch_get_image(get_request)
            .sync()
            .map_err(|e| format_err!("Failed to get image {}: {:?}", hash, e))?;
        let images = response
            .images
            .expect("No images in batch_get_image response");
        if images.is_empty() {
            bail!("batch_get_image returned {} images", images.len());
        }
        let image = images.into_iter().next().unwrap();
        let manifest = image
            .image_manifest
            .expect("no manifest in batch_get_image response");
        let mut put_request = PutImageRequest::default();
        put_request.image_manifest = manifest;
        put_request.repository_name = REPOSITORY_NAME.to_string();
        put_request.image_tag = Some(tag.clone());
        let result = self.aws.ecr().put_image(put_request).sync();
        if let Err(e) = result {
            if let RusotoError::Service(PutImageError::ImageAlreadyExists(_)) = e {
                println!("Tagging {} with {}: Image already exist", hash, tag);
                Ok(())
            } else {
                Err(format_err!("Failed to tag image: {:?}", e))
            }
        } else {
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    backend::k8s::node::K8sNode, query_sequence_numbers, ChainInfo, FullNode, Node, Result, Swarm,
    Validator,
};
use anyhow::{bail, format_err};
use diem_logger::*;
use diem_sdk::{
    crypto::ed25519::Ed25519PrivateKey,
    types::{
        chain_id::{ChainId, NamedChain},
        AccountKey, LocalAccount, PeerId,
    },
};
use k8s_openapi::api::batch::v1::Job;
use k8s_openapi::api::core::v1::Service;
use kube::{
    api::{Api, ListParams},
    client::Client as K8sClient,
    Config,
};
use rayon::prelude::*;
use serde_json::Value;
use std::{
    collections::HashMap,
    convert::TryFrom,
    process::{Command, Stdio},
    str,
};
use tokio::{runtime::Runtime, time::Duration};

const HEALTH_CHECK_URL: &str = "http://127.0.0.1:8001";
const KUBECTL_BIN: &str = "kubectl";
const HELM_BIN: &str = "helm";
const JSON_RPC_PORT: u32 = 80;
const VALIDATOR_LB: &str = "validator-fullnode-lb";

pub struct K8sSwarm {
    validators: HashMap<PeerId, K8sNode>,
    fullnodes: HashMap<PeerId, K8sNode>,
    root_account: LocalAccount,
    treasury_compliance_account: LocalAccount,
    designated_dealer_account: LocalAccount,
    kube_client: K8sClient,
    runtime: Runtime,
    helm_repo: String,
    pub chain_id: ChainId,
}

impl K8sSwarm {
    pub async fn new(
        root_key: &[u8],
        treasury_compliance_key: &[u8],
        helm_repo: &str,
    ) -> Result<Self> {
        Command::new(KUBECTL_BIN).arg("proxy").spawn()?;
        diem_retrier::retry_async(k8s_retry_strategy(), || {
            Box::pin(async move {
                debug!("Running local kube pod healthcheck on {}", HEALTH_CHECK_URL);
                reqwest::get(HEALTH_CHECK_URL).await?.text().await?;
                println!("Local kube pod healthcheck passed");
                Ok::<(), reqwest::Error>(())
            })
        })
        .await?;
        let config = Config::new(
            reqwest::Url::parse(HEALTH_CHECK_URL).expect("Failed to parse kubernetes endpoint url"),
        );
        let kube_client = K8sClient::try_from(config)?;
        let fullnodes = HashMap::new();
        let services = list_services(kube_client.clone()).await?;
        let validators = services
            .into_iter()
            .filter(|s| s.name.contains(VALIDATOR_LB))
            .map(|s| {
                let node_id = parse_node_id(&s.name).expect("error to parse node id");
                let node = K8sNode {
                    name: format!("val-{}", node_id),
                    peer_id: PeerId::random(),
                    node_id,
                    ip: s.host_ip.clone(),
                    port: JSON_RPC_PORT,
                    dns: s.name,
                    runtime: Runtime::new().unwrap(),
                };
                Ok((node.peer_id(), node))
            })
            .collect::<Result<HashMap<_, _>>>()?;

        let client = validators.values().next().unwrap().json_rpc_client();
        let key = load_root_key(&root_key);
        let account_key = AccountKey::from_private_key(key);
        let address = diem_sdk::types::account_config::diem_root_address();
        let sequence_number = query_sequence_numbers(&client, &[address])
            .await
            .map_err(|e| {
                format_err!(
                    "query_sequence_numbers on {:?} for dd account failed: {}",
                    client,
                    e
                )
            })?[0];
        let root_account = LocalAccount::new(address, account_key, sequence_number);

        let key = load_tc_key(&treasury_compliance_key);
        let account_key = AccountKey::from_private_key(key);
        let address = diem_sdk::types::account_config::treasury_compliance_account_address();
        let sequence_number = query_sequence_numbers(&client, &[address])
            .await
            .map_err(|e| {
                format_err!(
                    "query_sequence_numbers on {:?} for dd account failed: {}",
                    client,
                    e
                )
            })?[0];
        let treasury_compliance_account = LocalAccount::new(address, account_key, sequence_number);

        let key = load_tc_key(&treasury_compliance_key);
        let account_key = AccountKey::from_private_key(key);
        let address = diem_sdk::types::account_config::testnet_dd_account_address();
        let sequence_number = query_sequence_numbers(&client, &[address])
            .await
            .map_err(|e| {
                format_err!(
                    "query_sequence_numbers on {:?} for dd account failed: {}",
                    client,
                    e
                )
            })?[0];
        let designated_dealer_account = LocalAccount::new(address, account_key, sequence_number);

        Ok(Self {
            validators,
            fullnodes,
            root_account,
            treasury_compliance_account,
            designated_dealer_account,
            kube_client,
            runtime: Runtime::new().unwrap(),
            chain_id: ChainId::new(NamedChain::DEVNET.id()),
            helm_repo: helm_repo.to_string(),
        })
    }

    fn get_url(&self) -> String {
        self.validators
            .values()
            .next()
            .unwrap()
            .json_rpc_endpoint()
            .to_string()
    }

    #[allow(dead_code)]
    fn get_kube_client(&self) -> K8sClient {
        self.kube_client.clone()
    }
}

impl Drop for K8sSwarm {
    // When the K8sSwarm struct goes out of scope we need to wipe the chain state
    fn drop(&mut self) {
        clean_k8s_cluster(self.helm_repo.clone(), self.validators.len())
            .map_err(|err| format_err!("Failed to clean k8s cluster with new genesis: {}", err))
            .unwrap();
    }
}

impl Swarm for K8sSwarm {
    fn health_check(&mut self) -> Result<()> {
        self.runtime.block_on(async {
            Command::new(KUBECTL_BIN).arg("proxy").spawn()?;
            diem_retrier::retry_async(k8s_retry_strategy(), || {
                Box::pin(async move {
                    debug!("Running local kube pod healthcheck on {}", HEALTH_CHECK_URL);
                    let _res = reqwest::get(HEALTH_CHECK_URL).await.unwrap().text().await;
                    info!("Local kube pod healthcheck passed");
                    Ok(())
                })
            })
            .await
        })
    }

    fn validators<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a dyn Validator> + 'a> {
        Box::new(self.validators.values().map(|v| v as &'a dyn Validator))
    }

    fn validators_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut dyn Validator> + 'a> {
        Box::new(
            self.validators
                .values_mut()
                .map(|v| v as &'a mut dyn Validator),
        )
    }

    fn validator(&self, id: PeerId) -> Option<&dyn Validator> {
        self.validators.get(&id).map(|v| v as &dyn Validator)
    }

    fn validator_mut(&mut self, id: PeerId) -> Option<&mut dyn Validator> {
        self.validators
            .get_mut(&id)
            .map(|v| v as &mut dyn Validator)
    }

    fn full_nodes<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a dyn FullNode> + 'a> {
        Box::new(self.fullnodes.values().map(|v| v as &'a dyn FullNode))
    }

    fn full_nodes_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut dyn FullNode> + 'a> {
        Box::new(
            self.fullnodes
                .values_mut()
                .map(|v| v as &'a mut dyn FullNode),
        )
    }

    fn full_node(&self, id: PeerId) -> Option<&dyn FullNode> {
        self.fullnodes.get(&id).map(|v| v as &dyn FullNode)
    }

    fn full_node_mut(&mut self, id: PeerId) -> Option<&mut dyn FullNode> {
        self.fullnodes.get_mut(&id).map(|v| v as &mut dyn FullNode)
    }

    fn add_validator(&mut self, _id: PeerId) -> Result<PeerId> {
        todo!()
    }

    fn remove_validator(&mut self, _id: PeerId) -> Result<()> {
        todo!()
    }

    fn add_full_node(&mut self, _id: PeerId) -> Result<()> {
        todo!()
    }

    fn remove_full_node(&mut self, _id: PeerId) -> Result<()> {
        todo!()
    }

    fn chain_info(&mut self) -> ChainInfo<'_> {
        let url = self.get_url();
        ChainInfo::new(
            &mut self.root_account,
            &mut self.treasury_compliance_account,
            &mut self.designated_dealer_account,
            url,
            self.chain_id,
        )
    }
}

fn k8s_retry_strategy() -> impl Iterator<Item = Duration> {
    diem_retrier::exp_retry_strategy(1000, 5000, 30)
}

#[derive(Clone, Debug)]
pub struct KubeService {
    pub name: String,
    pub host_ip: String,
}

impl TryFrom<Service> for KubeService {
    type Error = anyhow::Error;

    fn try_from(service: Service) -> Result<Self, Self::Error> {
        let metadata = service.metadata;
        let name = metadata
            .name
            .ok_or_else(|| format_err!("node name not found"))?;
        let spec = service
            .spec
            .ok_or_else(|| format_err!("spec not found for node"))?;
        let host_ip = spec.cluster_ip.unwrap_or_default();
        Ok(Self { name, host_ip })
    }
}

pub async fn list_services(client: K8sClient) -> Result<Vec<KubeService>> {
    let node_api: Api<Service> = Api::all(client);
    let lp = ListParams::default();
    let services = node_api.list(&lp).await?.items;
    services.into_iter().map(KubeService::try_from).collect()
}

fn parse_node_id(s: &str) -> Result<usize> {
    let v = s.split('-').collect::<Vec<&str>>();
    if v.len() < 5 {
        return Err(format_err!("Failed to parse {:?} node id format", s));
    }
    let idx: usize = v[0][3..].parse().unwrap();
    Ok(idx)
}

fn load_root_key(root_key_bytes: &[u8]) -> Ed25519PrivateKey {
    Ed25519PrivateKey::try_from(root_key_bytes).unwrap()
}

fn load_tc_key(tc_key_bytes: &[u8]) -> Ed25519PrivateKey {
    Ed25519PrivateKey::try_from(tc_key_bytes).unwrap()
}

pub fn clean_k8s_cluster(
    helm_repo: String,
    base_num_validators: usize,
) -> Result<(), anyhow::Error> {
    let rt = Runtime::new().unwrap();

    // get the previous chain era
    let raw_helm_values = Command::new(HELM_BIN)
        .arg("get")
        .arg("values")
        .arg("diem")
        .arg("--output")
        .arg("json")
        .output()
        .unwrap();

    // parse genesis
    let helm_values = String::from_utf8(raw_helm_values.stdout).unwrap();
    let v: Value = serde_json::from_str(&helm_values).unwrap();
    let era = &v["genesis"]["era"];
    let num_validators = &v["genesis"]["numValidators"];
    let new_era = if era == 1 { 2 } else { 1 };

    println!("genesis.era: {} --> {}", era, new_era);
    println!(
        "genesis.numValidators: {} --> {}",
        num_validators, base_num_validators
    );

    // upgrade testnet
    let testnet_upgrade_args = [
        "upgrade",
        "diem",
        &format!("{}/testnet", helm_repo),
        "--reuse-values",
        "--set",
        &format!("genesis.era={}", new_era),
        "--set",
        &format!("genesis.numValidators={}", base_num_validators),
    ];
    println!("{:?}", testnet_upgrade_args);
    let testnet_upgrade_output = Command::new(HELM_BIN)
        .stdout(Stdio::inherit())
        .args(&testnet_upgrade_args)
        .output()
        .expect("failed to helm upgrade diem");
    assert!(testnet_upgrade_output.status.success());

    // upgrade validators in parallel
    (0..base_num_validators).into_par_iter().for_each(|i| {
        let validator_upgrade_args = [
            "upgrade",
            &format!("val{}", i),
            &format!("{}/diem-validator", helm_repo),
            "--reuse-values",
            "--set",
            &format!("chain.era={}", new_era),
        ];
        println!("{:?}", validator_upgrade_args);
        let validator_upgrade_output = Command::new(HELM_BIN)
            .stdout(Stdio::inherit())
            .args(&validator_upgrade_args)
            .output()
            .expect("failed to helm upgrade diem");
        assert!(validator_upgrade_output.status.success());
    });

    let kube_client = rt.block_on(K8sClient::try_default()).unwrap();

    rt.block_on(async {
        diem_retrier::retry_async(k8s_retry_strategy(), || {
            let jobs: Api<Job> = Api::namespaced(kube_client.clone(), "default");
            Box::pin(async move {
                let job_name = format!("diem-testnet-genesis-e{}", new_era);
                println!("Running get job: {}", &job_name);
                let genesis_job = jobs.get_status(&job_name).await.unwrap();
                println!("Status: {:?}", genesis_job.status);
                let status = genesis_job.status.unwrap();
                match status.succeeded {
                    Some(1) => {
                        println!("Genesis job completed");
                        return Ok(());
                    }
                    _ => bail!("Genesis job not completed"),
                }
            })
        })
        .await
    })
}
<commit_msg>[forge][small] run x<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    backend::k8s::node::K8sNode, query_sequence_numbers, ChainInfo, FullNode, Node, Result, Swarm,
    Validator,
};
use anyhow::{bail, format_err};
use diem_logger::*;
use diem_sdk::{
    crypto::ed25519::Ed25519PrivateKey,
    types::{
        chain_id::{ChainId, NamedChain},
        AccountKey, LocalAccount, PeerId,
    },
};
use k8s_openapi::api::{batch::v1::Job, core::v1::Service};
use kube::{
    api::{Api, ListParams},
    client::Client as K8sClient,
    Config,
};
use rayon::prelude::*;
use serde_json::Value;
use std::{
    collections::HashMap,
    convert::TryFrom,
    process::{Command, Stdio},
    str,
};
use tokio::{runtime::Runtime, time::Duration};

const HEALTH_CHECK_URL: &str = "http://127.0.0.1:8001";
const KUBECTL_BIN: &str = "kubectl";
const HELM_BIN: &str = "helm";
const JSON_RPC_PORT: u32 = 80;
const VALIDATOR_LB: &str = "validator-fullnode-lb";

pub struct K8sSwarm {
    validators: HashMap<PeerId, K8sNode>,
    fullnodes: HashMap<PeerId, K8sNode>,
    root_account: LocalAccount,
    treasury_compliance_account: LocalAccount,
    designated_dealer_account: LocalAccount,
    kube_client: K8sClient,
    runtime: Runtime,
    helm_repo: String,
    pub chain_id: ChainId,
}

impl K8sSwarm {
    pub async fn new(
        root_key: &[u8],
        treasury_compliance_key: &[u8],
        helm_repo: &str,
    ) -> Result<Self> {
        Command::new(KUBECTL_BIN).arg("proxy").spawn()?;
        diem_retrier::retry_async(k8s_retry_strategy(), || {
            Box::pin(async move {
                debug!("Running local kube pod healthcheck on {}", HEALTH_CHECK_URL);
                reqwest::get(HEALTH_CHECK_URL).await?.text().await?;
                println!("Local kube pod healthcheck passed");
                Ok::<(), reqwest::Error>(())
            })
        })
        .await?;
        let config = Config::new(
            reqwest::Url::parse(HEALTH_CHECK_URL).expect("Failed to parse kubernetes endpoint url"),
        );
        let kube_client = K8sClient::try_from(config)?;
        let fullnodes = HashMap::new();
        let services = list_services(kube_client.clone()).await?;
        let validators = services
            .into_iter()
            .filter(|s| s.name.contains(VALIDATOR_LB))
            .map(|s| {
                let node_id = parse_node_id(&s.name).expect("error to parse node id");
                let node = K8sNode {
                    name: format!("val-{}", node_id),
                    peer_id: PeerId::random(),
                    node_id,
                    ip: s.host_ip.clone(),
                    port: JSON_RPC_PORT,
                    dns: s.name,
                    runtime: Runtime::new().unwrap(),
                };
                Ok((node.peer_id(), node))
            })
            .collect::<Result<HashMap<_, _>>>()?;

        let client = validators.values().next().unwrap().json_rpc_client();
        let key = load_root_key(&root_key);
        let account_key = AccountKey::from_private_key(key);
        let address = diem_sdk::types::account_config::diem_root_address();
        let sequence_number = query_sequence_numbers(&client, &[address])
            .await
            .map_err(|e| {
                format_err!(
                    "query_sequence_numbers on {:?} for dd account failed: {}",
                    client,
                    e
                )
            })?[0];
        let root_account = LocalAccount::new(address, account_key, sequence_number);

        let key = load_tc_key(&treasury_compliance_key);
        let account_key = AccountKey::from_private_key(key);
        let address = diem_sdk::types::account_config::treasury_compliance_account_address();
        let sequence_number = query_sequence_numbers(&client, &[address])
            .await
            .map_err(|e| {
                format_err!(
                    "query_sequence_numbers on {:?} for dd account failed: {}",
                    client,
                    e
                )
            })?[0];
        let treasury_compliance_account = LocalAccount::new(address, account_key, sequence_number);

        let key = load_tc_key(&treasury_compliance_key);
        let account_key = AccountKey::from_private_key(key);
        let address = diem_sdk::types::account_config::testnet_dd_account_address();
        let sequence_number = query_sequence_numbers(&client, &[address])
            .await
            .map_err(|e| {
                format_err!(
                    "query_sequence_numbers on {:?} for dd account failed: {}",
                    client,
                    e
                )
            })?[0];
        let designated_dealer_account = LocalAccount::new(address, account_key, sequence_number);

        Ok(Self {
            validators,
            fullnodes,
            root_account,
            treasury_compliance_account,
            designated_dealer_account,
            kube_client,
            runtime: Runtime::new().unwrap(),
            chain_id: ChainId::new(NamedChain::DEVNET.id()),
            helm_repo: helm_repo.to_string(),
        })
    }

    fn get_url(&self) -> String {
        self.validators
            .values()
            .next()
            .unwrap()
            .json_rpc_endpoint()
            .to_string()
    }

    #[allow(dead_code)]
    fn get_kube_client(&self) -> K8sClient {
        self.kube_client.clone()
    }
}

impl Drop for K8sSwarm {
    // When the K8sSwarm struct goes out of scope we need to wipe the chain state
    fn drop(&mut self) {
        clean_k8s_cluster(self.helm_repo.clone(), self.validators.len())
            .map_err(|err| format_err!("Failed to clean k8s cluster with new genesis: {}", err))
            .unwrap();
    }
}

impl Swarm for K8sSwarm {
    fn health_check(&mut self) -> Result<()> {
        self.runtime.block_on(async {
            Command::new(KUBECTL_BIN).arg("proxy").spawn()?;
            diem_retrier::retry_async(k8s_retry_strategy(), || {
                Box::pin(async move {
                    debug!("Running local kube pod healthcheck on {}", HEALTH_CHECK_URL);
                    let _res = reqwest::get(HEALTH_CHECK_URL).await.unwrap().text().await;
                    info!("Local kube pod healthcheck passed");
                    Ok(())
                })
            })
            .await
        })
    }

    fn validators<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a dyn Validator> + 'a> {
        Box::new(self.validators.values().map(|v| v as &'a dyn Validator))
    }

    fn validators_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut dyn Validator> + 'a> {
        Box::new(
            self.validators
                .values_mut()
                .map(|v| v as &'a mut dyn Validator),
        )
    }

    fn validator(&self, id: PeerId) -> Option<&dyn Validator> {
        self.validators.get(&id).map(|v| v as &dyn Validator)
    }

    fn validator_mut(&mut self, id: PeerId) -> Option<&mut dyn Validator> {
        self.validators
            .get_mut(&id)
            .map(|v| v as &mut dyn Validator)
    }

    fn full_nodes<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a dyn FullNode> + 'a> {
        Box::new(self.fullnodes.values().map(|v| v as &'a dyn FullNode))
    }

    fn full_nodes_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut dyn FullNode> + 'a> {
        Box::new(
            self.fullnodes
                .values_mut()
                .map(|v| v as &'a mut dyn FullNode),
        )
    }

    fn full_node(&self, id: PeerId) -> Option<&dyn FullNode> {
        self.fullnodes.get(&id).map(|v| v as &dyn FullNode)
    }

    fn full_node_mut(&mut self, id: PeerId) -> Option<&mut dyn FullNode> {
        self.fullnodes.get_mut(&id).map(|v| v as &mut dyn FullNode)
    }

    fn add_validator(&mut self, _id: PeerId) -> Result<PeerId> {
        todo!()
    }

    fn remove_validator(&mut self, _id: PeerId) -> Result<()> {
        todo!()
    }

    fn add_full_node(&mut self, _id: PeerId) -> Result<()> {
        todo!()
    }

    fn remove_full_node(&mut self, _id: PeerId) -> Result<()> {
        todo!()
    }

    fn chain_info(&mut self) -> ChainInfo<'_> {
        let url = self.get_url();
        ChainInfo::new(
            &mut self.root_account,
            &mut self.treasury_compliance_account,
            &mut self.designated_dealer_account,
            url,
            self.chain_id,
        )
    }
}

fn k8s_retry_strategy() -> impl Iterator<Item = Duration> {
    diem_retrier::exp_retry_strategy(1000, 5000, 30)
}

#[derive(Clone, Debug)]
pub struct KubeService {
    pub name: String,
    pub host_ip: String,
}

impl TryFrom<Service> for KubeService {
    type Error = anyhow::Error;

    fn try_from(service: Service) -> Result<Self, Self::Error> {
        let metadata = service.metadata;
        let name = metadata
            .name
            .ok_or_else(|| format_err!("node name not found"))?;
        let spec = service
            .spec
            .ok_or_else(|| format_err!("spec not found for node"))?;
        let host_ip = spec.cluster_ip.unwrap_or_default();
        Ok(Self { name, host_ip })
    }
}

pub async fn list_services(client: K8sClient) -> Result<Vec<KubeService>> {
    let node_api: Api<Service> = Api::all(client);
    let lp = ListParams::default();
    let services = node_api.list(&lp).await?.items;
    services.into_iter().map(KubeService::try_from).collect()
}

fn parse_node_id(s: &str) -> Result<usize> {
    let v = s.split('-').collect::<Vec<&str>>();
    if v.len() < 5 {
        return Err(format_err!("Failed to parse {:?} node id format", s));
    }
    let idx: usize = v[0][3..].parse().unwrap();
    Ok(idx)
}

fn load_root_key(root_key_bytes: &[u8]) -> Ed25519PrivateKey {
    Ed25519PrivateKey::try_from(root_key_bytes).unwrap()
}

fn load_tc_key(tc_key_bytes: &[u8]) -> Ed25519PrivateKey {
    Ed25519PrivateKey::try_from(tc_key_bytes).unwrap()
}

pub fn clean_k8s_cluster(
    helm_repo: String,
    base_num_validators: usize,
) -> Result<(), anyhow::Error> {
    let rt = Runtime::new().unwrap();

    // get the previous chain era
    let raw_helm_values = Command::new(HELM_BIN)
        .arg("get")
        .arg("values")
        .arg("diem")
        .arg("--output")
        .arg("json")
        .output()
        .unwrap();

    // parse genesis
    let helm_values = String::from_utf8(raw_helm_values.stdout).unwrap();
    let v: Value = serde_json::from_str(&helm_values).unwrap();
    let era = &v["genesis"]["era"];
    let num_validators = &v["genesis"]["numValidators"];
    let new_era = if era == 1 { 2 } else { 1 };

    println!("genesis.era: {} --> {}", era, new_era);
    println!(
        "genesis.numValidators: {} --> {}",
        num_validators, base_num_validators
    );

    // upgrade testnet
    let testnet_upgrade_args = [
        "upgrade",
        "diem",
        &format!("{}/testnet", helm_repo),
        "--reuse-values",
        "--set",
        &format!("genesis.era={}", new_era),
        "--set",
        &format!("genesis.numValidators={}", base_num_validators),
    ];
    println!("{:?}", testnet_upgrade_args);
    let testnet_upgrade_output = Command::new(HELM_BIN)
        .stdout(Stdio::inherit())
        .args(&testnet_upgrade_args)
        .output()
        .expect("failed to helm upgrade diem");
    assert!(testnet_upgrade_output.status.success());

    // upgrade validators in parallel
    (0..base_num_validators).into_par_iter().for_each(|i| {
        let validator_upgrade_args = [
            "upgrade",
            &format!("val{}", i),
            &format!("{}/diem-validator", helm_repo),
            "--reuse-values",
            "--set",
            &format!("chain.era={}", new_era),
        ];
        println!("{:?}", validator_upgrade_args);
        let validator_upgrade_output = Command::new(HELM_BIN)
            .stdout(Stdio::inherit())
            .args(&validator_upgrade_args)
            .output()
            .expect("failed to helm upgrade diem");
        assert!(validator_upgrade_output.status.success());
    });

    let kube_client = rt.block_on(K8sClient::try_default()).unwrap();

    rt.block_on(async {
        diem_retrier::retry_async(k8s_retry_strategy(), || {
            let jobs: Api<Job> = Api::namespaced(kube_client.clone(), "default");
            Box::pin(async move {
                let job_name = format!("diem-testnet-genesis-e{}", new_era);
                println!("Running get job: {}", &job_name);
                let genesis_job = jobs.get_status(&job_name).await.unwrap();
                println!("Status: {:?}", genesis_job.status);
                let status = genesis_job.status.unwrap();
                match status.succeeded {
                    Some(1) => {
                        println!("Genesis job completed");
                        Ok(())
                    }
                    _ => bail!("Genesis job not completed"),
                }
            })
        })
        .await
    })
}
<|endoftext|>


<commit_before>use std::ptr;

use env::NapiEnv;
use result::NapiResult;
use sys;

use super::NapiValue;

#[derive(Clone, Copy, Debug)]
pub struct NapiArray {
    value: sys::napi_value,
}

impl NapiArray {
    pub fn new(env: &NapiEnv) -> NapiResult<Self> {
        let mut value = ptr::null_mut();
        env.handle_status(unsafe {
            sys::napi_create_array(env.as_sys_env(), &mut value)
        })?;

        Ok(Self { value })
    }

    pub fn with_length(env: &NapiEnv, length: usize) -> NapiResult<Self> {
        let mut value = ptr::null_mut();
        env.handle_status(unsafe {
            sys::napi_create_array_with_length(
                env.as_sys_env(),
                length,
                &mut value,
            )
        })?;

        Ok(Self { value })
    }
}

impl NapiValue for NapiArray {
    fn as_sys_value(&self) -> sys::napi_value {
        self.value
    }

    fn from_sys_value(value: sys::napi_value) -> Self {
        Self { value }
    }
}
<commit_msg>refactor(napi): rename NapiArray::with_length to with_len<commit_after>use std::ptr;

use env::NapiEnv;
use result::NapiResult;
use sys;

use super::NapiValue;

#[derive(Clone, Copy, Debug)]
pub struct NapiArray {
    value: sys::napi_value,
}

impl NapiArray {
    pub fn new(env: &NapiEnv) -> NapiResult<Self> {
        let mut value = ptr::null_mut();
        env.handle_status(unsafe {
            sys::napi_create_array(env.as_sys_env(), &mut value)
        })?;

        Ok(Self { value })
    }

    pub fn with_len(env: &NapiEnv, len: usize) -> NapiResult<Self> {
        let mut value = ptr::null_mut();
        env.handle_status(unsafe {
            sys::napi_create_array_with_length(
                env.as_sys_env(),
                len,
                &mut value,
            )
        })?;

        Ok(Self { value })
    }
}

impl NapiValue for NapiArray {
    fn as_sys_value(&self) -> sys::napi_value {
        self.value
    }

    fn from_sys_value(value: sys::napi_value) -> Self {
        Self { value }
    }
}
<|endoftext|>