-
Notifications
You must be signed in to change notification settings - Fork 0
Comments from #49: Tenant Scoping #68
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
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
a110860
Make cache for entitiesWithoutPredictionsPerTenant tenant specific
lisajulia a8140a8
Merge branch 'main' into final-review-tenant-scoping
Schmarvinius 2231d61
refactor(ai-core): make AICoreService tenant-agnostic and DI-friendly
Schmarvinius de4f637
feat(ai-core): restrict AICore entity APIs to current tenant
Schmarvinius c30080b
chore(ai-core): rename config namespace to cds.ai.core
Schmarvinius 1eb8c33
fix(ai-core): handle null tenant in resourceGroupForTenant
Schmarvinius e730fa8
fix(ci): cleanup all run attempts and cds-itest resource groups
Schmarvinius 6ebc3fd
fix(itest): align config namespace with cds.ai.core rename
Schmarvinius 8c0197e
test(ai-core): add unit tests for tenant scoping and mock service
Schmarvinius 7512273
chore(recommendations): add TODO for model-changed integration test
Schmarvinius b105dde
update cleanup
Schmarvinius d539f21
fix(ci): scope resource group cleanup to own job only
Schmarvinius cdb3967
test(ai-core): add unit tests for uncovered code paths
Schmarvinius 6657df8
fix(ci): include cds-itest- prefix in resource group cleanup
Schmarvinius 211d808
refactor: extract inline cleanup script into shared JS file
Schmarvinius File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| /** | ||
| * Cleans up AI Core test resource groups. | ||
| * | ||
| * Required environment variables: | ||
| * RESOURCE_GROUP_PREFIX - The prefix identifying resource groups owned by this run | ||
| * (e.g. "itest-12345-1-j17" or "sonar-12345-1") | ||
| * | ||
| * Optional environment variables: | ||
| * STALE_PREFIXES - Comma-separated list of additional prefixes to clean up | ||
| * (defaults to "itest-rg-,cds-itest-") | ||
| * | ||
| * Credentials are resolved from VCAP_SERVICES (via cds bind) or AICORE_SERVICE_KEY. | ||
| */ | ||
| const https = require("https"); | ||
|
|
||
| const DEFAULT_STALE_PREFIXES = ["itest-rg-", "cds-itest-"]; | ||
|
|
||
| function getCredentials() { | ||
| const vcap = JSON.parse(process.env.VCAP_SERVICES || "{}"); | ||
| return ( | ||
| (vcap.aicore || vcap["ai-core"] || [{}])[0].credentials || | ||
| JSON.parse(process.env.AICORE_SERVICE_KEY || "null") | ||
| ); | ||
| } | ||
|
|
||
| function request(url, opts = {}) { | ||
| return new Promise((resolve, reject) => { | ||
| const u = new URL(url); | ||
| const req = https.request( | ||
| { | ||
| hostname: u.hostname, | ||
| path: u.pathname + u.search, | ||
| method: opts.method || "GET", | ||
| headers: opts.headers || {}, | ||
| }, | ||
| (res) => { | ||
| let data = ""; | ||
| res.on("data", (chunk) => (data += chunk)); | ||
| res.on("end", () => resolve({ status: res.statusCode, body: data })); | ||
| } | ||
| ); | ||
| req.on("error", reject); | ||
| if (opts.body) req.write(opts.body); | ||
| req.end(); | ||
| }); | ||
| } | ||
|
|
||
| async function getAccessToken(credentials) { | ||
| const tokenUrl = credentials.url + "/oauth/token"; | ||
| const params = new URLSearchParams({ grant_type: "client_credentials" }); | ||
| const authHeader = | ||
| "Basic " + | ||
| Buffer.from(credentials.clientid + ":" + credentials.clientsecret).toString( | ||
| "base64" | ||
| ); | ||
| const res = await request(tokenUrl + "?" + params.toString(), { | ||
| headers: { Authorization: authHeader }, | ||
| }); | ||
| return JSON.parse(res.body).access_token; | ||
| } | ||
|
|
||
| async function deleteResourceGroups(apiUrl, headers, prefixes) { | ||
| const res = await request(apiUrl + "/v2/admin/resourceGroups", { headers }); | ||
| const groups = JSON.parse(res.body).resources || []; | ||
| const toDelete = groups.filter( | ||
| (rg) => | ||
| rg.resourceGroupId && | ||
| prefixes.some((p) => rg.resourceGroupId.startsWith(p)) | ||
| ); | ||
|
|
||
| for (const rg of toDelete) { | ||
| const delRes = await request( | ||
| apiUrl + "/v2/admin/resourceGroups/" + rg.resourceGroupId, | ||
| { method: "DELETE", headers } | ||
| ); | ||
| console.log("Delete", rg.resourceGroupId, "->", delRes.status); | ||
| } | ||
|
|
||
| console.log("Cleaned up", toDelete.length, "resource groups"); | ||
| } | ||
|
|
||
| async function main() { | ||
| const ownPrefix = process.env.RESOURCE_GROUP_PREFIX; | ||
| if (!ownPrefix) { | ||
| console.error("RESOURCE_GROUP_PREFIX environment variable is required"); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const credentials = getCredentials(); | ||
| if (!credentials) { | ||
| console.log("No AI Core credentials found, skipping cleanup"); | ||
| return; | ||
| } | ||
|
|
||
| const stalePrefixes = process.env.STALE_PREFIXES | ||
| ? process.env.STALE_PREFIXES.split(",").map((s) => s.trim()) | ||
| : DEFAULT_STALE_PREFIXES; | ||
|
|
||
| const prefixes = [ownPrefix, ...stalePrefixes]; | ||
|
|
||
| const apiUrl = credentials.serviceurls.AI_API_URL; | ||
| const token = await getAccessToken(credentials); | ||
| const headers = { | ||
| Authorization: "Bearer " + token, | ||
| "AI-Resource-Group": "default", | ||
| }; | ||
|
|
||
| console.log("Cleaning resource groups matching prefixes:", prefixes); | ||
| await deleteResourceGroups(apiUrl, headers, prefixes); | ||
| } | ||
|
|
||
| main().catch((e) => { | ||
| console.error(e.message); | ||
| process.exit(0); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.