<commit_before>#![recursion_limit = "128"]

#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

use clap::{ArgMatches,
           ErrorKind as ClapErrorKind,
           Shell};
use configopt::{ConfigOpt,
                Error as ConfigOptError};
use futures::stream::StreamExt;
use hab::{cli::{self,
                gateway_util,
                hab::{license::License,
                      origin::{Rbac,
                               RbacSet,
                               RbacShow},
                      pkg::{ExportCommand as PkgExportCommand,
                            PkgExec},
                      sup::{HabSup,
                            Secret,
                            Sup},
                      svc::{self,
                            BulkLoad as SvcBulkLoad,
                            Load as SvcLoad,
                            Svc},
                      util::{bldr_auth_token_from_args_env_or_load,
                             bldr_url_from_args_env_load_or_default},
                      Hab,
                      Origin,
                      Pkg},
                parse_optional_arg,
                KeyType},
          command::{self,
                    pkg::{download::{PackageSet,
                                     PackageSetFile},
                          list::ListingType,
                          uninstall::UninstallHookMode}},
          error::{Error,
                  Result},
          license,
          scaffolding,
          AUTH_TOKEN_ENVVAR,
          BLDR_URL_ENVVAR,
          ORIGIN_ENVVAR,
          PRODUCT,
          VERSION};
use habitat_api_client::BuildOnUpload;
use habitat_common::{self as common,
                     cli::key_cache_from_matches,
                     cli_config::CliConfig,
                     command::package::install::{InstallHookMode,
                                                 InstallMode,
                                                 InstallSource,
                                                 LocalPackageUsage},
                     types::ResolvedListenCtlAddr,
                     ui::{self,
                          Status,
                          UIWriter,
                          UI},
                     FeatureFlag};
use habitat_core::{crypto::{init,
                            keys::{Key,
                                   KeyCache}},
                   env::{self as henv,
                         Config as _},
                   fs::{cache_artifact_path,
                        FS_ROOT_PATH},
                   os::process::ShutdownTimeout,
                   package::{target,
                             PackageIdent,
                             PackageTarget},
                   service::ServiceGroup,
                   tls::ctl_gateway as ctl_gateway_tls,
                   url::default_bldr_url,
                   ChannelIdent};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol::{self as sup_proto,
                           codec::*,
                           net::ErrCode,
                           types::*};
use std::{collections::HashMap,
          convert::TryFrom,
          env,
          ffi::OsString,
          fs::File,
          io::{self,
               prelude::*,
               Read},
          path::{Path,
                 PathBuf},
          process,
          result,
          str::FromStr,
          string::ToString,
          thread};
use tabwriter::TabWriter;
use webpki::DnsNameRef;

/// Makes the --org CLI param optional when this env var is set
const HABITAT_ORG_ENVVAR: &str = "HAB_ORG";
/// Makes the --user CLI param optional when this env var is set
const HABITAT_USER_ENVVAR: &str = "HAB_USER";

lazy_static! {
    static ref STATUS_HEADER: Vec<&'static str> = {
        vec!["package",
             "type",
             "desired",
             "state",
             "elapsed (s)",
             "pid",
             "group",]
    };
}

#[tokio::main]
async fn main() {
    env_logger::init();
    let mut ui = UI::default_with_env();
    let flags = FeatureFlag::from_env(&mut ui);
    if let Err(e) = start(&mut ui, flags).await {
        let exit_code = e.exit_code();
        ui.fatal(e).unwrap();
        std::process::exit(exit_code)
    }
}

