<commit_before>//! SGX-specific extensions to general I/O primitives
//!
//! SGX file descriptors behave differently from Unix file descriptors. See the
//! description of [`TryIntoRawFd`] for more details.
#![unstable(feature = "sgx_platform", issue = "56975")]

use crate::net;
pub use crate::sys::abi::usercalls::raw::Fd as RawFd;
use crate::sys::{self, AsInner, FromInner, IntoInner, TryIntoInner};

/// A trait to extract the raw SGX file descriptor from an underlying
/// object.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait AsRawFd {
    /// Extracts the raw file descriptor.
    ///
    /// This method does **not** pass ownership of the raw file descriptor
    /// to the caller. The descriptor is only guaranteed to be valid while
    /// the original object has not yet been destroyed.
    #[unstable(feature = "sgx_platform", issue = "56975")]
    fn as_raw_fd(&self) -> RawFd;
}

/// A trait to express the ability to construct an object from a raw file
/// descriptor.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait FromRawFd {
    /// An associated type that contains relevant metadata for `Self`.
    type Metadata: Default;

    /// Constructs a new instance of `Self` from the given raw file
    /// descriptor and metadata.
    ///
    /// This function **consumes ownership** of the specified file
    /// descriptor. The returned object will take responsibility for closing
    /// it when the object goes out of scope.
    ///
    /// This function is also unsafe as the primitives currently returned
    /// have the contract that they are the sole owner of the file
    /// descriptor they are wrapping. Usage of this function could
    /// accidentally allow violating this contract which can cause memory
    /// unsafety in code that relies on it being true.
    #[unstable(feature = "sgx_platform", issue = "56975")]
    unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> Self;
}

/// A trait to express the ability to consume an object and acquire ownership of
/// its raw file descriptor.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait TryIntoRawFd: Sized {
    /// Consumes this object, returning the raw underlying file descriptor, if
    /// this object is not cloned.
    ///
    /// This function **transfers ownership** of the underlying file descriptor
    /// to the caller. Callers are then the unique owners of the file descriptor
    /// and must close the descriptor once it's no longer needed.
    ///
    /// Unlike other platforms, on SGX, the file descriptor is shared between
    /// all clones of an object. To avoid race conditions, this function will
    /// only return `Ok` when called on the final clone.
    #[unstable(feature = "sgx_platform", issue = "56975")]
    fn try_into_raw_fd(self) -> Result<RawFd, Self>;
}

impl AsRawFd for net::TcpStream {
    fn as_raw_fd(&self) -> RawFd {
        *self.as_inner().as_inner().as_inner().as_inner()
    }
}

impl AsRawFd for net::TcpListener {
    fn as_raw_fd(&self) -> RawFd {
        *self.as_inner().as_inner().as_inner().as_inner()
    }
}

/// Metadata for `TcpStream`.
#[derive(Debug, Clone, Default)]
pub struct TcpStreamMetadata {
    /// Local address of the TCP stream
    pub local_addr: Option<String>,
    /// Peer address of the TCP stream
    pub peer_addr: Option<String>,
}

impl FromRawFd for net::TcpStream {
    type Metadata = TcpStreamMetadata;

    unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpStream {
        let fd = sys::fd::FileDesc::from_inner(fd);
        let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
        net::TcpStream::from_inner(sys::net::TcpStream::from_inner((socket, metadata.peer_addr)))
    }
}

/// Metadata for `TcpListener`.
#[derive(Debug, Clone, Default)]
pub struct TcpListenerMetadata {
    /// Local address of the TCP listener
    pub local_addr: Option<String>,
}

impl FromRawFd for net::TcpListener {
    type Metadata = TcpListenerMetadata;

    unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpListener {
        let fd = sys::fd::FileDesc::from_inner(fd);
        let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
        net::TcpListener::from_inner(sys::net::TcpListener::from_inner(socket))
    }
}

impl TryIntoRawFd for net::TcpStream {
    fn try_into_raw_fd(self) -> Result<RawFd, Self> {
        let (socket, peer_addr) = self.into_inner().into_inner();
        match socket.try_into_inner() {
            Ok(fd) => Ok(fd.into_inner()),
            Err(socket) => {
                let sys = sys::net::TcpStream::from_inner((socket, peer_addr));
                Err(net::TcpStream::from_inner(sys))
            }
        }
    }
}

impl TryIntoRawFd for net::TcpListener {
    fn try_into_raw_fd(self) -> Result<RawFd, Self> {
        match self.into_inner().into_inner().try_into_inner() {
            Ok(fd) => Ok(fd.into_inner()),
            Err(socket) => {
                let sys = sys::net::TcpListener::from_inner(socket);
                Err(net::TcpListener::from_inner(sys))
            }
        }
    }
}
<commit_msg>Add missing stability attribute<commit_after>//! SGX-specific extensions to general I/O primitives
//!
//! SGX file descriptors behave differently from Unix file descriptors. See the
//! description of [`TryIntoRawFd`] for more details.
#![unstable(feature = "sgx_platform", issue = "56975")]

use crate::net;
pub use crate::sys::abi::usercalls::raw::Fd as RawFd;
use crate::sys::{self, AsInner, FromInner, IntoInner, TryIntoInner};

/// A trait to extract the raw SGX file descriptor from an underlying
/// object.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait AsRawFd {
    /// Extracts the raw file descriptor.
    ///
    /// This method does **not** pass ownership of the raw file descriptor
    /// to the caller. The descriptor is only guaranteed to be valid while
    /// the original object has not yet been destroyed.
    #[unstable(feature = "sgx_platform", issue = "56975")]
    fn as_raw_fd(&self) -> RawFd;
}

/// A trait to express the ability to construct an object from a raw file
/// descriptor.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait FromRawFd {
    /// An associated type that contains relevant metadata for `Self`.
    type Metadata: Default;

    /// Constructs a new instance of `Self` from the given raw file
    /// descriptor and metadata.
    ///
    /// This function **consumes ownership** of the specified file
    /// descriptor. The returned object will take responsibility for closing
    /// it when the object goes out of scope.
    ///
    /// This function is also unsafe as the primitives currently returned
    /// have the contract that they are the sole owner of the file
    /// descriptor they are wrapping. Usage of this function could
    /// accidentally allow violating this contract which can cause memory
    /// unsafety in code that relies on it being true.
    #[unstable(feature = "sgx_platform", issue = "56975")]
    unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> Self;
}

/// A trait to express the ability to consume an object and acquire ownership of
/// its raw file descriptor.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait TryIntoRawFd: Sized {
    /// Consumes this object, returning the raw underlying file descriptor, if
    /// this object is not cloned.
    ///
    /// This function **transfers ownership** of the underlying file descriptor
    /// to the caller. Callers are then the unique owners of the file descriptor
    /// and must close the descriptor once it's no longer needed.
    ///
    /// Unlike other platforms, on SGX, the file descriptor is shared between
    /// all clones of an object. To avoid race conditions, this function will
    /// only return `Ok` when called on the final clone.
    #[unstable(feature = "sgx_platform", issue = "56975")]
    fn try_into_raw_fd(self) -> Result<RawFd, Self>;
}

impl AsRawFd for net::TcpStream {
    fn as_raw_fd(&self) -> RawFd {
        *self.as_inner().as_inner().as_inner().as_inner()
    }
}

impl AsRawFd for net::TcpListener {
    fn as_raw_fd(&self) -> RawFd {
        *self.as_inner().as_inner().as_inner().as_inner()
    }
}

/// Metadata for `TcpStream`.
#[derive(Debug, Clone, Default)]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct TcpStreamMetadata {
    /// Local address of the TCP stream
    pub local_addr: Option<String>,
    /// Peer address of the TCP stream
    pub peer_addr: Option<String>,
}

impl FromRawFd for net::TcpStream {
    type Metadata = TcpStreamMetadata;

    unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpStream {
        let fd = sys::fd::FileDesc::from_inner(fd);
        let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
        net::TcpStream::from_inner(sys::net::TcpStream::from_inner((socket, metadata.peer_addr)))
    }
}

/// Metadata for `TcpListener`.
#[derive(Debug, Clone, Default)]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct TcpListenerMetadata {
    /// Local address of the TCP listener
    pub local_addr: Option<String>,
}

impl FromRawFd for net::TcpListener {
    type Metadata = TcpListenerMetadata;

    unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> net::TcpListener {
        let fd = sys::fd::FileDesc::from_inner(fd);
        let socket = sys::net::Socket::from_inner((fd, metadata.local_addr));
        net::TcpListener::from_inner(sys::net::TcpListener::from_inner(socket))
    }
}

impl TryIntoRawFd for net::TcpStream {
    fn try_into_raw_fd(self) -> Result<RawFd, Self> {
        let (socket, peer_addr) = self.into_inner().into_inner();
        match socket.try_into_inner() {
            Ok(fd) => Ok(fd.into_inner()),
            Err(socket) => {
                let sys = sys::net::TcpStream::from_inner((socket, peer_addr));
                Err(net::TcpStream::from_inner(sys))
            }
        }
    }
}

