Skip to content
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

Merged
merged 2 commits into from
Jun 18, 2021
Merged

feat: Rust templates #376

merged 2 commits into from
Jun 18, 2021

Conversation

jcrossley3
Copy link
Contributor

@jcrossley3 jcrossley3 commented Jun 7, 2021

Depends on boson-project/buildpacks#95

Open questions:

  1. I added a test-rust make target, but didn't add it to the existing test target yet. Does CI run that? Is cargo available there?
  2. I did not reference a specific tagged version of the builder image in .builders.yaml and I'm not sure who makes latest available. Is that also CI?
  3. Should I squash these to make a more cohesive commit message?

@lance
Copy link
Member

lance commented Jun 11, 2021

Open questions:

1. I added a `test-rust` make target, but didn't add it to the existing `test` target yet. Does CI run that? Is `cargo` available there?

We use GH Actions for CI, and yes you can add cargo https://github.com/actions-rs/cargo.

2. I did not reference a specific tagged version of the builder image in `.builders.yaml` and I'm not sure who makes `latest` available. Is that also CI?

Yes when we publish a new version CI pushes to quay.io

3. Should I squash these to make a more cohesive commit message?

Your choice. We can squash when landing too.

Copy link
Member

@lance lance left a 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?

Comment on lines 15 to 30
#[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
}
Copy link
Member

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.

https://github.com/boson-project/buildpacks/blob/5f204f9f69ef0c8b12ca2ef0d4298d0165f819e0/buildpacks/go/faas/main.go#L28-L39

Is it possible to change boson-project/buildpacks#95 to follow this same pattern?

Copy link
Contributor Author

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:

  1. 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.
  2. 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.
  3. 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?

Copy link
Member

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 and handler.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. :)

use env_logger as elog;
use log::info;

async fn index(req: HttpRequest) -> HttpResponse {
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so.

@jcrossley3
Copy link
Contributor Author

We use GH Actions for CI, and yes you can add cargo https://github.com/actions-rs/cargo.

Where do I add that, exactly?

@zroubalik
Copy link
Contributor

We use GH Actions for CI, and yes you can add cargo https://github.com/actions-rs/cargo.

Where do I add that, exactly?

Here you can find the GH Actions definitions, that are being used by CI and for PR validation.

@jcrossley3
Copy link
Contributor Author

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 test target and everything worked. Yay!

@jcrossley3
Copy link
Contributor Author

Ok, I squashed my commits and I think I'm generally happy with it as an initial strawman.

Copy link
Contributor

@zroubalik zroubalik left a 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 zroubalik changed the title Rust templates feat: Rust templates Jun 16, 2021
@jcrossley3
Copy link
Contributor Author

Do you think that you could add Rust Function developer guide?

@zroubalik Is there something in particular I missed in the README.md included in each template? I'm assuming the intended audience already knows rust well enough to have their environment set up and should already be pretty familiar with the cargo subcommands.

Is the guide still useful if it's mostly restating what's in the README's?

@lance
Copy link
Member

lance commented Jun 16, 2021

@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.

@jcrossley3
Copy link
Contributor Author

Do you think that you could add Rust Function developer guide?

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.
@lance lance merged commit 4711638 into knative:main Jun 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants