Skip to content

Commit

Permalink
feat: add smdk publish (#2657)
Browse files Browse the repository at this point in the history
part of #2631
  • Loading branch information
digikata committed Oct 7, 2022
1 parent b51f9ad commit 737e330
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 22 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/fluvio-hub-util/Cargo.toml
Expand Up @@ -11,6 +11,7 @@ repository = "https://github.com/infinyon/fluvio"
[dependencies]
cargo_toml = "0.11"
const_format = "0.2"
dirs = "4.0.0"
ed25519-dalek = { version = "1.0.1", features = ["serde"] }
flate2 = "1.0"
hex = "0.4"
Expand All @@ -31,6 +32,7 @@ tracing = "0.1.36"
thiserror = "1.0"

fluvio-controlplane-metadata = { path="../fluvio-controlplane-metadata", features = [ "smartmodule" ] }
fluvio-types = { path = "../fluvio-types" }

# feature control
[dependencies.surf]
Expand Down
5 changes: 5 additions & 0 deletions crates/fluvio-hub-util/src/errors.rs
@@ -1,3 +1,5 @@
use crate::infinyon_tok::InfinyonCredentialError;

#[derive(thiserror::Error, Debug)]
pub enum HubUtilError {
#[error(transparent)]
Expand Down Expand Up @@ -54,6 +56,9 @@ pub enum HubUtilError {
#[error("Unable to access package-meta in: {0}")]
UnableGetPackageMeta(String),

#[error(transparent)]
UnableToReadCredentials(#[from] InfinyonCredentialError),

#[error("signature error")]
SignatureError,

Expand Down
20 changes: 12 additions & 8 deletions crates/fluvio-hub-util/src/hubaccess.rs
Expand Up @@ -7,7 +7,7 @@ use surf::StatusCode;
use tracing::debug;

use crate::errors::Result;
use crate::HubUtilError;
use crate::{HubUtilError, read_infinyon_token};
use crate::keymgmt::Keypair;
use crate::{HUB_API_ACT, HUB_API_HUBID, HUB_REMOTE};

Expand All @@ -20,18 +20,16 @@ const ACTION_CREATE_HUBID: &str = "chid";

#[derive(Serialize, Deserialize)]
pub struct HubAccess {
pub remote: String, // remote host url
pub authn_token: String, // cloud auth token
pub hubid: String, // hubid associated with the signing key
pub pkgkey: String, // package signing key (private)
pub pubkey: String, // package signing key (public)
pub remote: String, // remote host url
pub hubid: String, // hubid associated with the signing key
pub pkgkey: String, // package signing key (private)
pub pubkey: String, // package signing key (public)
}

impl HubAccess {
pub fn new() -> Self {
HubAccess {
remote: HUB_REMOTE.to_string(),
authn_token: String::new(),
hubid: String::new(),
pkgkey: String::new(),
pubkey: String::new(),
Expand Down Expand Up @@ -95,10 +93,11 @@ impl HubAccess {
};
let msg_action_token = serde_json::to_string(&mat)
.map_err(|_e| HubUtilError::HubAccess("Failed access setup".to_string()))?;
let authn_token = read_infinyon_token()?;
let req = surf::get(api_url)
.content_type(mime::JSON)
.body_bytes(msg_action_token)
.header("Authorization", &self.authn_token);
.header("Authorization", &authn_token);
let mut res = req
.await
.map_err(|e| HubUtilError::HubAccess(format!("Failed to connect {e}")))?;
Expand Down Expand Up @@ -197,6 +196,11 @@ impl HubAccess {
write_ptr_file(base_path, &hname)?;
Ok(())
}

/// return signing keypair
pub fn keypair(&self) -> Result<Keypair> {
Keypair::from_hex(&self.pkgkey)
}
}

fn write_ptr_file<P: AsRef<Path>>(base_path: P, profile: &str) -> Result<()> {
Expand Down
94 changes: 94 additions & 0 deletions crates/fluvio-hub-util/src/infinyon_tok.rs
@@ -0,0 +1,94 @@
//
// minimal login token read module that just exposes a
// 'read_infinyon_token' function to read from the current login config
//
use std::env;
use std::fs;
use std::path::Path;

use serde::{Deserialize, Serialize};
use fluvio_types::defaults::CLI_CONFIG_PATH;

const INFINYON_CONFIG_PATH_ENV: &str = "INFINYON_CONFIG_PATH";
const DEFAULT_LOGINS_DIR: &str = "logins"; // from logins.rs
const CURRENT_LOGIN_FILE_NAME: &str = "current";

type InfinyonToken = String;

#[derive(thiserror::Error, Debug)]
pub enum InfinyonCredentialError {
#[error("Read error {0}")]
Read(String),

#[error("unable to parse credentials")]
UnableToParseCredentials,
}

pub fn read_infinyon_token() -> Result<InfinyonToken, InfinyonCredentialError> {
// the ENV variable should point directly to the applicable profile
if let Ok(profilepath) = env::var(INFINYON_CONFIG_PATH_ENV) {
let cred = Credentials::load(Path::new(&profilepath))?;
return Ok(cred.token);
}
let cfgpath = default_file_path();
// this will read the indirection file to resolve the profile
let cred = Credentials::try_load(&cfgpath)?;
Ok(cred.token)
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Credentials {
remote: String,
email: String,
id: String,
token: String,
}

impl Credentials {
/// Try to load credentials from disk
fn try_load<P: AsRef<Path>>(base_path: P) -> Result<Self, InfinyonCredentialError> {
let current_login_path = base_path.as_ref().join(CURRENT_LOGIN_FILE_NAME);
let cfg_path = fs::read_to_string(&current_login_path).map_err(|_| {
let strpath = current_login_path.to_string_lossy().to_string();
InfinyonCredentialError::Read(strpath)
})?;
let cred_path = base_path.as_ref().join(cfg_path);
Self::load(&cred_path)
}

fn load(cred_path: &Path) -> Result<Self, InfinyonCredentialError> {
let file_str = fs::read_to_string(&cred_path).map_err(|_| {
let strpath = cred_path.to_string_lossy().to_string();
InfinyonCredentialError::Read(strpath)
})?;
let creds: Credentials = toml::from_str(&*file_str)
.map_err(|_| InfinyonCredentialError::UnableToParseCredentials)?;
Ok(creds)
}
}

fn default_file_path() -> String {
let mut login_path = dirs::home_dir().unwrap_or_default();
login_path.push(CLI_CONFIG_PATH);
login_path.push(DEFAULT_LOGINS_DIR);
login_path.to_string_lossy().to_string()
}

#[cfg(test)]
mod infinyon_tok_tests {
use super::read_infinyon_token;

// load default credentials (ignore by default becasuse config is not populated in ci env)
#[ignore]
#[test]
fn read_default() {
let token = read_infinyon_token();
assert!(token.is_ok());
println!("token: {}", token.unwrap());
}

// #[test]
// fn load_from_env() {
// std::env::set_var(INFINYON_CONFIG_PATH_ENV, value)
// }
}
26 changes: 14 additions & 12 deletions crates/fluvio-hub-util/src/keymgmt.rs
Expand Up @@ -81,14 +81,7 @@ impl Keypair {
if pem.tag != "PRIVATE KEY" {
return Err(HubUtilError::InvalidKeyPairFile(fname.into()));
}
let skey = ed25519_dalek::SecretKey::from_bytes(&pem.contents)
.map_err(|_| HubUtilError::InvalidPublicKeyFile(fname.into()))?;
let pkey: ed25519_dalek::PublicKey = (&skey).into();
let ekeypair = ed25519_dalek::Keypair {
secret: skey,
public: pkey,
};
Ok(Keypair { kp: ekeypair })
Keypair::from_secret_bytes(&pem.contents)
}

/// writes the private key from which the public is derivable on load
Expand All @@ -109,6 +102,11 @@ impl Keypair {
hex::encode(self.kp.secret.as_bytes())
}

pub fn from_hex(hexstring: &str) -> Result<Keypair> {
let pkbytes = hex::decode(hexstring).map_err(|_| HubUtilError::KeyVerify)?;
Keypair::from_secret_bytes(&pkbytes)
}

/// a pubkey from an ssh private key (id_ed25519) generated via
/// e.g. $ ssh-keygen -t ed25519 -C "sshkey@example.com" -f ./id_ed25519 -P ""
/// ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIACGeGHWvt/E60k/FLuDsCkArLAIa4lvwk1wg3nJIGJl sshkey@example.com
Expand All @@ -119,14 +117,18 @@ impl Keypair {
.key_data()
.ed25519()
.ok_or(HubUtilError::KeyVerify)?;
let skey = ed25519_dalek::SecretKey::from_bytes(keypair.private.as_ref())
.map_err(|_| HubUtilError::KeyVerify)?;
Keypair::from_secret_bytes(keypair.private.as_ref())
}

fn from_secret_bytes(sbytes: &[u8]) -> Result<Keypair> {
let skey =
ed25519_dalek::SecretKey::from_bytes(sbytes).map_err(|_| HubUtilError::KeyVerify)?;
let pkey: ed25519_dalek::PublicKey = (&skey).into();
let kp = ed25519_dalek::Keypair {
let ekeypair = ed25519_dalek::Keypair {
secret: skey,
public: pkey,
};
Ok(Keypair { kp })
Ok(Keypair { kp: ekeypair })
}

pub fn ref_dalek(&self) -> &ed25519_dalek::Keypair {
Expand Down
2 changes: 2 additions & 0 deletions crates/fluvio-hub-util/src/lib.rs
Expand Up @@ -3,6 +3,7 @@ mod hubaccess;
mod package;
mod packagemeta;
mod utils;
mod infinyon_tok;

use const_format::concatcp;

Expand All @@ -13,6 +14,7 @@ pub use package::*;
pub use packagemeta::*;
pub use errors::HubUtilError;
pub use utils::*;
pub use infinyon_tok::read_infinyon_token;

pub const HUB_PACKAGE_VERSION: &str = "0.1";
pub const HUB_PACKAGE_META: &str = "package-meta.yaml";
Expand Down
28 changes: 27 additions & 1 deletion crates/fluvio-hub-util/src/package.rs
Expand Up @@ -15,6 +15,7 @@ use crate::DEF_HUB_INIT_DIR;
use crate::HUB_MANIFEST_BLOB;
use crate::HUB_PACKAGE_META;
use crate::HUB_PACKAGE_META_CLEAN;
use crate::HubAccess;
use crate::HubUtilError;
use crate::PackageMeta;

Expand All @@ -26,7 +27,32 @@ type Result<T> = std::result::Result<T, HubUtilError>;
/// # Arguments
/// * pkgmeta: package-meta.yaml path
/// * outdir: optional output directory
pub fn package_assemble(pkgmeta: &str, outdir: Option<&str>) -> Result<String> {
pub fn package_assemble_and_sign(
pkgmeta: &str,
access: &HubAccess,
outdir: Option<&str>,
) -> Result<String> {
let tarname = package_assemble(pkgmeta, outdir)?;
let ipkgname = tar_to_ipkg(&tarname);
let keypair = access.keypair()?;
package_sign(&tarname, &keypair, &ipkgname)?;
Ok(ipkgname)
}

fn tar_to_ipkg(fname: &str) -> String {
let path = Path::new(fname);
path.with_extension("ipkg").display().to_string()
}

/// assemble files into an unsigned fluvio package, a file will be created named
/// packagename-A.B.C.tar
///
/// # Arguments
/// * pkgmeta: package-meta.yaml path
/// * outdir: optional output directory
///
/// # Returns: staging tarfilename
fn package_assemble(pkgmeta: &str, outdir: Option<&str>) -> Result<String> {
debug!(target: "package_assemble", "opening");
let pm = PackageMeta::read_from_file(pkgmeta)?;
let mut pm_clean = pm.clone();
Expand Down
3 changes: 2 additions & 1 deletion crates/smdk/Cargo.toml
Expand Up @@ -29,9 +29,10 @@ include_dir = "0.7.2"
tempdir = "0.3.7"

fluvio = { path = "../fluvio", default-features = false }
fluvio-hub-util = { path = "../fluvio-hub-util" }
fluvio-protocol = { path = "../fluvio-protocol", features=["record","api"] }
fluvio-future = { version = "0.4.0", features = ["subscriber"]}
fluvio-smartengine = { path = "../fluvio-smartengine" }
fluvio-extension-common = { path = "../fluvio-extension-common", features = ["target"] }
fluvio-smartmodule = { path = "../fluvio-smartmodule", default-features = false }
fluvio-controlplane-metadata = { path = "../fluvio-controlplane-metadata", features = ["smartmodule"] }
fluvio-controlplane-metadata = { path = "../fluvio-controlplane-metadata", features = ["smartmodule"] }
8 changes: 8 additions & 0 deletions crates/smdk/src/cmd.rs
Expand Up @@ -5,6 +5,8 @@ use crate::build::BuildOpt;
use crate::generate::GenerateOpt;
use crate::test::TestOpt;
use crate::load::LoadOpt;
use crate::publish::PublishOpt;
use crate::set_hubid::SetHubidOpt;

/// Manage and view Fluvio clusters
#[derive(Debug, Parser)]
Expand All @@ -15,6 +17,10 @@ pub enum SmdkCommand {
Generate(GenerateOpt),
Test(TestOpt),
Load(LoadOpt),
/// Publish SmartModule to Hub
Publish(PublishOpt),
/// Sethubid credentials
SetHubid(SetHubidOpt),
}

impl SmdkCommand {
Expand All @@ -24,6 +30,8 @@ impl SmdkCommand {
SmdkCommand::Generate(opt) => opt.process(),
SmdkCommand::Test(opt) => opt.process(),
SmdkCommand::Load(opt) => opt.process(),
SmdkCommand::Publish(opt) => opt.process(),
SmdkCommand::SetHubid(opt) => opt.process(),
}
}
}
2 changes: 2 additions & 0 deletions crates/smdk/src/main.rs
Expand Up @@ -3,6 +3,8 @@ mod cmd;
mod generate;
mod test;
mod load;
mod publish;
mod set_hubid;
mod wasm;

use clap::Parser;
Expand Down

0 comments on commit 737e330

Please sign in to comment.