impl TryIntoRawFd for net::TcpListener {
    fn try_into_raw_fd(self) -> Result<RawFd, Self> {
        match self.into_inner().into_inner().try_into_inner() {
            Ok(fd) => Ok(fd.into_inner()),
            Err(socket) => {
                let sys = sys::net::TcpListener::from_inner(socket);
                Err(net::TcpListener::from_inner(sys))
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use base::Event;
use cros_fuzz::fuzz_target;
use devices::virtio::{Block, Interrupt, Queue, VirtioDevice};
use tempfile;
use vm_memory::{GuestAddress, GuestMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

fuzz_target!(|bytes| {
    let size_u64 = size_of::<u64>();
    let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

    // The fuzz data is interpreted as:
    // starting index 8 bytes
    // command location 8 bytes
    // command 16 bytes
    // descriptors circular buffer 16 bytes * 3
    if bytes.len() < 4 * size_u64 {
        // Need an index to start.
        return;
    }

    let mut data_image = Cursor::new(bytes);

    let first_index = read_u64(&mut data_image);
    if first_index > MEM_SIZE / DESC_SIZE {
        return;
    }
    let first_offset = first_index * DESC_SIZE;
    if first_offset as usize + size_u64 > bytes.len() {
        return;
    }

    let command_addr = read_u64(&mut data_image);
    if command_addr > MEM_SIZE - CMD_SIZE as u64 {
        return;
    }
    if mem
        .write_all_at_addr(
            &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
            GuestAddress(command_addr as u64),
        )
        .is_err()
    {
        return;
    }

    data_image.seek(SeekFrom::Start(first_offset)).unwrap();
    let desc_table = read_u64(&mut data_image);

    if mem
        .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
        .is_err()
    {
        return;
    }

    let mut q = Queue::new(QUEUE_SIZE);
    q.ready = true;
    q.size = QUEUE_SIZE / 2;
    q.max_size = QUEUE_SIZE;

    let queue_evts: Vec<Event> = vec![Event::new().unwrap()];
    let queue_fd = queue_evts[0].as_raw_fd();
    let queue_evt = unsafe { Event::from_raw_fd(libc::dup(queue_fd)) };

    let disk_file = tempfile::tempfile().unwrap();
    let mut block = Block::new(Box::new(disk_file), false, true, 512, None).unwrap();

    block.activate(
        mem,
        Interrupt::new(
            Arc::new(AtomicUsize::new(0)),
            Event::new().unwrap(),
            Event::new().unwrap(),
            None,   // msix_config
            0xFFFF, // VIRTIO_MSI_NO_VECTOR
        ),
        vec![q],
        queue_evts,
    );

    queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}
<commit_msg>fuzz: adapt block_fuzzer for Block::new features param<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use base::Event;
use cros_fuzz::fuzz_target;
use devices::virtio::{base_features, Block, Interrupt, Queue, VirtioDevice};
use tempfile;
use vm_memory::{GuestAddress, GuestMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

fuzz_target!(|bytes| {
    let size_u64 = size_of::<u64>();
    let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

    // The fuzz data is interpreted as:
    // starting index 8 bytes
    // command location 8 bytes
    // command 16 bytes
    // descriptors circular buffer 16 bytes * 3
    if bytes.len() < 4 * size_u64 {
        // Need an index to start.
        return;
    }

    let mut data_image = Cursor::new(bytes);

    let first_index = read_u64(&mut data_image);
    if first_index > MEM_SIZE / DESC_SIZE {
        return;
    }
    let first_offset = first_index * DESC_SIZE;
    if first_offset as usize + size_u64 > bytes.len() {
        return;
    }

    let command_addr = read_u64(&mut data_image);
    if command_addr > MEM_SIZE - CMD_SIZE as u64 {
        return;
    }
    if mem
        .write_all_at_addr(
            &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
            GuestAddress(command_addr as u64),
        )
        .is_err()
    {
        return;
    }

    data_image.seek(SeekFrom::Start(first_offset)).unwrap();
    let desc_table = read_u64(&mut data_image);

    if mem
        .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
        .is_err()
    {
        return;
    }

    let mut q = Queue::new(QUEUE_SIZE);
    q.ready = true;
    q.size = QUEUE_SIZE / 2;
    q.max_size = QUEUE_SIZE;

    let queue_evts: Vec<Event> = vec![Event::new().unwrap()];
    let queue_fd = queue_evts[0].as_raw_fd();
    let queue_evt = unsafe { Event::from_raw_fd(libc::dup(queue_fd)) };

    let features = base_features();

    let disk_file = tempfile::tempfile().unwrap();
    let mut block = Block::new(features, Box::new(disk_file), false, true, 512, None).unwrap();

    block.activate(
        mem,
        Interrupt::new(
            Arc::new(AtomicUsize::new(0)),
            Event::new().unwrap(),
            Event::new().unwrap(),
            None,   // msix_config
            0xFFFF, // VIRTIO_MSI_NO_VECTOR
        ),
        vec![q],
        queue_evts,
    );

    queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}
<|endoftext|>


<commit_before>use rustc_span::ExpnId;
use std::fmt;

rustc_index::newtype_index! {
    pub struct NodeId {
        DEBUG_FORMAT = "NodeId({})"
    }
}

rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId);

/// `NodeId` used to represent the root of the crate.
pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0);

/// When parsing and doing expansions, we initially give all AST nodes this AST
/// node value. Then later, in the renumber pass, we renumber them to have
/// small, positive ids.
pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;

impl NodeId {
    pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self {
        NodeId::from_u32(expn_id.as_u32())
    }

    pub fn placeholder_to_expn_id(self) -> ExpnId {
        ExpnId::from_u32(self.as_u32())
    }
}

impl fmt::Display for NodeId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.as_u32(), f)
    }
}
<commit_msg>The renumber pass is long gone<commit_after>use rustc_span::ExpnId;
use std::fmt;

rustc_index::newtype_index! {
    pub struct NodeId {
        DEBUG_FORMAT = "NodeId({})"
    }
}

rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId);

/// `NodeId` used to represent the root of the crate.
pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0);

/// When parsing and doing expansions, we initially give all AST nodes this AST
/// node value. Then later, during expansion, we renumber them to have small,
/// positive ids.
pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;

impl NodeId {
    pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self {
        NodeId::from_u32(expn_id.as_u32())
    }

    pub fn placeholder_to_expn_id(self) -> ExpnId {
        ExpnId::from_u32(self.as_u32())
    }
}

impl fmt::Display for NodeId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.as_u32(), f)
    }
}
<|endoftext|>


<commit_before>use diesel::result;

use std::convert::From;
use std::error::Error;
use std::path::PathBuf;
use std::{fmt, io};

use self::DatabaseError::*;

pub type DatabaseResult<T> = Result<T, DatabaseError>;

#[derive(Debug)]
pub enum DatabaseError {
    ProjectRootNotFound(PathBuf),
    DatabaseUrlMissing,
    IoError(io::Error),
    QueryError(result::Error),
    ConnectionError(result::ConnectionError),
    MigrationError(Box<dyn Error + Send + Sync + 'static>),
}

impl From<io::Error> for DatabaseError {
    fn from(e: io::Error) -> Self {
        IoError(e)
    }
}

impl From<result::Error> for DatabaseError {
    fn from(e: result::Error) -> Self {
        QueryError(e)
    }
}

impl From<result::ConnectionError> for DatabaseError {
    fn from(e: result::ConnectionError) -> Self {
        ConnectionError(e)
    }
}

impl From<Box<dyn Error + Send + Sync + 'static>> for DatabaseError {
    fn from(e: Box<dyn Error + Send + Sync + 'static>) -> Self {
        MigrationError(e)
    }
}

impl Error for DatabaseError {}

impl fmt::Display for DatabaseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ProjectRootNotFound(ref p) => {
                write!(f, "Unable to find diesel.toml or Cargo.toml in {:?} or any parent directories.", p)
            }
            DatabaseUrlMissing => {
                f.write_str("The --database-url argument must be passed, or the DATABASE_URL environment variable must be set.")
            }
            IoError(ref error) => f.write_str(&error
                .source()
                .map(ToString::to_string)
                .unwrap_or_else(|| error.to_string())),
            QueryError(ref error) => f.write_str(&error
                .source()
                .map(ToString::to_string)
                .unwrap_or_else(|| error.to_string())),
            ConnectionError(ref error) => f.write_str(&error
                .source()
                .map(ToString::to_string)
                                                      .unwrap_or_else(|| error.to_string())),
            MigrationError(ref error) => {
                write!(f, "Failed to run migrations: {}", error)
            }
        }
    }
}

impl PartialEq for DatabaseError {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&ProjectRootNotFound(_), &ProjectRootNotFound(_)) => true,
            _ => false,
        }
    }
}
<commit_msg>Fix broken formatting<commit_after>use diesel::result;

use std::convert::From;
use std::error::Error;
use std::path::PathBuf;
use std::{fmt, io};

use self::DatabaseError::*;

pub type DatabaseResult<T> = Result<T, DatabaseError>;

#[derive(Debug)]
pub enum DatabaseError {
    ProjectRootNotFound(PathBuf),
    DatabaseUrlMissing,
    IoError(io::Error),
    QueryError(result::Error),
    ConnectionError(result::ConnectionError),
    MigrationError(Box<dyn Error + Send + Sync + 'static>),
}

impl From<io::Error> for DatabaseError {
    fn from(e: io::Error) -> Self {
        IoError(e)
    }
}

impl From<result::Error> for DatabaseError {
    fn from(e: result::Error) -> Self {
        QueryError(e)
    }
}

impl From<result::ConnectionError> for DatabaseError {
    fn from(e: result::ConnectionError) -> Self {
        ConnectionError(e)
    }
}

impl From<Box<dyn Error + Send + Sync + 'static>> for DatabaseError {
    fn from(e: Box<dyn Error + Send + Sync + 'static>) -> Self {
        MigrationError(e)
    }
}

impl Error for DatabaseError {}

impl fmt::Display for DatabaseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ProjectRootNotFound(ref p) => {
                write!(f, "Unable to find diesel.toml or Cargo.toml in {:?} or any parent directories.", p)
            }
            DatabaseUrlMissing => {
                f.write_str("The --database-url argument must be passed, or the DATABASE_URL environment variable must be set.")
            }
            IoError(ref error) => f.write_str(&error
                .source()
                .map(ToString::to_string)
                .unwrap_or_else(|| error.to_string())),
            QueryError(ref error) => f.write_str(&error
                .source()
                .map(ToString::to_string)
                .unwrap_or_else(|| error.to_string())),
            ConnectionError(ref error) => f.write_str(&error
                .source()
                .map(ToString::to_string)
                .unwrap_or_else(|| error.to_string())),
            MigrationError(ref error) => {
                write!(f, "Failed to run migrations: {}", error)
            }
        }
    }
}

impl PartialEq for DatabaseError {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&ProjectRootNotFound(_), &ProjectRootNotFound(_)) => true,
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>use super::{svc::{ConfigOptSharedLoad,
                  SharedLoad,
                  DEFAULT_SVC_CONFIG_DIR},
            util::{tls::{CertificateChainCli,
                         PrivateKeyCli,
                         RootCertificateStoreCli},
                   CacheKeyPath,
                   ConfigOptCacheKeyPath,
                   ConfigOptRemoteSup,
                   DurationProxy,
                   RemoteSup}};
use crate::{error::Error,
            VERSION};
use configopt::{self,
                configopt_fields,
                ConfigOpt};
use habitat_common::{cli::{RING_ENVVAR,
                           RING_KEY_ENVVAR},
                     command::package::install::InstallSource,
                     types::{EventStreamConnectMethod,
                             EventStreamMetaPair,
                             EventStreamServerCertificate,
                             EventStreamToken,
                             GossipListenAddr,
                             HttpListenAddr,
                             ListenCtlAddr,
                             ResolvedListenCtlAddr},
                     FeatureFlag,
                     FEATURE_FLAGS};
use habitat_core::{env::Config,
                   package::PackageIdent,
                   util as core_util};
use rants::{error::Error as RantsError,
            Address as NatsAddress};
use std::{fmt,
          net::{IpAddr,
                SocketAddr},
          path::PathBuf,
          str::FromStr};
use structopt::{clap::AppSettings,
                StructOpt};

// All commands relating to the Supervisor (ie commands handled by both the `hab` and `hab-sup`
// binary)
#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, name = "sup")]
#[allow(clippy::large_enum_variant)]
pub enum HabSup {
    /// Depart a Supervisor from the gossip ring; kicking and banning the target from joining again
    /// with the same member-id
    #[structopt(no_version, aliases = &["d", "de", "dep", "depa", "depart"])]
    Depart {
        /// The member-id of the Supervisor to depart
        #[structopt(name = "MEMBER_ID")]
        member_id:  String,
        #[structopt(flatten)]
        remote_sup: RemoteSup,
    },
    #[structopt(no_version, aliases = &["sec", "secr"])]
    Secret(Secret),
    /// Query the status of Habitat services
    #[structopt(no_version, aliases = &["stat", "statu"])]
    Status {
        /// A package identifier (ex: core/redis, core/busybox-static/1.42.2)
        #[structopt(name = "PKG_IDENT")]
        pkg_ident:  Option<PackageIdent>,
        #[structopt(flatten)]
        remote_sup: RemoteSup,
    },
    /// Restart a Supervisor without restarting its services
    #[structopt(no_version)]
    Restart {
        #[structopt(flatten)]
        remote_sup: RemoteSup,
    },
    #[cfg(not(target_os = "macos"))]
    #[structopt(flatten)]
    Sup(Sup),
}

// Supervisor commands handled by the `hab-sup` binary
#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "hab-sup",
            version = VERSION,
            about = "The Habitat Supervisor",
            author = "\nThe Habitat Maintainers <humans@habitat.sh>\n",
            settings = &[AppSettings::VersionlessSubcommands],
        )]
#[allow(clippy::large_enum_variant)]
pub enum Sup {
    /// Start an interactive Bash-like shell
    #[structopt(no_version, aliases = &["b", "ba", "bas"])]
    Bash,
    #[structopt(no_version, aliases = &["r", "ru"])]
    Run(SupRun),
    /// Start an interactive Bourne-like shell
    #[structopt(no_version)]
    Sh,
    /// Gracefully terminate the Habitat Supervisor and all of its running services
    #[structopt(no_version, aliases = &["ter"])]
    Term,
}

// TODO (DM): This is unnecessarily difficult due to this issue in serde
// https://github.com/serde-rs/serde/issues/723. The easiest way to get around the issue is by
// using a wrapper type since NatsAddress is not defined in this crate.
#[derive(Deserialize, Serialize, Debug)]
pub struct EventStreamAddress(#[serde(with = "core_util::serde::string")] NatsAddress);

impl fmt::Display for EventStreamAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
}

impl FromStr for EventStreamAddress {
    type Err = RantsError;

    fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(EventStreamAddress(s.parse()?)) }
}

impl From<EventStreamAddress> for NatsAddress {
    fn from(address: EventStreamAddress) -> Self { address.0 }
}

fn parse_peer(s: &str) -> Result<SocketAddr, Error> {
    Ok(habitat_common::util::resolve_socket_addr_with_default_port(s, GossipListenAddr::DEFAULT_PORT)?.1)
}

/// Run the Habitat Supervisor
#[configopt_fields]
#[derive(ConfigOpt, StructOpt, Deserialize)]
#[configopt(attrs(serde), default_config_file("/hab/sup/default/config/sup.toml"))]
#[serde(deny_unknown_fields)]
#[structopt(name = "run",
            no_version,
            about = "Run the Habitat Supervisor",
            // set custom usage string, otherwise the binary
            // is displayed confusingly as `hab-sup`
            // see: https://github.com/kbknapp/clap-rs/blob/2724ec5399c500b12a1a24d356f4090f4816f5e2/src/app/mod.rs#L373-L394
            usage = "hab sup run [FLAGS] [OPTIONS] [--] [PKG_IDENT_OR_ARTIFACT]",
            rename_all = "screamingsnake",
        )]
