-
Notifications
You must be signed in to change notification settings - Fork 200
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(compiler): allow explicit lift qualifications of preflight objects #5935
Conversation
Correctly calculate the phase of array literals (allowing phase independent arrays).
Thanks for opening this pull request! 🎉
|
Console preview environment is available at https://wing-console-pr-5935.fly.dev 🚀 Last Updated (UTC) 2024-03-17 11:24 |
BenchmarksComparison to Baseline 🟥⬜🟥🟥⬜⬜⬜⬜⬜⬜🟩⬜🟩
⬜ Within 1.5 standard deviations Benchmarks may vary outside of normal expectations, especially when running in GitHub Actions CI. Results
Last Updated (UTC) 2024-03-17 11:30 |
Signed-off-by: monada-bot[bot] <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
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.
Very excited about this!
libs/wingc/src/jsify/snapshots/access_property_on_value_returned_from_collection.snap
Outdated
Show resolved
Hide resolved
libs/wingc/src/jsify/snapshots/base_class_captures_inflight.snap
Outdated
Show resolved
Hide resolved
libs/wingc/src/jsify/snapshots/lift_binary_preflight_expression.snap
Outdated
Show resolved
Hide resolved
Co-authored-by: Elad Ben-Israel <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
I'm playing around with the changes a bit on my local machine -- to be honest, I'm not sure I'm (personally) in love with the syntax. I feel like it's potentially confusing that lift is a function that you call within an inflight function body (the body being where you put code that runs on the cloud), but instead it's really being used to provide declarative information about the enclosing function. For example, take a look at this code: bring cloud;
let b1 = new cloud.Bucket() as "b1";
let b2 = new cloud.Bucket() as "b2";
let selectBucket = inflight (i: num) => {
if i % 2 == 0 {
return b1;
} else {
return b2;
}
};
new cloud.Function(inflight () => {
if false {
lift(b1, ["put"]); // <-- calling "lift" inside of some control flow
lift(b2, ["put"]);
}
let bucket = selectBucket(1);
bucket.put("hello.txt", "world");
}); I think this code snippet could be pretty confusing to someone learning Wing for the first time, since many a user would rightfully assume the code inside the Likewise, when I read this code I'd expect that since lift looks like a function call, I could just refactor them into a separate function: let addLifts = () => {
lift(b1, ["put"]);
lift(b2, ["put"]);
};
new cloud.Function(inflight () => {
addLifts();
let bucket = selectBucket(1);
bucket.put("hello.txt", "world");
}); Unfortunately this doesn't work (it's a compile error). Even if we change it to "lift!()", it would still looks like a function call is being made. I feel like the mechanism (providing some way to declare sets of preflight objects and operations that should be added to the internal "lift map") seems like right direction, but my preference would be if we could iterate on the syntax a bit more before merging this feature. I see @eladb has already approved this so I don't know if this is open to discussion, but I figured I'd shoot my shot sharing the feedback. |
I agree with you @Chriscbr that this solution could be potentially confusing for the reasons you mentioned. These reasons came up when we discussed this last week. There are many things we can do but I suspect most of them will require dedicated syntax to be designed. What I like about this proposal (and the reason I approved the PR) is that it's a small incremental step which allows us to experiment with this idea of explicit lifting without introducing new dedicated syntax just yet. At this stage of the project, I am not sure the investment required to design a dedicated syntax for lifting is the right thing to focus on (especially because it has the potential to suck us all into this wonderful conversation). I suggest is to start with this to unblock the use case, and continue the discussion for dedicated syntax asynchronously. I'd love to see some proposals and as usual I have some ideas ;-) |
@Chriscbr I see what you mean and you're right.
Is wrong. You're just missing an But more generally I think there are three different approaches here:
You explained nicely the disadvantage of this approach and it's also somewhat limiting compared to the library option. I'm totally open to discussion here. |
One small tweak we can do (we discussed this as well) is to require that lift() will be used as a statement (i.e return type void) and that all lifts will be placed at the top of the closure definition. |
Signed-off-by: monada-bot[bot] <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
Signed-off-by: monada-bot[bot] <[email protected]>
Here's where I stand after a lengthy talk with @Chriscbr, either add this ☝️ and merge this PR or take a look at this new rfc: #5951 and continue from there (or both?). |
The RFC will take time to stabilize. I vote for merging this pull request (with the requirements that all lifts are at the top). This is a stable incremental step forward, it will unblock this use case and will allow us to continue the syntax discussion over the RFC without blocking. |
@Chriscbr @MarkMcCulloh this is basically ready, please read #5935 (comment) and if you don't object, I'll merge this. |
Thanks for contributing, @yoav-steinberg! This PR will now be added to the merge queue, or immediately merged if |
Congrats! 🚀 This was released in Wing 0.61.17. |
Fixes #6002 To support the needs of cloud applications with many resources, Wing's compiler performs static analysis to identify all of the infrastructure resources that are used by different bits of runtime code. (In Wing parlance these are usually called preflight objects, and inflight functions, respectively). By identifying these relationships, the compiler can automatically generate applications whose resources have least-privileged access. For example, database read/write permissions only need to be added to the compute resources that perform queries on it. This analysis is sufficient for many programs. But in general, obtaining the minimal set of resources needed for arbitrary inflight code is, in the worst case, as hard as the [halting problem](https://en.wikipedia.org/wiki/Halting_problem). To illustrate, the inflight code below only needs access to the preflight object `buckets[0]`, but it's impossible for any reasonable compiler to deduce this. ```js bring cloud; let buckets = [ new cloud.Bucket() as "b1", new cloud.Bucket() as "b2" ]; new cloud.Function(inflight () => { let var i = 10; while i > 0 { i -= 1; } buckets.at(i).put("key", "value"); }); ``` Even if Wing's analyzer did provide a [covering set](https://en.wikipedia.org/wiki/Set_cover_problem) of the resources and permissions needed by inflight code (in other words, a set of permissions that might be broader than necessary), there are cases when users will want or need to scope down permissions. #5935 addresses some of these needs by letting the user annotate functions with additional lifting information: ```js new cloud.Function(inflight () => { lift(buckets.at(1), ["put"]); let var i = 10; while i > 0 { i -= 1; } buckets.at(i).put("key", "value"); }); ``` This was an important change to unblock many use cases. Unfortunately, a side effect is that we've lost the guarantee that every program that compiles and works in the Wing simulator will also work in the cloud. That's because the simulator doesn't simulate permissions -- so even if bad `lift()` annotations are provided by the user, a program will still work in the simulator. Ouch! As a rule of thumb, we want to maximize the extent to which Wing programs working on the simulator result in the same programs working on the cloud. Even though we can't catch these errors at compile time, they can be detected at runtime. To that end, this PR implements a basic permissions model within the Wing simulator. Whenever a resource tries calling a method on another resource, the API call will first be checked against a list of permissions (generated during preflight) to see if the request should go through. If not, an error will be raised to the caller. The end result is that as long as you're able to test the code path with a unit test, it should still work locally. Here's an example of the error that will be surfaced to the user during `wing test` or in the Wing Console: ``` Error: Resource "root/Default/Function" does not have permission to perform operation "put" on resource "root/Default/b1". --> bucket.main.w:14:3 | while i > 0 { | i -= 1; | } 14 | buckets.at(i).put("key", "value"); | ^ at /Users/chrisr/dev/wing-test/bucket.main.w:14:3 ``` ----- ### Implementation notes **The good:** The main implementation is built around a new resource named `sim.Policy`. It stores a "principal" (a resource the policy applies to) and a list of statements (a list of resources and method names). It's probably not the only way to model this, but it felt like a decent starting point given that permissions are also represented with resources within CloudFormation and Terraform on most major cloud providers. Policy checking is also straightforward. All requests between resources currently go through the simulator's internal HTTP server. We've added an additional piece of information that identifies who is the resource that's performing the method call, which is sent to the server. **The mid:** How to store the information from `sim.Policy` in the simulator is an open question. This PR models `sim.Policy` like a resource (why not?). Except, nobody communicates with the policy directly. Instead, when the simulator is initializing or shutting down resources, if it sees a resource is a policy, it saves the resolved policy information into a policy registry. When we're checking if an API call should be allowed or not, it performs a lookup in the policy registry. But does that mean `sim.Policy` has no effect on other resources? Not quite. First, an aside... > Within the simulator, a resource can be identified in two ways. The first is with their construct path (a string like `root/MyApp/Storage/Bucket`), and the second is with a simulation-unique ID aka "handle" (a string like `sim-42`), which serves as an analog to physical resource names on cloud providers. > To make this concrete, imagine you have a Wing app with a `sim.Container`, and you modify the container's image. When the simulator is updating to the new version of your app, the construct path for the resource will stay the same, but the resource itself needs to be destroyed and recreated, which results in a fresh simulation ID. > Today this difference between these IDs isn't strictly necessary - it's possible we could use construct paths everywhere in the simulator. But using these IDs helps us detect dependency relationships in the resource graph, and if we wanted to model other kinds of application updates in the simulator (for example, Terraform's [`create_before_destroy` property](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#create_before_destroy)), then this distinction suddenly becomes very important. So for now I'm trying to keep it. Each `sim.Policy` refers to resources via their simulation handles, as we do elsewhere in the simulator. But this means a `sim.Policy` resource can't be created until all of the resources it refers to have been created. This introduces some interesting edge cases (for example, how does a `cloud.Service` call other resources during start-up when its `sim.Policy` can't be created until `cloud.Service` has finished starting up??) I managed to solve most of these cases with auxiliary resources that run after the initial resource's creation. **The bad:** In order for resources to make requests to the simulator server with their own handles, they need to know their own handles. Unfortunately, to expose this information in `ISimulatorContext`, I needed to refactor inflight simulation classes so that simulator context is passed to `init()` instead of to the JavaScript constructor. This results in some uglier code, but I don't think the distinction between constructors and `init()` will be relevant once more sim resources are implemented in Wing. ## Checklist - [ ] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [ ] Description explains motivation and solution - [ ] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
## Checklist [rendered version](https://github.com/winglang/wing/blob/yoav/rfc-explicit_lift_qualification/docs/contributing/999-rfcs/2024-03-14-explicit-lift-qualification.md) Related to #76, #5935 - [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [x] Description explains motivation and solution - [ ] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
Fixes: #76
Creates a
lift
builtin function that can be used in inflight code to explicitly add lift qualifications to a method:Checklist
pr/e2e-full
label if this feature requires end-to-end testingBy submitting this pull request, I confirm that my contribution is made under the terms of the Wing Cloud Contribution License.