Skip to content
This repository has been archived by the owner on Sep 27, 2021. It is now read-only.

Commit

Permalink
Add 'nexus-fixer'
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry committed Jul 24, 2019
1 parent d63c7a0 commit 2d1078f
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ native
# Maven / SBT / Ant target #
target/
.bloop
project/.bloop

# IDE
.settings
Expand All @@ -21,6 +20,9 @@ project/.bloop
**/logs/
**/nbactions.xml

# Rust
**/*.rs.bk
Cargo.lock

# Packages #
############
Expand Down
12 changes: 11 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ Boolean isPR = env.CHANGE_ID != null

pipeline {
agent { label 'slave-sbt' }
tools {
jdk 'jdk11'
}
options {
timeout(time: 30, unit: 'MINUTES')
}
environment {
NEXUS_PATH_PREFIX = """${sh(returnStdout: true, script: 'oc get configmap storage -o jsonpath="{.data.path_prefix}"')}"""
NEXUS_USER_ID = """${sh(returnStdout: true, script: 'oc get configmap storage -o jsonpath="{.data.user_id}"')}"""
NEXUS_GROUP_ID = """${sh(returnStdout: true, script: 'oc get configmap storage -o jsonpath="{.data.group_id}"')}"""
}
stages {
stage("Review") {
when {
Expand Down Expand Up @@ -35,10 +43,12 @@ pipeline {
}
stage("Build & Publish Artifacts") {
when {
expression { !isPR }
expression { isPR }
}
steps {
checkout scm
sh 'java -version'
sh 'echo $NEXUS_PATH_PREFIX $NEXUS_USER_ID $NEXUS_GROUP_ID'
sh 'sbt releaseEarly universal:packageZipTarball'
stash name: "service", includes: "target/universal/storage-*.tgz"
}
Expand Down
19 changes: 18 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ lazy val storage = project
mockito % Test,
scalaTest % Test
),
cleanFiles += baseDirectory.value / "permissions-fixer" / "target" / "**",
mappings in Universal := {
val universalMappings = (mappings in Universal).value
val universalMappings = (mappings in Universal).value :+ cargo.value
universalMappings.foldLeft(Vector.empty[(File, String)]) {
case (acc, (file, filename)) if filename.contains("kanela-agent") =>
acc :+ (file, "lib/instrumentation-agent.jar")
Expand Down Expand Up @@ -128,6 +129,22 @@ lazy val buildInfoSettings = Seq(
buildInfoPackage := "ch.epfl.bluebrain.nexus.storage.config"
)

lazy val cargo = taskKey[(File, String)]("Run Cargo to build 'nexus-fixer'")

cargo := {
import scala.sys.process._

val log = streams.value.log
val cmd = Process(Seq("cargo", "build", "--release"), baseDirectory.value / "permissions-fixer")
if ((cmd !) == 0) {
log.success("Cargo build successful.")
(baseDirectory.value / "permissions-fixer" / "target" / "release" / "nexus-fixer") -> "bin/nexus-fixer"
} else {
log.error("Cargo build failed.")
throw new RuntimeException
}
}

inThisBuild(
List(
homepage := Some(url("https://github.com/BlueBrain/nexus-storage")),
Expand Down
14 changes: 14 additions & 0 deletions permissions-fixer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "nexus-fixer"
version = "1.0.0"
authors = ["Henry <henry.genet@epfl.ch>"]
edition = "2018"
build = "build.rs"

[dependencies]
clap = "2.33.0"
libc = "0.2.60"
rand = "0.7"

[build-dependencies]
built = "0.3.1"
34 changes: 34 additions & 0 deletions permissions-fixer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Nexus storage permissions fixer

### Setup

Use [rustup](https://rustup.rs).

### Build with custom configuration

```bash
NEXUS_PATH_PREFIX=/path/to/gpfs/ NEXUS_USER_NAME=bbp.gpfs.nexus NEXUS_GROUP_NAME=bbp cargo build --release
```

### Usage

#### Apply permissions

```bash
nexus-fixer PATH
```

Where `PATH` is an absolute and valid path to a file or directory.
If `PATH` points to a directory, permissions are applied recursively on the directory and all its children.

#### Show compile-time configuration

```bash
nexus-fixer -s
```

#### Show help

```bash
nexus-fixer -h
```
5 changes: 5 additions & 0 deletions permissions-fixer/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use built::write_built_file;

fn main() {
write_built_file().expect("Failed to acquire build-time information");
}
55 changes: 55 additions & 0 deletions permissions-fixer/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use libc::{gid_t, uid_t};

use crate::errors::Failure;
use crate::errors::Failure::{InvalidGroupId, InvalidUserId};

#[allow(dead_code)]
mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

const PATH_PREFIX: Option<&'static str> = option_env!("NEXUS_PATH_PREFIX");
const PATH_PREFIX_DEFAULT: &'static str = "/tmp/";

const UID: Option<&'static str> = option_env!("NEXUS_USER_ID");
const UID_DEFAULT: uid_t = 1000;

const GID: Option<&'static str> = option_env!("NEXUS_GROUP_ID");
const GID_DEFAULT: gid_t = 1000;

pub const FILE_MASK: u32 = 0o440;
pub const DIR_MASK: u32 = 0o550;

pub fn get_path_prefix() -> &'static str {
PATH_PREFIX.unwrap_or(PATH_PREFIX_DEFAULT)
}

pub fn get_uid() -> Result<uid_t, Failure> {
match UID {
Some(uid) => uid.parse::<uid_t>().map_err(|_| InvalidUserId),
None => Ok(UID_DEFAULT),
}
}

pub fn get_gid() -> Result<gid_t, Failure> {
match GID {
Some(gid) => gid.parse::<gid_t>().map_err(|_| InvalidGroupId),
None => Ok(GID_DEFAULT),
}
}

pub fn show_config() -> Result<(), Failure> {
println!("Package version: {}", built_info::PKG_VERSION);
println!(
"Git commit: {}",
built_info::GIT_VERSION.unwrap_or("Unknown")
);
println!("Rust version: {}", built_info::RUSTC_VERSION);
println!("Platform: {}", built_info::TARGET);
println!("Path prefix: {}", get_path_prefix());
println!("UID: {}", get_uid()?);
println!("GID: {}", get_gid()?);
println!("File permissions mask: {:o}", FILE_MASK);
println!("Directory permissions mask: {:o}", DIR_MASK);
Ok(())
}
45 changes: 45 additions & 0 deletions permissions-fixer/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::io;

use Failure::*;

#[derive(Eq, PartialEq)]
pub enum Failure {
PathCannotBeEmpty,
PathMustBeAbsolute,
PathMustBeCanonical,
PathMustStartWithPrefix,
PathCannotHaveNul,
FileNotFound,
IOError(String),
InvalidUserId,
InvalidGroupId,
ChmodFailed,
ChownFailed,
}

impl From<io::Error> for Failure {
fn from(ioe: io::Error) -> Self {
Failure::IOError(ioe.description().to_owned())
}
}

impl Debug for Failure {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
IOError(desc) => write!(f, "IO exception: {}", desc),
PathCannotBeEmpty => write!(f, "{}", "path cannot be empty"),
PathMustBeAbsolute => write!(f, "{}", "path must be absolute"),
PathMustBeCanonical => write!(f, "{}", "path must be canonical"),
PathMustStartWithPrefix => write!(f, "{}", "path must start with configured prefix"),
PathCannotHaveNul => write!(f, "{}", "path cannot contain 'nul' characters"),
FileNotFound => write!(f, "{}", "file not found"),
InvalidUserId => write!(f, "{}", "invalid user ID"),
InvalidGroupId => write!(f, "{}", "invalid group ID"),
ChmodFailed => write!(f, "{}", "'chmod' operation failed"),
ChownFailed => write!(f, "{}", "'chown' operation failed"),
}
}
}
43 changes: 43 additions & 0 deletions permissions-fixer/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use clap::{App, Arg};

use crate::config::show_config;
use crate::errors::Failure;
use crate::errors::Failure::PathCannotBeEmpty;
use crate::path::*;

mod config;
mod errors;
mod path;

fn main() -> Result<(), Failure> {
let args = App::new("nexus-fixer")
.version("1.0.0")
.author("BlueBrain Nexus <http://github.com/BlueBrain/nexus>")
.about("Utility to fix permissions on files moved by the Nexus storage service.")
.arg(
Arg::with_name("PATH")
.help("The target path")
.required(true)
.conflicts_with("show-config")
.index(1),
)
.arg(
Arg::with_name("show-config")
.short("s")
.long("show-config")
.help("Prints compile-time configuration")
.conflicts_with("PATH"),
)
.get_matches();

if args.is_present("show-config") {
return show_config();
}

match args.value_of("PATH") {
Some(path) => check_path(path)
.and_then(visit_all)
.map(|_| println!("Done.")),
None => Err(PathCannotBeEmpty),
}
}
Loading

0 comments on commit 2d1078f

Please sign in to comment.