pub struct SupRun {
    /// The listen address for the Gossip Gateway
    #[structopt(long = "listen-gossip",
                env = GossipListenAddr::ENVVAR,
                default_value = GossipListenAddr::default_as_str())]
    pub listen_gossip: GossipListenAddr,
    /// Start the supervisor in local mode
    #[structopt(long = "local-gossip-mode",
                conflicts_with_all = &["LISTEN_GOSSIP", "PEER", "PEER_WATCH_FILE"])]
    pub local_gossip_mode: bool,
    /// The listen address for the HTTP Gateway
    #[structopt(long = "listen-http",
                env = HttpListenAddr::ENVVAR,
                default_value = HttpListenAddr::default_as_str())]
    pub listen_http: HttpListenAddr,
    /// Disable the HTTP Gateway completely
    #[structopt(long = "http-disable", short = "D")]
    pub http_disable: bool,
    /// The listen address for the Control Gateway
    #[structopt(long = "listen-ctl",
                env = ListenCtlAddr::ENVVAR,
                default_value = ListenCtlAddr::default_as_str())]
    pub listen_ctl: ResolvedListenCtlAddr,
    /// The control gateway server's TLS certificate
    #[structopt(long = "ctl-server-certificate", default_value = "/hab/cache/keys/ctl")]
    pub ctl_server_certificate: Option<CertificateChainCli>,
    /// Enable TLS for the control gateway and set the server's private key
    ///
    /// See `--ctl-server-certificate` and `--ctl-client-certificate` for additional settings.
    #[structopt(long = "ctl-server-key", default_value = "/hab/cache/keys/ctl")]
    pub ctl_server_key: Option<PrivateKeyCli>,
    /// Enable client authentication for the control gateway and set the certificate authority to
    /// use when authenticating the client
    #[structopt(long = "ctl-client-ca-certificate",
                default_value = "/hab/cache/keys/ctl")]
    pub ctl_client_ca_certificate: Option<RootCertificateStoreCli>,
    /// The organization the Supervisor and its services are part of
    #[structopt(long = "org")]
    pub organization: Option<String>,
    /// The listen address of one or more initial peers (IP[:PORT])
    // TODO (DM): Currently there is not a good way to use `parse_peer` when deserializing. Due to
    // https://github.com/serde-rs/serde/issues/723. There are workarounds but they are all ugly.
    // This means that you have to specify the port when setting this with a config file.
    #[structopt(long = "peer", parse(try_from_str = parse_peer))]
    pub peer: Vec<SocketAddr>,
    /// Make this Supervisor a permanent peer
    #[structopt(long = "permanent-peer", short = "I")]
    pub permanent_peer: bool,
    /// Watch this file for connecting to the ring
    #[structopt(long = "peer-watch-file", conflicts_with = "PEER")]
    pub peer_watch_file: Option<PathBuf>,
    #[structopt(flatten)]
    #[serde(flatten)]
    pub cache_key_path: CacheKeyPath,
    /// The name of the ring used by the Supervisor when running with wire encryption
    #[structopt(long = "ring",
                short = "r",
                env = RING_ENVVAR,
                conflicts_with = "RING_KEY")]
    pub ring: Option<String>,
    /// The contents of the ring key when running with wire encryption
    ///
    /// This option is explicitly undocumented and for testing purposes only. Do not use it in a
    /// production system. Use the corresponding environment variable instead. (ex:
    /// 'SYM-SEC-1\nfoo-20181113185935\n\nGCrBOW6CCN75LMl0j2V5QqQ6nNzWm6and9hkKBSUFPI=')
    #[structopt(long = "ring-key",
                env = RING_KEY_ENVVAR,
                hidden = true)]
    pub ring_key: Option<String>,
    /// Enable automatic updates for the Supervisor itself
    #[structopt(long = "auto-update", short = "A")]
    pub auto_update: bool,
    /// The period of time in seconds between Supervisor update checks
    #[structopt(long = "auto-update-period", default_value = "60")]
    pub auto_update_period: DurationProxy,
    /// The period of time in seconds between service update checks
    #[structopt(long = "service-update-period", default_value = "60")]
    pub service_update_period: DurationProxy,
    /// The private key for HTTP Gateway TLS encryption
    ///
    /// Read the private key from KEY_FILE. This should be an RSA private key or PKCS8-encoded
    /// private key in PEM format.
    #[structopt(long = "key", requires = "CERT_FILE")]
    pub key_file: Option<PathBuf>,
    /// The server certificates for HTTP Gateway TLS encryption
    ///
    /// Read server certificates from CERT_FILE. This should contain PEM-format certificates in
    /// the right order. The first certificate should certify KEY_FILE. The last should be a
    /// root CA.
    #[structopt(long = "certs", requires = "KEY_FILE")]
    pub cert_file: Option<PathBuf>,
    /// The CA certificate for HTTP Gateway TLS encryption
    ///
    /// Read the CA certificate from CA_CERT_FILE. This should contain PEM-format certificate that
    /// can be used to validate client requests
    #[structopt(long = "ca-certs",
                requires_all = &["CERT_FILE", "KEY_FILE"])]
    pub ca_cert_file: Option<PathBuf>,
    /// Load a Habitat package as part of the Supervisor startup
    ///
    /// The package can be specified by a package identifier (ex: core/redis) or filepath to a
    /// Habitat artifact (ex: /home/core-redis-3.0.7-21120102031201-x86_64-linux.hart).
    #[structopt()]
    pub pkg_ident_or_artifact: Option<InstallSource>,
    /// Verbose output showing file and line/column numbers
    #[structopt(short = "v")]
    pub verbose: bool,
    /// Turn ANSI color off
    #[structopt(long = "no-color")]
    pub no_color: bool,
    /// Use structured JSON logging for the Supervisor
    ///
    /// This option also sets NO_COLOR.
    #[structopt(long = "json-logging")]
    pub json_logging: bool,
    /// The IPv4 address to use as the `sys.ip` template variable
    ///
    /// If this argument is not set, the supervisor tries to dynamically determine an IP address.
    /// If that fails, the supervisor defaults to using `127.0.0.1`.
    #[structopt(long = "sys-ip-address")]
    pub sys_ip_address: Option<IpAddr>,
    /// The name of the application for event stream purposes
    ///
    /// This will be attached to all events generated by this Supervisor.
    #[structopt(long = "event-stream-application", empty_values = false)]
    pub event_stream_application: Option<String>,
    /// The name of the environment for event stream purposes
    ///
    /// This will be attached to all events generated by this Supervisor.
    #[structopt(long = "event-stream-environment", empty_values = false)]
    pub event_stream_environment: Option<String>,
    /// Event stream connection timeout before exiting the Supervisor
    ///
    /// Set to '0' to immediately start the Supervisor and continue running regardless of the
    /// initial connection status.
    #[structopt(long = "event-stream-connect-timeout",
                env = EventStreamConnectMethod::ENVVAR,
                default_value = "0")]
    pub event_stream_connect_timeout: EventStreamConnectMethod,
    /// The event stream connection url used to send events to Chef Automate
    ///
    /// This enables the event stream and requires EVENT_STREAM_APPLICATION,
    /// EVENT_STREAM_ENVIRONMENT, and EVENT_STREAM_TOKEN also be set.
    #[structopt(long = "event-stream-url",
                requires_all = &["EVENT_STREAM_APPLICATION",
                                 "EVENT_STREAM_ENVIRONMENT",
                                 EventStreamToken::ARG_NAME])]
    pub event_stream_url: Option<EventStreamAddress>,
    /// The name of the site where this Supervisor is running for event stream purposes
    #[structopt(long = "event-stream-site", empty_values = false)]
    pub event_stream_site: Option<String>,
    /// The authentication token for connecting the event stream to Chef Automate
    #[structopt(long = "event-stream-token",
                env = EventStreamToken::ENVVAR)]
    pub event_stream_token: Option<EventStreamToken>,
    /// An arbitrary key-value pair to add to each event generated by this Supervisor
    #[structopt(long = "event-meta")]
    pub event_meta: Vec<EventStreamMetaPair>,
    /// The path to Chef Automate's event stream certificate used to establish a TLS connection
    ///
    /// The certificate should be in PEM format.
    #[structopt(long = "event-stream-server-certificate")]
    pub event_stream_server_certificate: Option<EventStreamServerCertificate>,
    /// Automatically cleanup old packages
    ///
    /// The Supervisor will automatically cleanup old packages only keeping the
    /// KEEP_LATEST_PACKAGES latest packages. If this argument is not specified, no
    /// automatic package cleanup is performed.
    #[structopt(long = "keep-latest-packages", env = "HAB_KEEP_LATEST_PACKAGES")]
    pub keep_latest_packages: Option<usize>,
    /// Paths to files or directories of service config files to load on startup
    ///
    /// See `hab svc bulkload --help` for details
    #[structopt(long = "svc-config-paths",
                default_value = DEFAULT_SVC_CONFIG_DIR,
                hidden = !FEATURE_FLAGS.contains(FeatureFlag::SERVICE_CONFIG_FILES))]
    pub svc_config_paths: Vec<PathBuf>,
    #[structopt(flatten)]
    #[serde(flatten)]
    pub shared_load: SharedLoad,
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version)]
/// Commands relating to a Habitat Supervisor's Control Gateway secret
pub enum Secret {
    /// Generate a secret key to use as a Supervisor's Control Gateway secret
    Generate,
    /// Generate a private key and certificate for use in the Supervisor's Control Gateway TLS
    /// connection
    GenerateTls {
        /// The DNS name to use in the certificates subject alternative name extension
        #[structopt(long = "subject-alternative-name")]
        subject_alternative_name: String,
        /// The directory to store the generated private key and certificate
        #[structopt(long = "path", default_value = "/hab/cache/keys/ctl")]
        path:                     PathBuf,
    },
}
<commit_msg>Factor out '/hab/cache/keys/ctl' constant<commit_after>use super::{svc::{ConfigOptSharedLoad,
                  SharedLoad,
                  DEFAULT_SVC_CONFIG_DIR},
            util::{tls::{CertificateChainCli,
                         PrivateKeyCli,
                         RootCertificateStoreCli},
                   CacheKeyPath,
                   ConfigOptCacheKeyPath,
                   ConfigOptRemoteSup,
                   DurationProxy,
                   RemoteSup}};
use crate::{error::Error,
            VERSION};
use configopt::{self,
                configopt_fields,
                ConfigOpt};
use habitat_common::{cli::{RING_ENVVAR,
                           RING_KEY_ENVVAR},
                     command::package::install::InstallSource,
                     types::{EventStreamConnectMethod,
                             EventStreamMetaPair,
                             EventStreamServerCertificate,
                             EventStreamToken,
                             GossipListenAddr,
                             HttpListenAddr,
                             ListenCtlAddr,
                             ResolvedListenCtlAddr},
                     FeatureFlag,
                     FEATURE_FLAGS};
use habitat_core::{env::Config,
                   package::PackageIdent,
                   util as core_util};
use rants::{error::Error as RantsError,
            Address as NatsAddress};
use std::{fmt,
          net::{IpAddr,
                SocketAddr},
          path::PathBuf,
          str::FromStr};
use structopt::{clap::AppSettings,
                StructOpt};

const HAB_CTL_KEYS_CACHE: &str = "/hab/cache/keys/ctl";

// All commands relating to the Supervisor (ie commands handled by both the `hab` and `hab-sup`
// binary)
#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, name = "sup")]
#[allow(clippy::large_enum_variant)]
pub enum HabSup {
    /// Depart a Supervisor from the gossip ring; kicking and banning the target from joining again
    /// with the same member-id
    #[structopt(no_version, aliases = &["d", "de", "dep", "depa", "depart"])]
    Depart {
        /// The member-id of the Supervisor to depart
        #[structopt(name = "MEMBER_ID")]
        member_id:  String,
        #[structopt(flatten)]
        remote_sup: RemoteSup,
    },
    #[structopt(no_version, aliases = &["sec", "secr"])]
    Secret(Secret),
    /// Query the status of Habitat services
    #[structopt(no_version, aliases = &["stat", "statu"])]
    Status {
        /// A package identifier (ex: core/redis, core/busybox-static/1.42.2)
        #[structopt(name = "PKG_IDENT")]
        pkg_ident:  Option<PackageIdent>,
        #[structopt(flatten)]
        remote_sup: RemoteSup,
    },
    /// Restart a Supervisor without restarting its services
    #[structopt(no_version)]
    Restart {
        #[structopt(flatten)]
        remote_sup: RemoteSup,
    },
    #[cfg(not(target_os = "macos"))]
    #[structopt(flatten)]
    Sup(Sup),
}

// Supervisor commands handled by the `hab-sup` binary
#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "hab-sup",
            version = VERSION,
            about = "The Habitat Supervisor",
            author = "\nThe Habitat Maintainers <humans@habitat.sh>\n",
            settings = &[AppSettings::VersionlessSubcommands],
        )]
#[allow(clippy::large_enum_variant)]
pub enum Sup {
    /// Start an interactive Bash-like shell
    #[structopt(no_version, aliases = &["b", "ba", "bas"])]
    Bash,
    #[structopt(no_version, aliases = &["r", "ru"])]
    Run(SupRun),
    /// Start an interactive Bourne-like shell
    #[structopt(no_version)]
    Sh,
    /// Gracefully terminate the Habitat Supervisor and all of its running services
    #[structopt(no_version, aliases = &["ter"])]
    Term,
}

// TODO (DM): This is unnecessarily difficult due to this issue in serde
// https://github.com/serde-rs/serde/issues/723. The easiest way to get around the issue is by
// using a wrapper type since NatsAddress is not defined in this crate.
#[derive(Deserialize, Serialize, Debug)]
pub struct EventStreamAddress(#[serde(with = "core_util::serde::string")] NatsAddress);

impl fmt::Display for EventStreamAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
}

impl FromStr for EventStreamAddress {
    type Err = RantsError;

    fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(EventStreamAddress(s.parse()?)) }
}

impl From<EventStreamAddress> for NatsAddress {
    fn from(address: EventStreamAddress) -> Self { address.0 }
}

fn parse_peer(s: &str) -> Result<SocketAddr, Error> {
    Ok(habitat_common::util::resolve_socket_addr_with_default_port(s, GossipListenAddr::DEFAULT_PORT)?.1)
}