#[allow(clippy::cognitive_complexity)]
async fn start(ui: &mut UI, feature_flags: FeatureFlag) -> Result<()> {
    // We parse arguments with configopt in a separate thread to eliminate
    // possible stack overflow crashes at runtime. OSX or a debug Windows build,
    // for instance, will crash with our large tree. This is a known issue:
    // https://github.com/kbknapp/clap-rs/issues/86
    let child = thread::Builder::new().stack_size(8 * 1024 * 1024)
                                      .spawn(Hab::try_from_args_with_configopt)
                                      .unwrap();
    let hab = child.join().unwrap();

    if let Ok(Hab::License(License::Accept)) = hab {
        license::accept_license(ui)?;
        return Ok(());
    }

    // Allow checking version information and displaying command help without accepting the license.
    // TODO (DM): To prevent errors in discrepancy between the structopt and cli versions only do
    // this when the license has not yet been accepted. When we switch fully to structopt this can
    // be completely removed and we should just call `Hab::from_args_with_configopt` which will
    // automatically result in this functionality.
    if !license::check_for_license_acceptance().unwrap_or_default()
                                               .accepted()
    {
        if let Err(ConfigOptError::Clap(e)) = &hab {
            if e.kind == ClapErrorKind::VersionDisplayed || e.kind == ClapErrorKind::HelpDisplayed {
                e.exit()
            }
        }
    }

    // We must manually detect a supervisor version check and call the `hab-sup` binary to get the
    // true Supervisor version.
    // TODO (DM): This is an ugly consequence of having `hab sup` subcommands handled by both the
    // `hab` binary and the `hab-sup` binary. Potential fixes:
    // 1. Handle all `hab sup` subcommands with the `hab-sup` binary
    // 2. Have a dedicated subcommand for commands handled by the `hab-sup` binary
    let mut args = env::args();
    if matches!((args.next().unwrap_or_default().as_str(),
                 args.next().unwrap_or_default().as_str(),
                 args.next().unwrap_or_default().as_str()),
                (_, "sup", "--version") | (_, "sup", "-V"))
    {
        return command::sup::start(ui, &args_after_first(2)).await;
    }

    license::check_for_license_acceptance_and_prompt(ui)?;

    // Parse and handle commands which have been migrated to use `structopt` here. Once everything
    // is migrated to use `structopt` the parsing logic below this using clap directly will be gone.
    match hab {
        Ok(hab) => {
            match hab {
                Hab::Origin(Origin::Rbac(action)) => {
                    match action {
                        Rbac::Set(rbac_set) => {
                            return sub_origin_member_role_set(ui, rbac_set).await;
                        }
                        Rbac::Show(rbac_show) => {
                            return sub_origin_member_role_show(ui, rbac_show).await;
                        }
                    }
                }
                #[cfg(not(target_os = "macos"))]
                Hab::Run(sup_run) => {
                    ui.warn("'hab run' as an alias for 'hab sup run' is deprecated. Please \
                             update your automation and processes accordingly.")?;
                    return command::launcher::start(ui, sup_run, &args_after_first(1)).await;
                }
                Hab::Studio(studio) => {
                    return command::studio::enter::start(ui, studio.args()).await;
                }
                Hab::Sup(sup) => {
                    match sup {
                        #[cfg(not(target_os = "macos"))]
                        HabSup::Sup(sup) => {
                            // These commands are handled by the `hab-sup` or `hab-launch` binaries.
                            // We need to pass the subcommand that was issued to the underlying
                            // binary. It is a bit hacky, but to do that we strip off the `hab sup`
                            // command prefix and pass the rest of the args to underlying binary.
                            let args = args_after_first(2);
                            match sup {
                                Sup::Bash | Sup::Sh | Sup::Term => {
                                    return command::sup::start(ui, &args).await;
                                }
                                Sup::Run(sup_run) => {
                                    return command::launcher::start(ui, sup_run, &args).await;
                                }
                            }
                        }
                        HabSup::Depart { member_id,
                                         remote_sup, } => {
                            return sub_sup_depart(member_id, remote_sup.inner()).await;
                        }
                        HabSup::Secret(secret) => {
                            match secret {
                                Secret::Generate => return sub_sup_secret_generate(),
                                Secret::GenerateTls { subject_alternative_name,
                                                      path, } => {
                                    return sub_sup_secret_generate_key(subject_alternative_name.inner(),
                                                                       path)
                                }
                            }
                        }
                        HabSup::Status { pkg_ident,
                                         remote_sup, } => {
                            ui.warn("'hab sup status' as an alias for 'hab svc status' is \
                                     deprecated. Please update your automation and processes \
                                     accordingly.")?;
                            return sub_svc_status(pkg_ident, remote_sup.inner()).await;
                        }
                        HabSup::Restart { remote_sup } => {
                            return sub_sup_restart(remote_sup.inner()).await;
                        }
                    }
                }
                Hab::Svc(svc) => {
                    match svc {
                        Svc::BulkLoad(svc_bulk_load) => {
                            if feature_flags.contains(FeatureFlag::SERVICE_CONFIG_FILES) {
                                return sub_svc_bulk_load(svc_bulk_load).await;
                            } else {
                                return Err(Error::ArgumentError(String::from("`hab svc bulkload` is only available when `HAB_FEAT_SERVICE_CONFIG_FILES` is set")));
                            }
                        }
                        Svc::Load(svc_load) => {
                            return sub_svc_load(svc_load).await;
                        }
                        Svc::Update(svc_update) => return sub_svc_update(svc_update).await,
                        Svc::Status(svc_status) => {
                            return sub_svc_status(svc_status.pkg_ident,
                                                  svc_status.remote_sup.inner()).await;
                        }
                        _ => {
                            // All other commands will be caught by the CLI parsing logic below.
                        }
                    }
                }
                #[cfg(not(target_os = "macos"))]
                Hab::Term => {
                    ui.warn("'hab term' as an alias for 'hab sup term' is deprecated. Please \
                             update your automation and processes accordingly.")?;
                    return command::sup::start(ui, &args_after_first(1)).await;
                }
                Hab::Pkg(pkg) => {
                    match pkg {
                        Pkg::Export(export) => {
                            match export {
                                #[cfg(target_os = "linux")]
                                PkgExportCommand::Cf(args) => {
                                    return command::pkg::export::cf::start(ui, &args.args).await;
                                }
                                #[cfg(any(target_os = "linux", target_os = "windows"))]
                                PkgExportCommand::Container(args) => {
                                    return command::pkg::export::container::start(ui, &args.args).await;
                                }
                                #[cfg(any(target_os = "linux", target_os = "windows"))]
                                PkgExportCommand::Docker(args) => {
                                    ui.warn("'hab pkg export docker' is now a deprecated alias \
                                             for 'hab pkg export container'. Please update your \
                                             automation and processes accordingly.")?;
                                    return command::pkg::export::container::start(ui, &args.args).await;
                                }
                                #[cfg(target_os = "linux")]
                                PkgExportCommand::Mesos(args) => {
                                    return command::pkg::export::mesos::start(ui, &args.args).await;
                                }
                                #[cfg(any(target_os = "linux", target_os = "windows"))]
                                PkgExportCommand::Tar(args) => {
                                    return command::pkg::export::tar::start(ui, &args.args).await;
                                }
                            }
                        }
                        Pkg::Exec(PkgExec { pkg_ident,
                                            cmd,
                                            args, }) => {
                            return command::pkg::exec::start(&pkg_ident.pkg_ident(),
                                                             cmd,
                                                             &args.args);
                        }
                        _ => {
                            // All other commands will be caught by the CLI parsing logic below.
                        }
                    }
                }
                _ => {
                    // All other commands will be caught by the CLI parsing logic below.
                }
            }
        }
        Err(e @ ConfigOptError::ConfigGenerated(_)) => e.exit(),
        Err(_) => {
            // Completely ignore all other errors. They will be caught by the CLI parsing logic
            // below.
        }
    };

    // Similar to the configopt parsing above We build the command tree in a
    // separate thread to eliminate possible stack overflow crashes at runtime.
    // See known issue:https://github.com/kbknapp/clap-rs/issues/86
    let cli_child = thread::Builder::new().stack_size(8 * 1024 * 1024)
                                          .spawn(move || {
                                              cli::get(feature_flags).get_matches_safe()
                                                                     .unwrap_or_else(|e| {
                                                                         e.exit();
                                                                     })
                                          })
                                          .unwrap();
    let app_matches = cli_child.join().unwrap();

    match app_matches.subcommand() {
        ("apply", Some(m)) => {<commit_msg>fix panic on invalid sup.toml file<commit_after>#![recursion_limit = "128"]

#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

use clap::{ArgMatches,
           ErrorKind as ClapErrorKind,
           Shell};
use configopt::{ConfigOpt,
                Error as ConfigOptError};
use futures::stream::StreamExt;
use hab::{cli::{self,
                gateway_util,
                hab::{license::License,
                      origin::{Rbac,
                               RbacSet,
                               RbacShow},
                      pkg::{ExportCommand as PkgExportCommand,
                            PkgExec},
                      sup::{HabSup,
                            Secret,
                            Sup},
                      svc::{self,
                            BulkLoad as SvcBulkLoad,
                            Load as SvcLoad,
                            Svc},
                      util::{bldr_auth_token_from_args_env_or_load,
                             bldr_url_from_args_env_load_or_default},
                      Hab,
                      Origin,
                      Pkg},
                parse_optional_arg,
                KeyType},
          command::{self,
                    pkg::{download::{PackageSet,
                                     PackageSetFile},
                          list::ListingType,
                          uninstall::UninstallHookMode}},
          error::{Error,
                  Result},
          license,
          scaffolding,
          AUTH_TOKEN_ENVVAR,
          BLDR_URL_ENVVAR,
          ORIGIN_ENVVAR,
          PRODUCT,
          VERSION};
use habitat_api_client::BuildOnUpload;
use habitat_common::{self as common,
                     cli::key_cache_from_matches,
                     cli_config::CliConfig,
                     command::package::install::{InstallHookMode,
                                                 InstallMode,
                                                 InstallSource,
                                                 LocalPackageUsage},
                     types::ResolvedListenCtlAddr,
                     ui::{self,
                          Status,
                          UIWriter,
                          UI},
                     FeatureFlag};
use habitat_core::{crypto::{init,
                            keys::{Key,
                                   KeyCache}},
                   env::{self as henv,
                         Config as _},
                   fs::{cache_artifact_path,
                        FS_ROOT_PATH},
                   os::process::ShutdownTimeout,
                   package::{target,
                             PackageIdent,
                             PackageTarget},
                   service::ServiceGroup,
                   tls::ctl_gateway as ctl_gateway_tls,
                   url::default_bldr_url,
                   ChannelIdent};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol::{self as sup_proto,
                           codec::*,
                           net::ErrCode,
                           types::*};
use std::{collections::HashMap,
          convert::TryFrom,
          env,
          ffi::OsString,
          fs::File,
          io::{self,
               prelude::*,
               Read},
          path::{Path,
                 PathBuf},
          process,
          result,
          str::FromStr,
          string::ToString,
          thread};
use tabwriter::TabWriter;
use webpki::DnsNameRef;

/// Makes the --org CLI param optional when this env var is set
const HABITAT_ORG_ENVVAR: &str = "HAB_ORG";
/// Makes the --user CLI param optional when this env var is set
const HABITAT_USER_ENVVAR: &str = "HAB_USER";

lazy_static! {
    static ref STATUS_HEADER: Vec<&'static str> = {
        vec!["package",
             "type",
             "desired",
             "state",
             "elapsed (s)",
             "pid",
             "group",]
    };
}

#[tokio::main]
async fn main() {
    env_logger::init();
    let mut ui = UI::default_with_env();
    let flags = FeatureFlag::from_env(&mut ui);
    if let Err(e) = start(&mut ui, flags).await {
        let exit_code = e.exit_code();
        ui.fatal(e).unwrap();
        std::process::exit(exit_code)
    }
}

#[allow(clippy::cognitive_complexity)]
async fn start(ui: &mut UI, feature_flags: FeatureFlag) -> Result<()> {
    // We parse arguments with configopt in a separate thread to eliminate
    // possible stack overflow crashes at runtime. OSX or a debug Windows build,
    // for instance, will crash with our large tree. This is a known issue:
    // https://github.com/kbknapp/clap-rs/issues/86
    let child = thread::Builder::new().stack_size(8 * 1024 * 1024)
                                      .spawn(Hab::try_from_args_with_configopt)
                                      .unwrap();
    let hab = child.join().unwrap();

    if let Ok(Hab::License(License::Accept)) = hab {
        license::accept_license(ui)?;
        return Ok(());
    }

    // Allow checking version information and displaying command help without accepting the license.
    // TODO (DM): To prevent errors in discrepancy between the structopt and cli versions only do
    // this when the license has not yet been accepted. When we switch fully to structopt this can
    // be completely removed and we should just call `Hab::from_args_with_configopt` which will
    // automatically result in this functionality.
    if !license::check_for_license_acceptance().unwrap_or_default()
                                               .accepted()
    {
        if let Err(ConfigOptError::Clap(e)) = &hab {
            if e.kind == ClapErrorKind::VersionDisplayed || e.kind == ClapErrorKind::HelpDisplayed {
                e.exit()
            }
        }
    }

    // We must manually detect a supervisor version check and call the `hab-sup` binary to get the
    // true Supervisor version.
    // TODO (DM): This is an ugly consequence of having `hab sup` subcommands handled by both the
    // `hab` binary and the `hab-sup` binary. Potential fixes:
    // 1. Handle all `hab sup` subcommands with the `hab-sup` binary
    // 2. Have a dedicated subcommand for commands handled by the `hab-sup` binary
    let mut args = env::args();
    if matches!((args.next().unwrap_or_default().as_str(),
                 args.next().unwrap_or_default().as_str(),
                 args.next().unwrap_or_default().as_str()),
                (_, "sup", "--version") | (_, "sup", "-V"))
    {
        return command::sup::start(ui, &args_after_first(2)).await;
    }

    license::check_for_license_acceptance_and_prompt(ui)?;

    // Parse and handle commands which have been migrated to use `structopt` here. Once everything
    // is migrated to use `structopt` the parsing logic below this using clap directly will be gone.
    match hab {
        Ok(hab) => {
            match hab {
                Hab::Origin(Origin::Rbac(action)) => {
                    match action {
                        Rbac::Set(rbac_set) => {
                            return sub_origin_member_role_set(ui, rbac_set).await;
                        }
                        Rbac::Show(rbac_show) => {
                            return sub_origin_member_role_show(ui, rbac_show).await;
                        }
                    }
                }
                #[cfg(not(target_os = "macos"))]
                Hab::Run(sup_run) => {
                    ui.warn("'hab run' as an alias for 'hab sup run' is deprecated. Please \
                             update your automation and processes accordingly.")?;
                    return command::launcher::start(ui, sup_run, &args_after_first(1)).await;
                }
                Hab::Studio(studio) => {
                    return command::studio::enter::start(ui, studio.args()).await;
                }
                Hab::Sup(sup) => {
                    match sup {
                        #[cfg(not(target_os = "macos"))]
                        HabSup::Sup(sup) => {
                            // These commands are handled by the `hab-sup` or `hab-launch` binaries.
                            // We need to pass the subcommand that was issued to the underlying
                            // binary. It is a bit hacky, but to do that we strip off the `hab sup`
                            // command prefix and pass the rest of the args to underlying binary.
                            let args = args_after_first(2);
                            match sup {
                                Sup::Bash | Sup::Sh | Sup::Term => {
                                    return command::sup::start(ui, &args).await;
                                }
                                Sup::Run(sup_run) => {
                                    return command::launcher::start(ui, sup_run, &args).await;
                                }
                            }
                        }
                        HabSup::Depart { member_id,
                                         remote_sup, } => {
                            return sub_sup_depart(member_id, remote_sup.inner()).await;
                        }
                        HabSup::Secret(secret) => {
                            match secret {
                                Secret::Generate => return sub_sup_secret_generate(),
                                Secret::GenerateTls { subject_alternative_name,
                                                      path, } => {
                                    return sub_sup_secret_generate_key(subject_alternative_name.inner(),
                                                                       path)
                                }
                            }
                        }
                        HabSup::Status { pkg_ident,
                                         remote_sup, } => {
                            ui.warn("'hab sup status' as an alias for 'hab svc status' is \
                                     deprecated. Please update your automation and processes \
                                     accordingly.")?;
                            return sub_svc_status(pkg_ident, remote_sup.inner()).await;
                        }
                        HabSup::Restart { remote_sup } => {
                            return sub_sup_restart(remote_sup.inner()).await;
                        }
                    }
                }
                Hab::Svc(svc) => {
                    match svc {
                        Svc::BulkLoad(svc_bulk_load) => {
                            if feature_flags.contains(FeatureFlag::SERVICE_CONFIG_FILES) {
                                return sub_svc_bulk_load(svc_bulk_load).await;
                            } else {
                                return Err(Error::ArgumentError(String::from("`hab svc bulkload` is only available when `HAB_FEAT_SERVICE_CONFIG_FILES` is set")));
                            }
                        }
                        Svc::Load(svc_load) => {
                            return sub_svc_load(svc_load).await;
                        }
                        Svc::Update(svc_update) => return sub_svc_update(svc_update).await,
                        Svc::Status(svc_status) => {
                            return sub_svc_status(svc_status.pkg_ident,
                                                  svc_status.remote_sup.inner()).await;
                        }
                        _ => {
                            // All other commands will be caught by the CLI parsing logic below.
                        }
                    }
                }
                #[cfg(not(target_os = "macos"))]
                Hab::Term => {
                    ui.warn("'hab term' as an alias for 'hab sup term' is deprecated. Please \
                             update your automation and processes accordingly.")?;
                    return command::sup::start(ui, &args_after_first(1)).await;
                }
                Hab::Pkg(pkg) => {
                    match pkg {
                        Pkg::Export(export) => {
                            match export {
                                #[cfg(target_os = "linux")]
                                PkgExportCommand::Cf(args) => {
                                    return command::pkg::export::cf::start(ui, &args.args).await;
                                }
                                #[cfg(any(target_os = "linux", target_os = "windows"))]
                                PkgExportCommand::Container(args) => {
                                    return command::pkg::export::container::start(ui, &args.args).await;
                                }
                                #[cfg(any(target_os = "linux", target_os = "windows"))]
                                PkgExportCommand::Docker(args) => {
                                    ui.warn("'hab pkg export docker' is now a deprecated alias \
                                             for 'hab pkg export container'. Please update your \
                                             automation and processes accordingly.")?;
                                    return command::pkg::export::container::start(ui, &args.args).await;
                                }
                                #[cfg(target_os = "linux")]
                                PkgExportCommand::Mesos(args) => {
                                    return command::pkg::export::mesos::start(ui, &args.args).await;
                                }
                                #[cfg(any(target_os = "linux", target_os = "windows"))]
                                PkgExportCommand::Tar(args) => {
                                    return command::pkg::export::tar::start(ui, &args.args).await;
                                }
                            }
                        }
                        Pkg::Exec(PkgExec { pkg_ident,
                                            cmd,
                                            args, }) => {
                            return command::pkg::exec::start(&pkg_ident.pkg_ident(),
                                                             cmd,
                                                             &args.args);
                        }
                        _ => {
                            // All other commands will be caught by the CLI parsing logic below.
                        }
                    }
                }
                _ => {
                    // All other commands will be caught by the CLI parsing logic below.
                }
            }
        }
        Err(e @ ConfigOptError::ConfigGenerated(_) | e @ ConfigOptError::ConfigFile(..)) => {
            e.exit()
        }
        Err(_) => {
            // Completely ignore all other errors. They will be caught by the CLI parsing logic
            // below.
        }
    };

    // Similar to the configopt parsing above We build the command tree in a
    // separate thread to eliminate possible stack overflow crashes at runtime.
    // See known issue:https://github.com/kbknapp/clap-rs/issues/86
    let cli_child = thread::Builder::new().stack_size(8 * 1024 * 1024)
                                          .spawn(move || {
                                              cli::get(feature_flags).get_matches_safe()
                                                                     .unwrap_or_else(|e| {
                                                                         e.exit();
                                                                     })
                                          })
                                          .unwrap();
    let app_matches = cli_child.join().unwrap();

    match app_matches.subcommand() {
        ("apply", Some(m)) => {
{'fraction_non_alphanumeric': 0.09372746935832732, 'fraction_numerical': 0.0019435127425472556, 'mean_word_length': 1.047493742378538, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 81, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 128, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1848558', 'n_tokens_mistral': 7605, 'n_tokens_neox': 7328, 'n_words': 1693}