Repository: cargo-lambda/cargo-lambda (language: python)

Issue:
Are environment variables avaiable when running on other port?
I have something else bound to 9000, so I run with:

    cargo lambda watch -p 9090

I have environment variables set in Cargo.toml like:

```toml
[package.metadata.lambda.env]
SOME_VAR = "value"
```

But when I invoke with:

    cargo lambda invoke -p 9090 --data-file sample-event.json

I get `{"errorType":"&alloc::boxed::Box<dyn std::error::Error+core::marker::Sync+core::marker::Send>","errorMessage":"environment variable not found"}`.

This is with version `cargo-lambda 0.10.1 (9564167 2022-08-11Z)`. Am I missing something?


Discussion / hints:
the port should not matter. I would print the full environment inside the function to verify if the variable is there, if it's not there, it's probably a bug caused by some recent changes.
Interesting. From:

```rust
    for (key, value) in env::vars() {
        info!("{key}: {value}");
    }
```

I get these (minus some unrelated vars):

```
AWS_LAMBDA_FUNCTION_MEMORY_SIZE: 4096
AWS_LAMBDA_FUNCTION_NAME: @package-bootstrap@
AWS_LAMBDA_FUNCTION_VERSION: 1
AWS_LAMBDA_RUNTIME_API: http://127.0.0.1:9090/.rt/@package-bootstrap@
CARGO: /Users/h4s/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/cargo
CARGO_HOME: /Users/h4s/.cargo
CARGO_MANIFEST_DIR: /Users/h4s/projects/github.com/theherk/test-proj
CARGO_PKG_AUTHORS:
CARGO_PKG_DESCRIPTION:
CARGO_PKG_HOMEPAGE:
CARGO_PKG_LICENSE:
CARGO_PKG_LICENSE_FILE:
CARGO_PKG_NAME: test-proj
CARGO_PKG_REPOSITORY:
CARGO_PKG_RUST_VERSION:
CARGO_PKG_VERSION: 0.1.0
CARGO_PKG_VERSION_MAJOR: 0
CARGO_PKG_VERSION_MINOR: 1
CARGO_PKG_VERSION_PATCH: 0
CARGO_PKG_VERSION_PRE:
RUSTUP_HOME: /Users/h4s/.rustup
RUSTUP_TOOLCHAIN: nightly-x86_64-apple-darwin
RUST_LOG:
RUST_RECURSION_COUNT: 3
_X_AMZN_TRACE_ID: Root=1-62fbc777-ba92ce598e42edbcd283a82f;Parent=2ca73e35bff1e8c1;Sampled=1
```

So, it is reading the environment, but those from this Cargo.toml:

```toml
[package]
name = "test-proj"
version = "0.1.0"
edition = "2021"

[package.metadata.lambda.env]
SOME_VAR = "value"
```

I should also see:

```
SOME_VAR: value
```
I found the issue. I will fix it in the next release, but there is also a workaround if you don't want to wait.

If you add the name of your binary as the argument to the `invoke` command, the variables should load correctly.

For what I see in your details, something like this:

```
cargo lambda invoke -p 9090 --data-file sample-event.json **test-proj**
```

_asterisks added for emphasis_

Fix patch:
diff --git a/crates/cargo-lambda-metadata/src/cargo/mod.rs b/crates/cargo-lambda-metadata/src/cargo/mod.rs
index cda9d4ac..e8fa2679 100644
--- a/crates/cargo-lambda-metadata/src/cargo/mod.rs
+++ b/crates/cargo-lambda-metadata/src/cargo/mod.rs
@@ -58,7 +58,7 @@ fn load_metadata<P: AsRef<Path>>(manifest_path: P) -> Result<CargoMetadata> {
 /// https://github.com/cargo-lambda/cargo-lambda#start---environment-variables
 pub fn function_metadata<P: AsRef<Path>>(
     manifest_path: P,
-    name: &str,
+    name: Option<&str>,
 ) -> Result<HashMap<String, String>> {
     let metadata = load_metadata(manifest_path)?;
     let mut env = HashMap::new();
@@ -66,16 +66,27 @@ pub fn function_metadata<P: AsRef<Path>>(
         serde_json::from_value(metadata.workspace_metadata).unwrap_or_default();
     env.extend(ws_metadata.env);
 
-    if let Some(res) = ws_metadata.bin.get(name) {
-        env.extend(res.env.clone());
+    if let Some(name) = name {
+        if let Some(res) = ws_metadata.bin.get(name) {
+            env.extend(res.env.clone());
+        }
     }
 
     for pkg in &metadata.packages {
+        let name = name.unwrap_or(&pkg.name);
+
         for target in &pkg.targets {
-            if target.name == name
+            let target_matches = target.name == name
                 && target.kind.iter().any(|kind| kind == "bin")
-                && pkg.metadata.is_object()
-            {
+                && pkg.metadata.is_object();
+
+            tracing::debug!(
+                name = name,
+                target_matches = target_matches,
+                "searching package metadata"
+            );
+
+            if target_matches {
                 let package_metadata: Metadata = serde_json::from_value(pkg.metadata.clone())
                     .into_diagnostic()
                     .wrap_err("invalid lambda metadata in Cargo.toml file")?;
@@ -87,10 +98,8 @@ pub fn function_metadata<P: AsRef<Path>>(
             }
         }
     }
-    if !env.is_empty() {
-        tracing::debug!("using {} environment variables from metadata", env.len());
-        tracing::debug!("{env:#?}");
-    }
+
+    tracing::debug!(env = ?env, "using environment variables from metadata");
     Ok(env)
 }
 
@@ -167,29 +176,38 @@ mod tests {
 
     #[test]
     fn test_metadata_packages() {
-        let env = function_metadata(fixture("single-binary-package"), "basic-lambda").unwrap();
+        let env =
+            function_metadata(fixture("single-binary-package"), Some("basic-lambda")).unwrap();
 
         assert_eq!(env.get("FOO").unwrap(), "BAR");
     }
 
     #[test]
     fn test_metadata_multi_packages() {
-        let env = function_metadata(fixture("multi-binary-package"), "get-product").unwrap();
+        let env = function_metadata(fixture("multi-binary-package"), Some("get-product")).unwrap();
 
         assert_eq!(env.get("FOO").unwrap(), "BAR");
 
-        let env = function_metadata(fixture("multi-binary-package"), "delete-product").unwrap();
+        let env =
+            function_metadata(fixture("multi-binary-package"), Some("delete-product")).unwrap();
 
         assert_eq!(env.get("BAZ").unwrap(), "QUX");
     }
 
     #[test]
     fn test_metadata_workspace_packages() {
-        let env = function_metadata(fixture("workspace-package"), "basic-lambda-1").unwrap();
+        let env = function_metadata(fixture("workspace-package"), Some("basic-lambda-1")).unwrap();
 
         assert_eq!(env.get("FOO").unwrap(), "BAR");
 
-        let env = function_metadata(fixture("workspace-package"), "basic-lambda-2").unwrap();
+        let env = function_metadata(fixture("workspace-package"), Some("basic-lambda-2")).unwrap();
+
+        assert_eq!(env.get("FOO").unwrap(), "BAR");
+    }
+
+    #[test]
+    fn test_metadata_packages_without_name() {
+        let env = function_metadata(fixture("single-binary-package"), None).unwrap();
 
         assert_eq!(env.get("FOO").unwrap(), "BAR");
     }
diff --git a/crates/cargo-lambda-watch/src/scheduler.rs b/crates/cargo-lambda-watch/src/scheduler.rs
index 01f9cc9e..7800140b 100644
--- a/crates/cargo-lambda-watch/src/scheduler.rs
+++ b/crates/cargo-lambda-watch/src/scheduler.rs
@@ -185,15 +185,7 @@ async fn start_function(
     no_reload: bool,
     gc_tx: Sender<String>,
 ) -> Result<(), ServerError> {
-    info!(function = ?name, "starting lambda function");
-
-    let env = match function_metadata(manifest_path, &name) {
-        Err(e) => {
-            warn!(error = %e, "ignoring invalid function metadata");
-            HashMap::new()
-        }
-        Ok(m) => m,
-    };
+    info!(function = ?name, manifest = ?manifest_path, "starting lambda function");
 
     let mut cmd = new_command("cargo");
 
@@ -208,11 +200,22 @@ async fn start_function(
         cmd.args(["--features", features]);
     }
 
-    if name != DEFAULT_PACKAGE_FUNCTION {
-        cmd.args(["--bin", &name]);
-    }
+    let bin_name = if is_valid_bin_name(&name) {
+        let str_name = name.as_str();
+        cmd.args(["--bin", str_name]);
+        Some(str_name)
+    } else {
+        None
+    };
+
+    let env = function_metadata(manifest_path, bin_name)
+        .map_err(|err| {
+            warn!(error = %err, "ignoring invalid function metadata");
+            err
+        })
+        .unwrap_or_default();
 
-    tracing::debug!(command = ?cmd, "spawning watch command");
+    tracing::debug!(env = ?env, command = ?cmd, "spawning watch command");
 
     let mut child = cmd
         .env("RUST_LOG", std::env::var("RUST_LOG").unwrap_or_default())
@@ -240,3 +243,7 @@ async fn start_function(
 
     Ok(())
 }
+
+fn is_valid_bin_name(name: &str) -> bool {
+    !name.is_empty() && name != DEFAULT_PACKAGE_FUNCTION
+}


Test patch:
diff --git a/test/fixtures/single-binary-package/src/main.rs b/test/fixtures/single-binary-package/src/main.rs
index 82532746..074f05b3 100644
--- a/test/fixtures/single-binary-package/src/main.rs
+++ b/test/fixtures/single-binary-package/src/main.rs
@@ -31,7 +31,11 @@ async fn function_handler(event: LambdaEvent<Request>) -> Result<Response, Error
     // Prepare the response
     let resp = Response {
         req_id: event.context.request_id,
-        msg: format!("Command {}.", command),
+        msg: format!(
+            "Command {}. Env FOO={}",
+            command,
+            std::env::var("FOO").unwrap()
+        ),
     };
 
     // Return `Response` (it will be serialized to JSON automatically by the runtime)