/// Run the Habitat Supervisor
#[configopt_fields]
#[derive(ConfigOpt, StructOpt, Deserialize)]
#[configopt(attrs(serde), default_config_file("/hab/sup/default/config/sup.toml"))]
#[serde(deny_unknown_fields)]
#[structopt(name = "run",
            no_version,
            about = "Run the Habitat Supervisor",
            // set custom usage string, otherwise the binary
            // is displayed confusingly as `hab-sup`
            // see: https://github.com/kbknapp/clap-rs/blob/2724ec5399c500b12a1a24d356f4090f4816f5e2/src/app/mod.rs#L373-L394
            usage = "hab sup run [FLAGS] [OPTIONS] [--] [PKG_IDENT_OR_ARTIFACT]",
            rename_all = "screamingsnake",
        )]
pub struct SupRun {
    /// The listen address for the Gossip Gateway
    #[structopt(long = "listen-gossip",
                env = GossipListenAddr::ENVVAR,
                default_value = GossipListenAddr::default_as_str())]
    pub listen_gossip: GossipListenAddr,
    /// Start the supervisor in local mode
    #[structopt(long = "local-gossip-mode",
                conflicts_with_all = &["LISTEN_GOSSIP", "PEER", "PEER_WATCH_FILE"])]
    pub local_gossip_mode: bool,
    /// The listen address for the HTTP Gateway
    #[structopt(long = "listen-http",
                env = HttpListenAddr::ENVVAR,
                default_value = HttpListenAddr::default_as_str())]
    pub listen_http: HttpListenAddr,
    /// Disable the HTTP Gateway completely
    #[structopt(long = "http-disable", short = "D")]
    pub http_disable: bool,
    /// The listen address for the Control Gateway
    #[structopt(long = "listen-ctl",
                env = ListenCtlAddr::ENVVAR,
                default_value = ListenCtlAddr::default_as_str())]
    pub listen_ctl: ResolvedListenCtlAddr,
    /// The control gateway server's TLS certificate
    #[structopt(long = "ctl-server-certificate", default_value = HAB_CTL_KEYS_CACHE)]
    pub ctl_server_certificate: Option<CertificateChainCli>,
    /// Enable TLS for the control gateway and set the server's private key
    ///
    /// See `--ctl-server-certificate` and `--ctl-client-certificate` for additional settings.
    #[structopt(long = "ctl-server-key", default_value = HAB_CTL_KEYS_CACHE)]
    pub ctl_server_key: Option<PrivateKeyCli>,
    /// Enable client authentication for the control gateway and set the certificate authority to
    /// use when authenticating the client
    #[structopt(long = "ctl-client-ca-certificate",
                default_value = HAB_CTL_KEYS_CACHE)]
    pub ctl_client_ca_certificate: Option<RootCertificateStoreCli>,
    /// The organization the Supervisor and its services are part of
    #[structopt(long = "org")]
    pub organization: Option<String>,
    /// The listen address of one or more initial peers (IP[:PORT])
    // TODO (DM): Currently there is not a good way to use `parse_peer` when deserializing. Due to
    // https://github.com/serde-rs/serde/issues/723. There are workarounds but they are all ugly.
    // This means that you have to specify the port when setting this with a config file.
    #[structopt(long = "peer", parse(try_from_str = parse_peer))]
    pub peer: Vec<SocketAddr>,
    /// Make this Supervisor a permanent peer
    #[structopt(long = "permanent-peer", short = "I")]
    pub permanent_peer: bool,
    /// Watch this file for connecting to the ring
    #[structopt(long = "peer-watch-file", conflicts_with = "PEER")]
    pub peer_watch_file: Option<PathBuf>,
    #[structopt(flatten)]
    #[serde(flatten)]
    pub cache_key_path: CacheKeyPath,
    /// The name of the ring used by the Supervisor when running with wire encryption
    #[structopt(long = "ring",
                short = "r",
                env = RING_ENVVAR,
                conflicts_with = "RING_KEY")]
    pub ring: Option<String>,
    /// The contents of the ring key when running with wire encryption
    ///
    /// This option is explicitly undocumented and for testing purposes only. Do not use it in a
    /// production system. Use the corresponding environment variable instead. (ex:
    /// 'SYM-SEC-1\nfoo-20181113185935\n\nGCrBOW6CCN75LMl0j2V5QqQ6nNzWm6and9hkKBSUFPI=')
    #[structopt(long = "ring-key",
                env = RING_KEY_ENVVAR,
                hidden = true)]
    pub ring_key: Option<String>,
    /// Enable automatic updates for the Supervisor itself
    #[structopt(long = "auto-update", short = "A")]
    pub auto_update: bool,
    /// The period of time in seconds between Supervisor update checks
    #[structopt(long = "auto-update-period", default_value = "60")]
    pub auto_update_period: DurationProxy,
    /// The period of time in seconds between service update checks
    #[structopt(long = "service-update-period", default_value = "60")]
    pub service_update_period: DurationProxy,
    /// The private key for HTTP Gateway TLS encryption
    ///
    /// Read the private key from KEY_FILE. This should be an RSA private key or PKCS8-encoded
    /// private key in PEM format.
    #[structopt(long = "key", requires = "CERT_FILE")]
    pub key_file: Option<PathBuf>,
    /// The server certificates for HTTP Gateway TLS encryption
    ///
    /// Read server certificates from CERT_FILE. This should contain PEM-format certificates in
    /// the right order. The first certificate should certify KEY_FILE. The last should be a
    /// root CA.
    #[structopt(long = "certs", requires = "KEY_FILE")]
    pub cert_file: Option<PathBuf>,
    /// The CA certificate for HTTP Gateway TLS encryption
    ///
    /// Read the CA certificate from CA_CERT_FILE. This should contain PEM-format certificate that
    /// can be used to validate client requests
    #[structopt(long = "ca-certs",
                requires_all = &["CERT_FILE", "KEY_FILE"])]
    pub ca_cert_file: Option<PathBuf>,
    /// Load a Habitat package as part of the Supervisor startup
    ///
    /// The package can be specified by a package identifier (ex: core/redis) or filepath to a
    /// Habitat artifact (ex: /home/core-redis-3.0.7-21120102031201-x86_64-linux.hart).
    #[structopt()]
    pub pkg_ident_or_artifact: Option<InstallSource>,
    /// Verbose output showing file and line/column numbers
    #[structopt(short = "v")]
    pub verbose: bool,
    /// Turn ANSI color off
    #[structopt(long = "no-color")]
    pub no_color: bool,
    /// Use structured JSON logging for the Supervisor
    ///
    /// This option also sets NO_COLOR.
    #[structopt(long = "json-logging")]
    pub json_logging: bool,
    /// The IPv4 address to use as the `sys.ip` template variable
    ///
    /// If this argument is not set, the supervisor tries to dynamically determine an IP address.
    /// If that fails, the supervisor defaults to using `127.0.0.1`.
    #[structopt(long = "sys-ip-address")]
    pub sys_ip_address: Option<IpAddr>,
    /// The name of the application for event stream purposes
    ///
    /// This will be attached to all events generated by this Supervisor.
    #[structopt(long = "event-stream-application", empty_values = false)]
    pub event_stream_application: Option<String>,
    /// The name of the environment for event stream purposes
    ///
    /// This will be attached to all events generated by this Supervisor.
    #[structopt(long = "event-stream-environment", empty_values = false)]
    pub event_stream_environment: Option<String>,
    /// Event stream connection timeout before exiting the Supervisor
    ///
    /// Set to '0' to immediately start the Supervisor and continue running regardless of the
    /// initial connection status.
    #[structopt(long = "event-stream-connect-timeout",
                env = EventStreamConnectMethod::ENVVAR,
                default_value = "0")]
    pub event_stream_connect_timeout: EventStreamConnectMethod,
    /// The event stream connection url used to send events to Chef Automate
    ///
    /// This enables the event stream and requires EVENT_STREAM_APPLICATION,
    /// EVENT_STREAM_ENVIRONMENT, and EVENT_STREAM_TOKEN also be set.
    #[structopt(long = "event-stream-url",
                requires_all = &["EVENT_STREAM_APPLICATION",
                                 "EVENT_STREAM_ENVIRONMENT",
                                 EventStreamToken::ARG_NAME])]
    pub event_stream_url: Option<EventStreamAddress>,
    /// The name of the site where this Supervisor is running for event stream purposes
    #[structopt(long = "event-stream-site", empty_values = false)]
    pub event_stream_site: Option<String>,
    /// The authentication token for connecting the event stream to Chef Automate
    #[structopt(long = "event-stream-token",
                env = EventStreamToken::ENVVAR)]
    pub event_stream_token: Option<EventStreamToken>,
    /// An arbitrary key-value pair to add to each event generated by this Supervisor
    #[structopt(long = "event-meta")]
    pub event_meta: Vec<EventStreamMetaPair>,
    /// The path to Chef Automate's event stream certificate used to establish a TLS connection
    ///
    /// The certificate should be in PEM format.
    #[structopt(long = "event-stream-server-certificate")]
    pub event_stream_server_certificate: Option<EventStreamServerCertificate>,
    /// Automatically cleanup old packages
    ///
    /// The Supervisor will automatically cleanup old packages only keeping the
    /// KEEP_LATEST_PACKAGES latest packages. If this argument is not specified, no
    /// automatic package cleanup is performed.
    #[structopt(long = "keep-latest-packages", env = "HAB_KEEP_LATEST_PACKAGES")]
    pub keep_latest_packages: Option<usize>,
    /// Paths to files or directories of service config files to load on startup
    ///
    /// See `hab svc bulkload --help` for details
    #[structopt(long = "svc-config-paths",
                default_value = DEFAULT_SVC_CONFIG_DIR,
                hidden = !FEATURE_FLAGS.contains(FeatureFlag::SERVICE_CONFIG_FILES))]
    pub svc_config_paths: Vec<PathBuf>,
    #[structopt(flatten)]
    #[serde(flatten)]
    pub shared_load: SharedLoad,
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version)]
/// Commands relating to a Habitat Supervisor's Control Gateway secret
pub enum Secret {
    /// Generate a secret key to use as a Supervisor's Control Gateway secret
    Generate,
    /// Generate a private key and certificate for use in the Supervisor's Control Gateway TLS
    /// connection
    GenerateTls {
        /// The DNS name to use in the certificates subject alternative name extension
        #[structopt(long = "subject-alternative-name")]
        subject_alternative_name: String,
        /// The directory to store the generated private key and certificate
        #[structopt(long = "path", default_value = HAB_CTL_KEYS_CACHE)]
        path:                     PathBuf,
    },
}
<|endoftext|>


