-
Notifications
You must be signed in to change notification settings - Fork 97
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
Extensibility: Decoupling Portable Resources RP from the Core #6325
Comments
/cc @youngbupark @kachawla - looking for feedback on this, or other ideas if you have them 👍 |
For efficiency, can we have an internal cache to hold the result of a |
Yes, caching would be possible if we support etags, however until we measure it's not clear how much benefit there would be. With an ETag based caching system you still have to make a request to the downstream service, so you're actually offloading the downstream not the upstream. We still have to plan for the worst case behavior of cache misses. |
It is better to add ARM or UCP component in sequence diagram. Ideally, any control plane operations must go through the ARM/UCP. Can we include connection metadata as part of the response of |
In ARM, service level role definition defines such permissions to access each operation via GET /{Namespace}/operations. Each RP must have its own 1P AAD App. When user registers RP, ARM creates 1P service principal for user subscription and assign the service role definition to the 1P service principal. The service role definition defines the several permissions such as listConnectionMetadata and listCredentials. I think we need to think about the similar role based access control for UCP in the long term. |
I'd be concerned about secrets, generally in ARM the list* APIs are used for obtaining secrets as they aren't part of the output. https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource#list |
As far as I know, listSecret/listCredentials should be remained as the separate API in the typical RP. I would not expect that |
Kk, I didn't realize that a call to get secrets of connections would require two calls (listConnectionMetadata -> listSecrets). If we keep |
This is a good idea. I'll put together an example and we can decide based on that. |
Overall problem and proposal makes sense to me except that we need a new custom operation ( |
This was what @youngbupark suggested also. Since we're all asking the same thing I'll put together an example so we have something realistic to think about. |
Coming back to this to try and get a consensus. It hearts my ❤️ to see our RPs so entangled with each-other 💔. I've modified the Mongo example to match something that would work for this. Option 1This option adds a The CoreRP would look up {
"id": "/.../providers/Applications.Connector/mongoDatabases/mongo0",
"name": "mongo0",
"type": "Applications.Connector/mongoDatabases",
"properties": {
"provisioningState": "Succeeded",
"application": "/.../providers/Applications.Core/applications/testApplication",
"environment": "/.../providers/Applications.Core/environments/env0",
"resource": "/.../providers/Microsoft.DocumentDB/databaseAccounts/testAccount/mongodbDatabases/db",
"database": "mongo0",
"status": {
// Output resources here
},
"connectorData": {
"values": {
"database": "mongo0"
}
}
}
} Why is database = mongo0 present in BOTH I assume this is the question you're all asking. The answer is that for connectors to be extensible, the core RP can know nothing about the schema of Why We'll definitely keep adding functionality to connectors. We need the ability to add properties for other purposes without breaking the API. Option 2This option adds a The CoreRP would look up {
"id": "/.../providers/Applications.Connector/mongoDatabases/mongo0",
"name": "mongo0",
"type": "Applications.Connector/mongoDatabases",
"properties": {
"provisioningState": "Succeeded",
"application": "/.../providers/Applications.Core/applications/testApplication",
"environment": "/.../providers/Applications.Core/environments/env0",
"resource": "/.../providers/Microsoft.DocumentDB/databaseAccounts/testAccount/mongodbDatabases/db",
"database": "mongo0",
"status": {
// Output resources here
},
"connectorMetadata": {
"valueProperties": [
"/properties/database"
]
}
}
} What's up with this wierd JSONPointer thing? I assume this is the question you're all asking. The answer is that for connectors to be extensible, the core RP can know nothing about the schema of This is a different technique for providing the keys that doesn't require duplicating data. |
How about a modification of option 1 that solves the problem of duplicated values by moving values to its own section in the schema -
All the generated values are associated to underlying resource instead to the connector directly, so we can define all the values that are populated during deployment in a separate section in the schema This will require updating schema of all connectors, but this is a one time change that we can afford to make at this point and will allow us to add more values while maintaining backwards compatibility in the future. |
The reason why I didn't choose this is that it obfuscates the experience for using our resources in Bicep. This trades-off the primary concern (what's the best user experience), for a secondary concern (how extensibility is implemented). It also can't be as simple as just Today looking up the database name is I don't think that the duplication is a blocker, but it needs to be justified 😆 |
I see. I think it is personal preference, I'd choose |
done |
done and duplicate |
Per discussion, the duplicate was in an internal tracking system not in Github. This isn't done :) |
👍 We've reviewed this issue and have agreed to add it to our backlog. Please subscribe to this issue for notifications, we'll provide updates when we pick it up. We also welcome community contributions! If you would like to pick this item up sooner and submit a pull request, please visit our contribution guidelines and assign this to yourself by commenting "/assign" on this issue. For more information on our triage process please visit our triage overview |
Summary
This issue discussed a proposal for decoupling the Portable Resources RP from core RP, and for how we implement extensibility for connectors. It's an explicit goal of this proposal that we define the extensibility mechanics for a connector, and then use this model for our implementation. This will also resolve some really bad coupling that we have in place right now between the two Radius RPs.
Status: This is a first draft, I'm looking for feedback and alternatives for us to debate.
Goals
containers
does) to interact with the connector contract separately from Radius project (3rd party extensibility to interact with connectors).Current State
The core RP has copied code from the connector RP so that it can read the internal data model of the connector RP. When a container is deployed that has connections, the core RP has to read that connection metadata from the connector RP's database using it's internal code. When a new connector type is added, new code has to be added to both core RP and connector RP - this needs to be resolved for 3rd party extensibility to function.
This is not intentional coupling, and was introduced when we split up the CustomRP implementation into two RPs. We didn't have time to complete the refactor, this post is describing solutions.
Requirements from a compute resource
When deploying a resource, a compute RP needs to interact with the resources named by 'connections' to determine the following:
Proposal
Connectors and routes should expose metadata needed for the connector contract as an ARM custom-action. That would look something like this.
The Core RP or (any other compute resource) is responsible for resolving the resource IDs of its connections, and calling into the connector RPs (via UCP proxy) to get the data needed for deployment.
For example the Core RP container support today wants to support:
The knowledge of which environment variables and role assignments is moved out of the CoreRP and into an extensible microservice contract.
Here's an example of what the data-flow would look like for a simple webapp + database scenario:
Alternatives
Can we include the data we need as part of the connector resource and skip the custom action? This would prevent us from needing another separate contract, and would be part of the standard RPC.
Example:
FAQ / Open-Issues
Q: How would this pattern work for an Azure service? RP -> RP contracts and authentication are somewhat common in ARM. Since connectors are a concept in Radius, it makes sense for them to have a common set of capabilities.
Q: What can we do to make this scale better? Right now this makes 1 request per connection, which will not scale well if containers end up being highly connected. For example some containers in eShop have ~6 connections. If we need to make this scale better we can introduce batch operations that accept more than one resource for the same RP. This is already a common pattern in ARM - several of the contracts support a batch variant that reduces the number of requests.
Q: What else can we do to improve efficiency? Adding etag support to the to the connector contract could improve our overall efficiency for no-ops and allow us to support synchronous no-ops for containers. Right now we assume that any PUT to a container (or other resource) should trigger a deployment because we don't have a way to reason about whether connector data has changed. We should consider this part of the design.
Q: What EXACTLY is the schema for
listConnectionMetadata
? This is a first draft looking for input on the general idea. We'll continue to refine the details if we think this is the right overall idea.Q: Do we need to permission secret and non-secret access to connection metadata separately? I'm not sure 😆 - looking for input on this. For now I'm assuming we want to assume a high level of trust for something accessing a connection's metadata.
Q: Do we need to permission secret and non-secret access to connection metadata separately? How do we know which types implement the connector protocol and which don't? For now we can use some heuristics, like whether a resource is a Radius type or not. Ultimately we need to implement a discovery API - a way to list the types - we already want this for the CLI so we can be loosely coupled. The discovery API and the registration for a connector type should include this information.
For now it's also fine to "ask for permission". The caller can invoke the custom action and handle the 404 if it's not implemented.
Q: How would we migrate towards this? First we'd define the contract, and start implementing inside connector RP. In parallel we can start consuming the new contract inside core RP with a fallback to the current logic.
AB#10775
The text was updated successfully, but these errors were encountered: