Merge branch 'master' into warnings-remove-kill

This commit is contained in:
Julian Kaindl 2020-10-18 18:37:56 +02:00 committed by GitHub
commit 2ffa264b62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 113 additions and 60 deletions

View file

@ -16,11 +16,19 @@ This version adds breaking changes:
- Unix socket support (#90)
- Warning messages for removing/killing tasks (#111) by [Julian Kaindl](https://github.com/kaindljulian)
### Changed
- Never create a default config when starting the client. Only starting the daemon can do that.
- Windows: The configuration file will now also be placed in `%APPDATA%\Local\pueue`.
### Fixed
- Fixed panic, when killing and immediately removing a task. (#119)
- Fixed broken non-responsive daemon, on panic in threads. (#119)
- Don't allow empty commands on `add`.
- The client will never persist/write the configuration file. (#116)
- The daemon will only persist configuration file on startup, if anything changes. (#116)
- (Probably fixed) Malformed configuration file. (#116)
## [0.7.2] - 2020-10-05

View file

@ -246,42 +246,52 @@ SUBCOMMANDS:
## Configs
The configuration file of Pueue is located in `$CARGO_HOME/pueue.yml`.
The default will be generated after starting pueue once.
The configuration file of Pueue is located in these directories:
- Linux: `$HOME/.config/pueue/pueue.yml`.
- MacOs: `$HOME/Library/Preferences/pueue/pueue.yml`
- Windows: `%APPDATA%\Local\pueue`
A default configuration file will be generated after starting `pueued` for the first time.
```yaml
---
client:
daemon_port: "6924"
shared:
port: "6924"
secret: "your_secret"
pueue_directory: /home/$USER/.local/share/pueue
use_unix_sockets: false
unix_sockets_path: /home/$USER/.local/share/pueue/pueue_$USER.socket
client:
read_local_logs: true
print_remove_warnings: false
daemon:
pueue_directory: /home/$USER/.local/share/pueue
default_parallel_tasks: 1
pause_on_failure: false
port: "6924"
secret: "your_secret"
callback: ""Task {{ id }}\nCommand: {{ command }}\nPath: {{ path }}\nFinished with status '{{ result }}'\""
groups:
cpu: 1
```
**Client**:
### Shared
- `daemon_port` The port the client tries to connect to.
- `port` The port the daemon listens on and the client connects to in TCP mode.
- `secret` The secret, that's used for authentication
- `pueue_directory` The location Pueue uses for its intermediate files and logs.
- `use_unix_sockets` Whether the daemon should listen on a Unix- or a TCP-socket.
- `unix_socket_path` The path the unix socket is located at.
### Client
- `read_local_logs` If the client runs as the same user (and on the same machine) as the daemon, logs don't have to be sent via the socket but rather read directly.
- `print_remove_warnings` The client will print warnings that require confirmation for different critical commands.
**Daemon**:
### Daemon
- `pueue_directory` The location Pueue uses for its intermediate files and logs.
- `default_parallel_tasks` Determines how many tasks should be processed concurrently.
- `default_parallel_tasks` Determines how many tasks should be processed concurrently.
- `pause_on_failure` If set to `true`, the daemon stops starting new task as soon as a single task fails. Already running tasks will continue.
- `port` The port the daemon should listen to.
- `secret` The secret, that's used for authentication
- `callback` The command that will be called after a task finishes. Can be parameterized
- `groups` This is a list of the groups with their amount of allowed parallel tasks. It's advised to not manipulate this manually, but rather use the `group` subcommand to create and remove groups.

View file

@ -15,28 +15,9 @@ use crate::client::Client;
#[async_std::main]
async fn main() -> Result<()> {
// Get settings from the configuration file and the program defaults.
let settings = Settings::new()?;
// Immediately save it. This also creates the save file in case it didn't exist yet.
if let Err(error) = settings.save() {
println!("Failed saving config file.");
println!("{:?}", error);
}
// Parse commandline options.
let opt = Opt::from_args();
// Set the verbosity level for the client app.
if opt.verbose >= 3 {
SimpleLogger::init(LevelFilter::Debug, Config::default())?;
} else if opt.verbose == 2 {
SimpleLogger::init(LevelFilter::Info, Config::default())?;
} else if opt.verbose == 1 {
SimpleLogger::init(LevelFilter::Warn, Config::default())?;
} else if opt.verbose == 0 {
SimpleLogger::init(LevelFilter::Error, Config::default())?;
}
if let SubCommand::Completions {
shell,
output_directory,
@ -47,6 +28,20 @@ async fn main() -> Result<()> {
return Ok(());
}
// Set the verbosity level of the logger.
if opt.verbose >= 3 {
SimpleLogger::init(LevelFilter::Debug, Config::default())?;
} else if opt.verbose == 2 {
SimpleLogger::init(LevelFilter::Info, Config::default())?;
} else if opt.verbose == 1 {
SimpleLogger::init(LevelFilter::Warn, Config::default())?;
} else if opt.verbose == 0 {
SimpleLogger::init(LevelFilter::Error, Config::default())?;
}
// Try to read settings from the configuration file.
let settings = Settings::new(true)?;
// Create client to talk with the daemon and connect.
let mut client = Client::new(settings, opt).await?;
client.start().await?;

View file

@ -27,14 +27,6 @@ mod task_handler;
#[async_std::main]
async fn main() -> Result<()> {
// Get settings from the configuration file and the program defaults.
let settings = Settings::new()?;
// Immediately save it. This also creates the save file in case it didn't exist yet.
if let Err(error) = settings.save() {
println!("Failed saving config file.");
println!("{:?}", error);
}
// Parse commandline options.
let opt = Opt::from_args();
@ -42,7 +34,7 @@ async fn main() -> Result<()> {
fork_daemon(&opt)?;
}
// Set the verbosity level for the client app.
// Set the verbosity level of the logger.
if opt.verbose >= 3 {
SimpleLogger::init(LevelFilter::Debug, Config::default())?;
} else if opt.verbose == 2 {
@ -53,6 +45,25 @@ async fn main() -> Result<()> {
SimpleLogger::init(LevelFilter::Error, Config::default())?;
}
// Try to read settings from the configuration file.
let settings = match Settings::read(false) {
Ok(settings) => settings,
Err(_) => {
// There's something wrong with the config file or something's missing.
// Try to read the config and fill missing values with defaults.
// This might be possible on version upgrade or first run.
let settings = Settings::new(false)?;
// Since we needed to add values to the configuration, we have to save it.
// This also creates the save file in case it didn't exist yet.
if let Err(error) = settings.save() {
println!("Failed saving config file.");
println!("{:?}", error);
}
settings
}
};
init_directories(&settings.shared.pueue_directory);
let state = State::new(&settings);

View file

@ -18,10 +18,14 @@ pub fn compile_shell_command(command_string: &str) -> Command {
/// Send a signal to a windows process.
pub fn send_signal_to_child(
_child: &Child,
_action: &ProcessAction,
action: &ProcessAction,
_children: bool,
) -> Result<bool> {
bail!("not supported on windows.")
match action {
ProcessAction::Pause => bail!("Pause is not yet supported on windows."),
ProcessAction::Resume => bail!("Resume is not yet supported on windows."),
ProcessAction::Kill => bail!("Kill is not yet supported on windows."),
}
}
/// Kill a child process

View file

@ -44,6 +44,12 @@ pub type Socket = Box<dyn GenericSocket>;
/// which depends on the parameters.
pub async fn get_client(unix_socket_path: Option<String>, port: Option<String>) -> Result<Socket> {
if let Some(socket_path) = unix_socket_path {
if !PathBuf::from(&socket_path).exists() {
bail!(
"Couldn't find unix socket at path {:?}. Is the daemon running yet?",
socket_path
);
}
let stream = UnixStream::connect(socket_path).await?;
return Ok(Box::new(stream));
}

View file

@ -12,15 +12,14 @@ fn get_home_dir() -> Result<PathBuf> {
}
pub fn default_config_directory() -> Result<PathBuf> {
Ok(get_home_dir()?.join("pueue"))
Ok(dirs::data_local_dir()
.ok_or(anyhow!("Couldn't resolve app data directory"))?
.join("pueue"))
}
pub fn get_config_directories() -> Result<Vec<PathBuf>> {
Ok(vec![
// Windows Terminal stores its config file in the "AppData/Local" directory.
dirs::data_local_dir()
.ok_or(anyhow!("Couldn't resolve app data directory"))?
.join("pueue"),
default_config_directory()?,
Path::new(".").to_path_buf(),
])

View file

@ -2,8 +2,9 @@ use std::collections::HashMap;
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use anyhow::{anyhow, Result};
use anyhow::{anyhow, bail, Result};
use config::Config;
use log::info;
use rand::Rng;
use serde_derive::{Deserialize, Serialize};
@ -30,16 +31,11 @@ pub struct Client {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Daemon {
pub default_parallel_tasks: usize,
#[serde(default = "pause_on_failure_default")]
pub pause_on_failure: bool,
pub callback: Option<String>,
pub groups: HashMap<String, usize>,
}
fn pause_on_failure_default() -> bool {
false
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Settings {
pub shared: Shared,
@ -53,7 +49,9 @@ impl Settings {
/// If a local config file already exists it is parsed and
/// overwrites the default option values.
/// The local config is located at "~/.config/pueue.yml".
pub fn new() -> Result<Settings> {
///
/// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
pub fn new(require_config: bool) -> Result<Settings> {
let mut config = Config::new();
config.set_default("shared.port", "6924")?;
@ -73,7 +71,21 @@ impl Settings {
config.set_default("daemon.groups", HashMap::<String, i64>::new())?;
// Add in the home config file
parse_config(&mut config)?;
parse_config(&mut config, require_config)?;
// Try to can deserialize the entire configuration
Ok(config.try_into()?)
}
/// Try to read the config file without any default values.
/// This is done by the daemon on startup.
/// If the file can be read without any need for defaults, we don't have to persist it
/// afterwards.
pub fn read(require_config: bool) -> Result<Settings> {
let mut config = Config::new();
// Merge configuration files we can find in ascending order.
parse_config(&mut config, require_config)?;
// Try to can deserialize the entire configuration
Ok(config.try_into()?)
@ -103,18 +115,26 @@ impl Settings {
/// Get all possible configuration paths and check if there are
/// configuration files at those locations.
/// All configs will be merged by importance.
fn parse_config(settings: &mut Config) -> Result<()> {
//println!("Parsing config files");
///
/// If `require_config` is `true`, an error will be thrown, if no configuration file can be found.
fn parse_config(settings: &mut Config, require_config: bool) -> Result<()> {
let mut config_found = false;
info!("Parsing config files");
for directory in get_config_directories()?.into_iter() {
let path = directory.join("pueue.yml");
//println!("Checking path: {:?}", &path);
info!("Checking path: {:?}", &path);
if path.exists() {
//println!("Parsing config file at: {:?}", path);
info!("Found config file at: {:?}", path);
config_found = true;
let config_file = config::File::with_name(path.to_str().unwrap());
settings.merge(config_file)?;
}
}
if require_config && !config_found {
bail!("Couldn't find a configuration file. Did you start the daemon yet?");
}
Ok(())
}