<commit_before>use std::{fs,
          io,
          path::Path,
          str::FromStr};

use crate::error::Result;

use crate::{common::ui::{Status,
                         UIWriter,
                         UI},
            hcore::{crypto::init,
                    package::PackageIdent}};

const SCAFFOLDING_GO_IDENT: &str = "core/scaffolding-go";
const SCAFFOLDING_GRADLE_IDENT: &str = "core/scaffolding-gradle";
const SCAFFOLDING_NODE_IDENT: &str = "core/scaffolding-node";
const SCAFFOLDING_RUBY_IDENT: &str = "core/scaffolding-ruby";

// Check to see if the --scaffolding passed matches available core scaffolding
// If not check if we've been given a pkg ident for a custom scaffolding
pub fn scaffold_check(ui: &mut UI, maybe_scaffold: Option<&str>) -> Result<Option<PackageIdent>> {
    match maybe_scaffold {
        Some(scaffold) => {
            init();
            match scaffold.to_lowercase().as_ref() {
                SCAFFOLDING_GO_IDENT | "go" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_GO_IDENT)?;
                    ui.status(Status::Using, &format!("Go Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                SCAFFOLDING_GRADLE_IDENT | "gradle" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_GRADLE_IDENT)?;
                    ui.status(Status::Using, &format!("Gradle Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                SCAFFOLDING_NODE_IDENT | "node" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_NODE_IDENT)?;
                    ui.status(Status::Using, &format!("Node Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                SCAFFOLDING_RUBY_IDENT | "ruby" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_RUBY_IDENT)?;
                    ui.status(Status::Using, &format!("Ruby Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                _ => {
                    let ident = PackageIdent::from_str(scaffold)?;
                    ui.status(Status::Using, &format!("custom Scaffolding: '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
            }
        }
        // If nothing was passed try to autodiscover the codebase
        // I'm not saying it's magic. But, MAGIC.
        None => autodiscover_scaffolding(ui),
    }
}

fn autodiscover_scaffolding(ui: &mut UI) -> Result<Option<PackageIdent>> {
    // Determine if the current dir has an app that can use
    // one of our scaffoldings and use it by default
    ui.begin("Attempting autodiscovery ")?;
    ui.para("No scaffolding type was provided. Let's see if we can figure out what kind of \
             application you're planning to package.")?;
    let current_path = Path::new(".");
    if is_project_go(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_GO_IDENT).unwrap();
        ui.begin("We've detected a Go codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else if is_project_gradle(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_GRADLE_IDENT).unwrap();
        ui.begin("We've detected a Gradle codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else if is_project_node(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_NODE_IDENT).unwrap();
        ui.begin("We've detected a Node.js codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else if is_project_ruby(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_RUBY_IDENT).unwrap();
        ui.begin("We've detected a Ruby codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else {
        ui.warn("Unable to determine the type of app in your current directory")?;
        ui.para("For now, we'll generate a plan with all of the available plan variables and \
                 build phase callbacks. For more documentation on plan options visit \
                 https://www.habitat.sh/docs/reference/plan-syntax/")?;
        Ok(None)
    }
}

fn is_project_go<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("main.go").is_file()
    || path.as_ref().join("Godeps/Godeps.json").is_file()
    || path.as_ref().join("vendor/vendor.json").is_file()
    || path.as_ref().join("glide.yaml").is_file()
    || project_uses_gb(path.as_ref()).unwrap_or(false)
}

fn is_project_gradle<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("build.gradle").is_file() || path.as_ref().join("settings.gradle").is_file()
}

fn is_project_node<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("package.json").is_file()
}

fn is_project_ruby<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("Gemfile").is_file()
}

fn project_uses_gb(dir: &Path) -> io::Result<bool> {
    if dir.is_dir() {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                project_uses_gb(&path)?;
            } else if path.is_file() {
                if let Some(ext) = path.extension() {
                    if ext == "go" {
                        return Ok(true);
                    }
                }
            } else {
                return Ok(false);
            }
        }
    }
    Ok(false)
}
<commit_msg>Allow clippy::identity_conversion due to clippy bug<commit_after>use std::{fs,
          io,
          path::Path,
          str::FromStr};

use crate::error::Result;

use crate::{common::ui::{Status,
                         UIWriter,
                         UI},
            hcore::{crypto::init,
                    package::PackageIdent}};

const SCAFFOLDING_GO_IDENT: &str = "core/scaffolding-go";
const SCAFFOLDING_GRADLE_IDENT: &str = "core/scaffolding-gradle";
const SCAFFOLDING_NODE_IDENT: &str = "core/scaffolding-node";
const SCAFFOLDING_RUBY_IDENT: &str = "core/scaffolding-ruby";

// Check to see if the --scaffolding passed matches available core scaffolding
// If not check if we've been given a pkg ident for a custom scaffolding
pub fn scaffold_check(ui: &mut UI, maybe_scaffold: Option<&str>) -> Result<Option<PackageIdent>> {
    match maybe_scaffold {
        Some(scaffold) => {
            init();
            match scaffold.to_lowercase().as_ref() {
                SCAFFOLDING_GO_IDENT | "go" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_GO_IDENT)?;
                    ui.status(Status::Using, &format!("Go Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                SCAFFOLDING_GRADLE_IDENT | "gradle" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_GRADLE_IDENT)?;
                    ui.status(Status::Using, &format!("Gradle Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                SCAFFOLDING_NODE_IDENT | "node" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_NODE_IDENT)?;
                    ui.status(Status::Using, &format!("Node Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                SCAFFOLDING_RUBY_IDENT | "ruby" => {
                    let ident = PackageIdent::from_str(SCAFFOLDING_RUBY_IDENT)?;
                    ui.status(Status::Using, &format!("Ruby Scaffolding '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
                _ => {
                    let ident = PackageIdent::from_str(scaffold)?;
                    ui.status(Status::Using, &format!("custom Scaffolding: '{}'", ident))?;
                    ui.para("")?;
                    Ok(Some(ident))
                }
            }
        }
        // If nothing was passed try to autodiscover the codebase
        // I'm not saying it's magic. But, MAGIC.
        None => autodiscover_scaffolding(ui),
    }
}

fn autodiscover_scaffolding(ui: &mut UI) -> Result<Option<PackageIdent>> {
    // Determine if the current dir has an app that can use
    // one of our scaffoldings and use it by default
    ui.begin("Attempting autodiscovery ")?;
    ui.para("No scaffolding type was provided. Let's see if we can figure out what kind of \
             application you're planning to package.")?;
    let current_path = Path::new(".");
    if is_project_go(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_GO_IDENT).unwrap();
        ui.begin("We've detected a Go codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else if is_project_gradle(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_GRADLE_IDENT).unwrap();
        ui.begin("We've detected a Gradle codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else if is_project_node(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_NODE_IDENT).unwrap();
        ui.begin("We've detected a Node.js codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else if is_project_ruby(&current_path) {
        let ident = PackageIdent::from_str(SCAFFOLDING_RUBY_IDENT).unwrap();
        ui.begin("We've detected a Ruby codebase")?;
        ui.status(Status::Using, &format!("Scaffolding package: '{}'", ident))?;
        ui.para("")?;
        Ok(Some(ident))
    } else {
        ui.warn("Unable to determine the type of app in your current directory")?;
        ui.para("For now, we'll generate a plan with all of the available plan variables and \
                 build phase callbacks. For more documentation on plan options visit \
                 https://www.habitat.sh/docs/reference/plan-syntax/")?;
        Ok(None)
    }
}

fn is_project_go<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("main.go").is_file()
    || path.as_ref().join("Godeps/Godeps.json").is_file()
    || path.as_ref().join("vendor/vendor.json").is_file()
    || path.as_ref().join("glide.yaml").is_file()
    || project_uses_gb(path.as_ref()).unwrap_or(false)
}

fn is_project_gradle<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("build.gradle").is_file() || path.as_ref().join("settings.gradle").is_file()
}

fn is_project_node<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("package.json").is_file()
}

fn is_project_ruby<T>(path: T) -> bool
    where T: AsRef<Path>
{
    path.as_ref().join("Gemfile").is_file()
}

fn project_uses_gb(dir: &Path) -> io::Result<bool> {
    if dir.is_dir() {
        // Remove this once https://github.com/rust-lang/rust-clippy/issues/4133 is resolved
        #[allow(clippy::identity_conversion)]
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                project_uses_gb(&path)?;
            } else if path.is_file() {
                if let Some(ext) = path.extension() {
                    if ext == "go" {
                        return Ok(true);
                    }
                }
            } else {
                return Ok(false);
            }
        }
    }
    Ok(false)
}
<|endoftext|>


<commit_before>/// The validator service. Connects to a beacon node and signs blocks when required.
use crate::attester_service::{AttestationGrpcClient, AttesterService};
use crate::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
use crate::config::Config as ValidatorConfig;
use crate::duties::UpdateOutcome;
use crate::duties::{DutiesManager, EpochDutiesMap};
use crate::error as error_chain;
use crate::error::ErrorKind;
use attester::test_utils::EpochMap;
use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester};
use block_proposer::{test_utils::LocalSigner as BlockProposerLocalSigner, BlockProducer};
use bls::Keypair;
use grpcio::{ChannelBuilder, EnvBuilder};
use protos::services::Empty;
use protos::services_grpc::{
    AttestationServiceClient, BeaconBlockServiceClient, BeaconNodeServiceClient,
    ValidatorServiceClient,
};
use slog::{debug, error, info, warn};
use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::sync::Arc;
use std::sync::RwLock;
use std::time::{Duration, Instant, SystemTime};
use tokio::prelude::*;
use tokio::runtime::Builder;
use tokio::timer::Interval;
use tokio_timer::clock::Clock;
use types::{Epoch, Fork, Slot};
use types::test_utils::generate_deterministic_keypairs;

//TODO: This service should be simplified in the future. Can be made more steamlined.

/// The validator service. This is the main thread that executes and maintains validator
/// duties.
pub struct Service {
    /// The node we currently connected to.
    connected_node_version: String,
    /// The chain id we are processing on.
    chain_id: u16,
    /// The fork state we processing on.
    fork: Fork,
    /// The slot clock for this service.
    slot_clock: SystemTimeSlotClock,
    /// The current slot we are processing.
    current_slot: Slot,
    /// The number of slots per epoch to allow for converting slots to epochs.
    slots_per_epoch: u64,
    // GRPC Clients
    /// The beacon block GRPC client.
    beacon_block_client: Arc<BeaconBlockServiceClient>,
    /// The validator GRPC client.
    validator_client: Arc<ValidatorServiceClient>,
    /// The attester GRPC client.
    attester_client: Arc<AttestationServiceClient>,
    /// The validator client logger.
    log: slog::Logger,
}

impl Service {
    ///  Initial connection to the beacon node to determine its properties.
    ///
    ///  This tries to connect to a beacon node. Once connected, it initialised the gRPC clients
    ///  and returns an instance of the service.
    fn initialize_service(
        config: &ValidatorConfig,
        log: slog::Logger,
    ) -> error_chain::Result<Self> {
        // initialise the beacon node client to check for a connection

        let env = Arc::new(EnvBuilder::new().build());
        // Beacon node gRPC beacon node endpoints.
        let beacon_node_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconNodeServiceClient::new(ch))
        };

        // retrieve node information
        let node_info = loop {
            match beacon_node_client.info(&Empty::new()) {
                Err(e) => {
                    warn!(log, "Could not connect to node. Error: {}", e);
                    info!(log, "Retrying in 5 seconds...");
                    std::thread::sleep(Duration::from_secs(5));
                    continue;
                }
                Ok(info) => {
                    if SystemTime::now()
                        .duration_since(SystemTime::UNIX_EPOCH)
                        .unwrap()
                        .as_secs()
                        < info.genesis_time
                    {
                        warn!(
                            log,
                            "Beacon Node's genesis time is in the future. No work to do.\n Exiting"
                        );
                        return Err("Genesis time in the future".into());
                    }
                    break info;
                }
            };
        };

        // build requisite objects to form Self
        let genesis_time = node_info.get_genesis_time();
        let genesis_slot = Slot::from(node_info.get_genesis_slot());

        info!(log,"Beacon node connected"; "Node Version" => node_info.version.clone(), "Chain ID" => node_info.chain_id, "Genesis time" => genesis_time);

        let proto_fork = node_info.get_fork();
        let mut previous_version: [u8; 4] = [0; 4];
        let mut current_version: [u8; 4] = [0; 4];
        previous_version.copy_from_slice(&proto_fork.get_previous_version()[..4]);
        current_version.copy_from_slice(&proto_fork.get_current_version()[..4]);
        let fork = Fork {
            previous_version,
            current_version,
            epoch: Epoch::from(proto_fork.get_epoch()),
        };

        // initialize the RPC clients

        // Beacon node gRPC beacon block endpoints.
        let beacon_block_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconBlockServiceClient::new(ch))
        };

        // Beacon node gRPC validator endpoints.
        let validator_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(ValidatorServiceClient::new(ch))
        };

        //Beacon node gRPC attester endpoints.
        let attester_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(AttestationServiceClient::new(ch))
        };

        // build the validator slot clock
        let slot_clock =
            SystemTimeSlotClock::new(genesis_slot, genesis_time, config.spec.seconds_per_slot)
                .expect("Unable to instantiate SystemTimeSlotClock.");

        let current_slot = slot_clock
            .present_slot()
            .map_err(|e| ErrorKind::SlotClockError(e))?
            .expect("Genesis must be in the future");

        Ok(Self {
            connected_node_version: node_info.version,
            chain_id: node_info.chain_id as u16,
            fork,
            slot_clock,
            current_slot,
            slots_per_epoch: config.spec.slots_per_epoch,
            beacon_block_client,
            validator_client,
            attester_client,
            log,
        })
    }

    /// Initialise the service then run the core thread.
    pub fn start(config: ValidatorConfig, log: slog::Logger) -> error_chain::Result<()> {
        // connect to the node and retrieve its properties and initialize the gRPC clients
        let service = Service::initialize_service(&config, log)?;

        // we have connected to a node and established its parameters. Spin up the core service

        // set up the validator service runtime
        let mut runtime = Builder::new()
            .clock(Clock::system())
            .name_prefix("validator-client-")
            .build()
            .map_err(|e| format!("Tokio runtime failed: {}", e))?;

        let duration_to_next_slot = service
            .slot_clock
            .duration_to_next_slot()
            .map_err(|e| format!("System clock error: {:?}", e))?
            .expect("Cannot start before genesis");

        // set up the validator work interval - start at next slot and proceed every slot
        let interval = {
            // Set the interval to start at the next slot, and every slot after
            let slot_duration = Duration::from_secs(config.spec.seconds_per_slot);
            //TODO: Handle checked add correctly
            Interval::new(Instant::now() + duration_to_next_slot, slot_duration)
        };

        /* kick off core service */

        // generate keypairs

        // TODO: keypairs are randomly generated; they should be loaded from a file or generated.
        // https://github.com/sigp/lighthouse/issues/160
        let keypairs = Arc::new(generate_deterministic_keypairs(8));

        /* build requisite objects to pass to core thread */

        // Builds a mapping of Epoch -> Map(PublicKey, EpochDuty)
        // where EpochDuty contains slot numbers and attestation data that each validator needs to
        // produce work on.
        let duties_map = RwLock::new(EpochDutiesMap::new(config.spec.slots_per_epoch));

        // builds a manager which maintains the list of current duties for all known validators
        // and can check when a validator needs to perform a task.
        let manager = Arc::new(DutiesManager {
            duties_map,
            pubkeys: keypairs.iter().map(|keypair| keypair.pk.clone()).collect(),
            beacon_node: service.validator_client.clone(),
        });

        // run the core thread
        runtime.block_on(
            interval
                .for_each(move |_| {
                    let log = service.log.clone();

                    /* get the current slot and epoch */
                    let current_slot = match service.slot_clock.present_slot() {
                        Err(e) => {
                            error!(log, "SystemTimeError {:?}", e);
                            return Ok(());
                        }
                        Ok(slot) => slot.expect("Genesis is in the future"),
                    };

                    let current_epoch = current_slot.epoch(service.slots_per_epoch);

                    debug_assert!(
                        current_slot > service.current_slot,
                        "The Timer should poll a new slot"
                    );

                    info!(log, "Processing slot: {}", current_slot.as_u64());

                    /* check for new duties */

                    let cloned_manager = manager.clone();
                    let cloned_log = log.clone();
                    // spawn a new thread separate to the runtime
                    std::thread::spawn(move || {
                        cloned_manager.run_update(current_epoch.clone(), cloned_log.clone());
                        dbg!("Finished thread");
                    });

                    /* execute any specified duties */

                    if let Some(work) = manager.get_current_work(current_slot) {
                        for (_public_key, work_type) in work {
                            if work_type.produce_block {
                                // TODO: Produce a beacon block in a new thread
                            }
                            if work_type.attestation_duty.is_some() {
                                // available AttestationDuty info
                                let attestation_duty =
                                    work_type.attestation_duty.expect("Cannot be None");
                                //TODO: Produce an attestation in a new thread
                            }
                        }
                    }

                    Ok(())
                })
                .map_err(|e| format!("Service thread failed: {:?}", e)),
        );

        // completed a slot process
        Ok(())
    }

    /*

        // Spawn a new thread to perform block production for the validator.
        let producer_thread = {
            let spec = spec.clone();
            let signer = Arc::new(BlockProposerLocalSigner::new(keypair.clone()));
            let duties_map = duties_map.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone()));
            thread::spawn(move || {
                let block_producer =
                    BlockProducer::new(spec, duties_map, slot_clock, client, signer);
                let mut block_producer_service = BlockProducerService {
                    block_producer,
                    poll_interval_millis,
                    log,
                };

                block_producer_service.run();
            })
        };

        // Spawn a new thread for attestation for the validator.
        let attester_thread = {
            let signer = Arc::new(AttesterLocalSigner::new(keypair.clone()));
            let epoch_map = epoch_map_for_attester.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(AttestationGrpcClient::new(attester_grpc_client.clone()));
            thread::spawn(move || {
                let attester = Attester::new(epoch_map, slot_clock, client, signer);
                let mut attester_service = AttesterService {
                    attester,
                    poll_interval_millis,
                    log,
                };

                attester_service.run();
            })
        };

        threads.push((duties_manager_thread, producer_thread, attester_thread));
    }

    // Naively wait for all the threads to complete.
    for tuple in threads {
        let (manager, producer, attester) = tuple;
        let _ = producer.join();
        let _ = manager.join();
        let _ = attester.join();
    }
    */
}
<commit_msg>rustfmt fix.<commit_after>/// The validator service. Connects to a beacon node and signs blocks when required.
use crate::attester_service::{AttestationGrpcClient, AttesterService};
use crate::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
use crate::config::Config as ValidatorConfig;
use crate::duties::UpdateOutcome;
use crate::duties::{DutiesManager, EpochDutiesMap};
use crate::error as error_chain;
use crate::error::ErrorKind;
use attester::test_utils::EpochMap;
use attester::{test_utils::LocalSigner as AttesterLocalSigner, Attester};
use block_proposer::{test_utils::LocalSigner as BlockProposerLocalSigner, BlockProducer};
use bls::Keypair;
use grpcio::{ChannelBuilder, EnvBuilder};
use protos::services::Empty;
use protos::services_grpc::{
    AttestationServiceClient, BeaconBlockServiceClient, BeaconNodeServiceClient,
    ValidatorServiceClient,
};
use slog::{debug, error, info, warn};
use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::sync::Arc;
use std::sync::RwLock;
use std::time::{Duration, Instant, SystemTime};
use tokio::prelude::*;
use tokio::runtime::Builder;
use tokio::timer::Interval;
use tokio_timer::clock::Clock;
use types::test_utils::generate_deterministic_keypairs;
use types::{Epoch, Fork, Slot};

//TODO: This service should be simplified in the future. Can be made more steamlined.

/// The validator service. This is the main thread that executes and maintains validator
/// duties.
pub struct Service {
    /// The node we currently connected to.
    connected_node_version: String,
    /// The chain id we are processing on.
    chain_id: u16,
    /// The fork state we processing on.
    fork: Fork,
    /// The slot clock for this service.
    slot_clock: SystemTimeSlotClock,
    /// The current slot we are processing.
    current_slot: Slot,
    /// The number of slots per epoch to allow for converting slots to epochs.
    slots_per_epoch: u64,
    // GRPC Clients
    /// The beacon block GRPC client.
    beacon_block_client: Arc<BeaconBlockServiceClient>,
    /// The validator GRPC client.
    validator_client: Arc<ValidatorServiceClient>,
    /// The attester GRPC client.
    attester_client: Arc<AttestationServiceClient>,
    /// The validator client logger.
    log: slog::Logger,
}

impl Service {
    ///  Initial connection to the beacon node to determine its properties.
    ///
    ///  This tries to connect to a beacon node. Once connected, it initialised the gRPC clients
    ///  and returns an instance of the service.
    fn initialize_service(
        config: &ValidatorConfig,
        log: slog::Logger,
    ) -> error_chain::Result<Self> {
        // initialise the beacon node client to check for a connection

        let env = Arc::new(EnvBuilder::new().build());
        // Beacon node gRPC beacon node endpoints.
        let beacon_node_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconNodeServiceClient::new(ch))
        };

        // retrieve node information
        let node_info = loop {
            match beacon_node_client.info(&Empty::new()) {
                Err(e) => {
                    warn!(log, "Could not connect to node. Error: {}", e);
                    info!(log, "Retrying in 5 seconds...");
                    std::thread::sleep(Duration::from_secs(5));
                    continue;
                }
                Ok(info) => {
                    if SystemTime::now()
                        .duration_since(SystemTime::UNIX_EPOCH)
                        .unwrap()
                        .as_secs()
                        < info.genesis_time
                    {
                        warn!(
                            log,
                            "Beacon Node's genesis time is in the future. No work to do.\n Exiting"
                        );
                        return Err("Genesis time in the future".into());
                    }
                    break info;
                }
            };
        };

        // build requisite objects to form Self
        let genesis_time = node_info.get_genesis_time();
        let genesis_slot = Slot::from(node_info.get_genesis_slot());

        info!(log,"Beacon node connected"; "Node Version" => node_info.version.clone(), "Chain ID" => node_info.chain_id, "Genesis time" => genesis_time);

        let proto_fork = node_info.get_fork();
        let mut previous_version: [u8; 4] = [0; 4];
        let mut current_version: [u8; 4] = [0; 4];
        previous_version.copy_from_slice(&proto_fork.get_previous_version()[..4]);
        current_version.copy_from_slice(&proto_fork.get_current_version()[..4]);
        let fork = Fork {
            previous_version,
            current_version,
            epoch: Epoch::from(proto_fork.get_epoch()),
        };

        // initialize the RPC clients

        // Beacon node gRPC beacon block endpoints.
        let beacon_block_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(BeaconBlockServiceClient::new(ch))
        };

        // Beacon node gRPC validator endpoints.
        let validator_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(ValidatorServiceClient::new(ch))
        };

        //Beacon node gRPC attester endpoints.
        let attester_client = {
            let ch = ChannelBuilder::new(env.clone()).connect(&config.server);
            Arc::new(AttestationServiceClient::new(ch))
        };

        // build the validator slot clock
        let slot_clock =
            SystemTimeSlotClock::new(genesis_slot, genesis_time, config.spec.seconds_per_slot)
                .expect("Unable to instantiate SystemTimeSlotClock.");

        let current_slot = slot_clock
            .present_slot()
            .map_err(|e| ErrorKind::SlotClockError(e))?
            .expect("Genesis must be in the future");

        Ok(Self {
            connected_node_version: node_info.version,
            chain_id: node_info.chain_id as u16,
            fork,
            slot_clock,
            current_slot,
            slots_per_epoch: config.spec.slots_per_epoch,
            beacon_block_client,
            validator_client,
            attester_client,
            log,
        })
    }

    /// Initialise the service then run the core thread.
    pub fn start(config: ValidatorConfig, log: slog::Logger) -> error_chain::Result<()> {
        // connect to the node and retrieve its properties and initialize the gRPC clients
        let service = Service::initialize_service(&config, log)?;

        // we have connected to a node and established its parameters. Spin up the core service

        // set up the validator service runtime
        let mut runtime = Builder::new()
            .clock(Clock::system())
            .name_prefix("validator-client-")
            .build()
            .map_err(|e| format!("Tokio runtime failed: {}", e))?;

        let duration_to_next_slot = service
            .slot_clock
            .duration_to_next_slot()
            .map_err(|e| format!("System clock error: {:?}", e))?
            .expect("Cannot start before genesis");

        // set up the validator work interval - start at next slot and proceed every slot
        let interval = {
            // Set the interval to start at the next slot, and every slot after
            let slot_duration = Duration::from_secs(config.spec.seconds_per_slot);
            //TODO: Handle checked add correctly
            Interval::new(Instant::now() + duration_to_next_slot, slot_duration)
        };

        /* kick off core service */

        // generate keypairs

        // TODO: keypairs are randomly generated; they should be loaded from a file or generated.
        // https://github.com/sigp/lighthouse/issues/160
        let keypairs = Arc::new(generate_deterministic_keypairs(8));

        /* build requisite objects to pass to core thread */

        // Builds a mapping of Epoch -> Map(PublicKey, EpochDuty)
        // where EpochDuty contains slot numbers and attestation data that each validator needs to
        // produce work on.
        let duties_map = RwLock::new(EpochDutiesMap::new(config.spec.slots_per_epoch));

        // builds a manager which maintains the list of current duties for all known validators
        // and can check when a validator needs to perform a task.
        let manager = Arc::new(DutiesManager {
            duties_map,
            pubkeys: keypairs.iter().map(|keypair| keypair.pk.clone()).collect(),
            beacon_node: service.validator_client.clone(),
        });

        // run the core thread
        runtime.block_on(
            interval
                .for_each(move |_| {
                    let log = service.log.clone();

                    /* get the current slot and epoch */
                    let current_slot = match service.slot_clock.present_slot() {
                        Err(e) => {
                            error!(log, "SystemTimeError {:?}", e);
                            return Ok(());
                        }
                        Ok(slot) => slot.expect("Genesis is in the future"),
                    };

                    let current_epoch = current_slot.epoch(service.slots_per_epoch);

                    debug_assert!(
                        current_slot > service.current_slot,
                        "The Timer should poll a new slot"
                    );

                    info!(log, "Processing slot: {}", current_slot.as_u64());

                    /* check for new duties */

                    let cloned_manager = manager.clone();
                    let cloned_log = log.clone();
                    // spawn a new thread separate to the runtime
                    std::thread::spawn(move || {
                        cloned_manager.run_update(current_epoch.clone(), cloned_log.clone());
                        dbg!("Finished thread");
                    });

                    /* execute any specified duties */

                    if let Some(work) = manager.get_current_work(current_slot) {
                        for (_public_key, work_type) in work {
                            if work_type.produce_block {
                                // TODO: Produce a beacon block in a new thread
                            }
                            if work_type.attestation_duty.is_some() {
                                // available AttestationDuty info
                                let attestation_duty =
                                    work_type.attestation_duty.expect("Cannot be None");
                                //TODO: Produce an attestation in a new thread
                            }
                        }
                    }

                    Ok(())
                })
                .map_err(|e| format!("Service thread failed: {:?}", e)),
        );

        // completed a slot process
        Ok(())
    }

    /*

        // Spawn a new thread to perform block production for the validator.
        let producer_thread = {
            let spec = spec.clone();
            let signer = Arc::new(BlockProposerLocalSigner::new(keypair.clone()));
            let duties_map = duties_map.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(BeaconBlockGrpcClient::new(beacon_block_grpc_client.clone()));
            thread::spawn(move || {
                let block_producer =
                    BlockProducer::new(spec, duties_map, slot_clock, client, signer);
                let mut block_producer_service = BlockProducerService {
                    block_producer,
                    poll_interval_millis,
                    log,
                };

                block_producer_service.run();
            })
        };

        // Spawn a new thread for attestation for the validator.
        let attester_thread = {
            let signer = Arc::new(AttesterLocalSigner::new(keypair.clone()));
            let epoch_map = epoch_map_for_attester.clone();
            let slot_clock = slot_clock.clone();
            let log = log.clone();
            let client = Arc::new(AttestationGrpcClient::new(attester_grpc_client.clone()));
            thread::spawn(move || {
                let attester = Attester::new(epoch_map, slot_clock, client, signer);
                let mut attester_service = AttesterService {
                    attester,
                    poll_interval_millis,
                    log,
                };

                attester_service.run();
            })
        };

        threads.push((duties_manager_thread, producer_thread, attester_thread));
    }

    // Naively wait for all the threads to complete.
    for tuple in threads {
        let (manager, producer, attester) = tuple;
        let _ = producer.join();
        let _ = manager.join();
        let _ = attester.join();
    }
    */
}
<|endoftext|>