-
Notifications
You must be signed in to change notification settings - Fork 138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Rust templates #376
feat: Rust templates #376
Conversation
f4b0d5a
to
44ec063
Compare
We use GH Actions for CI, and yes you can add cargo https://github.com/actions-rs/cargo.
Yes when we publish a new version CI pushes to quay.io
Your choice. We can squash when landing too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this contribution @jcrossley3! One of the things we try to do is hide all of the plumbing from the function developer so that they are presented with only a function in their source file. Would it be possible to modify this template/buildpack combo so that it looks a little more like the Go template/buildpack?
templates/rust/http/src/main.rs
Outdated
#[actix_web::main] | ||
async fn main() -> std::io::Result<()> { | ||
elog::from_env(elog::Env::default().default_filter_or("info")).init(); | ||
|
||
let port: u16 = match std::env::var("PORT") { | ||
Ok(v) => v.parse().unwrap(), | ||
Err(_) => 8080, | ||
}; | ||
|
||
// Create the HTTP server | ||
HttpServer::new(|| { | ||
App::new() | ||
.wrap(actix_web::middleware::Logger::default()) | ||
.route("/", web::get().to(index)) | ||
.route("/", web::post().to(index)) | ||
.route( | ||
"/health/{_:(readiness|liveness)}", | ||
web::get().to(HttpResponse::Ok), | ||
) | ||
}) | ||
.bind(("127.0.0.1", port))? | ||
.workers(1) | ||
.run() | ||
.await | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather that this code live in the buildpack similar to how it's done in go.
Is it possible to change boson-project/buildpacks#95 to follow this same pattern?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was actually my initial approach. Ultimately, I decided against it, mostly for three reasons:
- There are so many different ways that
App
can be configured depending on the handler function signature that I couldn't justify hiding that from the user. The template is just scratching the surface of what actix provides. - The handler would be referring to actix types, so there will always be a potential version mismatch: both the runtime and the user's handler will have to depend on compatible versions of actix. I couldn't justify introducing that obstacle.
- Library dependencies in rust are expressed as "crates" so I would either have to publish a crate containing these few lines of configuration code that a user will likely want to change anyway... or work up some hackery in the buildpack to make cargo think it's building a runtime crate that depends on the user's app for its handler.
In light of the latter, and the prior art of the java-based buildpacks expecting a fully-formed app from the user, I thought initially making the buildpack capable of compiling any rust app might serve the rust developer better. Heck, there are lots of alternatives to actix out there. You know as well as I do how many web frameworks we can swing a dead cat and hit.
I'm just guessing. I like magic, but only if I'm sure I'll never want to change it, and at this point, I'm not sure what I may want to change. So I figure we take a conservative approach initially and see if/how folks might use it. We can always introduce a new template later that hides the server config (App
) once doing that seems obvious in hindsight.
One thing I could do is split the code into two files: main.rs
and handler.rs
. And we'll move the handler and its tests into the latter. Would that make things more clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just guessing. I like magic, but only if I'm sure I'll never want to change it, and at this point, I'm not sure what I may want to change. So I figure we take a conservative approach initially and see if/how folks might use it. We can always introduce a new template later that hides the server config (
App
) once doing that seems obvious in hindsight.
That seems reasonable, I suppose.
One thing I could do is split the code into two files:
main.rs
andhandler.rs
. And we'll move the handler and its tests into the latter. Would that make things more clear?
Yes, I like this change. Thanks. :)
templates/rust/http/src/main.rs
Outdated
use env_logger as elog; | ||
use log::info; | ||
|
||
async fn index(req: HttpRequest) -> HttpResponse { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why this function is async. I apologize for my Rust ignorance. How is this async and how does that work in an HTTP request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm no expert, but rust async is inspired by erlang async. In rust, you have to bring your own runtime to schedule the lightweight processes defined by that async keyword. Actix uses the tokio runtime, which ultimately gets created by that "macro attribute" on line 15: #[actix_web::main]
.
I'm not familiar with the actix-web internals but I expect at some point, something is invoking await
on the Future
returned by the index
function. I know it says it's returning an HttpResponse
, but because that async
keyword is there, it's really returning a Future
.
Were you looking for more detail than that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks that does help. I just want to make sure we don't end up with a long running async function that gets scaled to 0 before work is done. But I guess ultimately the HTTP response is not sent until the function completes. Is that a valid reading?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so.
Where do I add that, exactly? |
Here you can find the GH Actions definitions, that are being used by CI and for PR validation. |
Thanks. I saw that, but couldn't figure out where the other langs were being configured and then thought, "maybe rust is available, by default?" So I just updated the |
Ok, I squashed my commits and I think I'm generally happy with it as an initial strawman. |
4caa8c1
to
b5017a5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, my Rust experience is limited though... :)
Do you think that you could add Rust Function developer guide? Something similar that has been done for other runtimes: https://github.com/boson-project/func/blob/main/docs/guides/developers_guide.md#language-guides
Thanks!
@zroubalik Is there something in particular I missed in the Is the guide still useful if it's mostly restating what's in the README's? |
@jcrossley3 it's a lot of what's in the readme, but it's also other stuff depending on the runtime/framework. Currently we try to have a language guide for each set of templates. You are probably right that this ultimately probably lands in the template itself - especially as these things start to move upstream. |
Done. |
Each template is a fully-formed actix-web application that includes a main.rs providing the server configuration and a handler.rs showing an example function and a few simple unit tests. A README.md provides a bit more detail to get the user started. The events handler is similar to the example in the old faas-rust-runtime project.
Depends on boson-project/buildpacks#95
Open questions:
test-rust
make target, but didn't add it to the existingtest
target yet. Does CI run that? Iscargo
available there?.builders.yaml
and I'm not sure who makeslatest
available. Is that also CI?