Skip to content

Commit d0e632f

Browse files
authored
Merge branch 'main' into patch-3
2 parents e7c4757 + afce320 commit d0e632f

18 files changed

+1025
-39
lines changed

orgs/branchprotection.yml

Lines changed: 582 additions & 0 deletions
Large diffs are not rendered by default.

orgs/contributors.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ orgs:
1313
- anshrupani
1414
- anthonydahanne
1515
- ANUGRAHG
16+
- anujc25
1617
- app-autoscaler-ci-bot
1718
- aqstack
1819
- aramprice
@@ -52,14 +53,12 @@ orgs:
5253
- danail-branekov
5354
- davewalter
5455
- dennisjbell
56+
- dilipmighty245
5557
- dlresende
5658
- dmikusa
57-
- domdom82
5859
- Dray56
59-
- dsabeti
6060
- duanemay
6161
- ebroberson
62-
- emalm
6362
- emmjohnson
6463
- evanfarrar
6564
- FelisiaM
@@ -93,7 +92,6 @@ orgs:
9392
- joemahady-comm
9493
- joergdw
9594
- johha
96-
- jrussett
9795
- julian-hj
9896
- kathap
9997
- KauzClay
@@ -214,4 +212,6 @@ orgs:
214212
- Milena-Encheva
215213
- AttilaAlmasi
216214
- dtasSap
215+
- ireneGonzalezRuiz
217216
- praveenkalluri18
217+
- Jobsby

orgs/org_management.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,17 @@ def generate_teams(self):
192192

193193
def generate_branch_protection(self):
194194
# basis is static config in self.branch_protection which is never overwritten
195-
# generate RFC0015 branch protection rules for every WG+TOC that opted in
195+
# generate RFC0015 branch protection rules for every WG+TOC by default
196196
for org in OrgGenerator._MANAGED_ORGS:
197197
branch_protection_repos = self.branch_protection["branch-protection"]["orgs"][org]["repos"]
198198
wgs = self.working_groups[org]
199199
if org == self.toc["org"]:
200200
wgs.append(self.toc)
201201
for wg in wgs:
202-
if wg.get("config", {}).get("generate_rfc0015_branch_protection_rules", False): # config is optional
203-
repo_rules = self._generate_wg_branch_protection(wg)
204-
for repo in repo_rules:
205-
if repo not in branch_protection_repos:
206-
branch_protection_repos[repo] = repo_rules[repo]
202+
repo_rules = self._generate_wg_branch_protection(wg)
203+
for repo in repo_rules:
204+
if repo not in branch_protection_repos:
205+
branch_protection_repos[repo] = repo_rules[repo]
207206

208207
def write_org_config(self, path: str):
209208
print(f"Writing org configuration to {path}")

orgs/test_org_management.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,6 @@
164164
bots:
165165
- github: bot-wg1-a5
166166
name: WG3 Area5 Bot
167-
config:
168-
generate_rfc0015_branch_protection_rules: true
169167
"""
170168

171169
wg4_other_org = """
@@ -206,8 +204,6 @@
206204
- cloudfoundry2/repo3
207205
- cloudfoundry2/repo4
208206
- cloudfoundry/repo5
209-
config:
210-
generate_rfc0015_branch_protection_rules: true
211207
"""
212208

213209
toc = """
@@ -231,7 +227,6 @@
231227
repositories:
232228
- cloudfoundry/community
233229
config:
234-
generate_rfc0015_branch_protection_rules: true
235230
github_project_sync:
236231
mapping:
237232
cloudfoundry: 31
@@ -732,8 +727,8 @@ def test_generate_branch_protection(self):
732727
bp_repos = o.branch_protection["branch-protection"]["orgs"]["cloudfoundry"]["repos"]
733728
# TOC and wg3 opted in, wg1 and wg2 not
734729
# note: repo1..4 are shared between wg1 (opt out) and wg3 (opt in) - wg3 wins
735-
self.assertSetEqual({f"repo{i}" for i in range(1, 6)} | {"community"}, set(bp_repos.keys()))
736-
# repo1 has static config that wins over generated branch protection rules
730+
self.assertSetEqual({f"repo{i}" for i in list(range(1, 6)) + [10, 11]} | {"community"}, set(bp_repos.keys()))
731+
# repo1 has static config that wins over generated branch protection rulesp
737732
self.assertTrue(bp_repos["repo1"]["protect"])
738733
self.assertNotIn("required_pull_request_reviews", bp_repos["repo1"])
739734

@@ -749,7 +744,7 @@ def test_generate_branch_protection_multiple_orgs(self):
749744
bp_repos = o.branch_protection["branch-protection"]["orgs"]["cloudfoundry"]["repos"]
750745
# TOC and wg3 opted in, wg1 and wg2 not
751746
# note: repo1..4 are shared between wg1 (opt out) and wg3 (opt in) - wg3 wins
752-
self.assertSetEqual({f"repo{i}" for i in range(1, 6)} | {"community"}, set(bp_repos.keys()))
747+
self.assertSetEqual({f"repo{i}" for i in list(range(1, 6)) + [10, 11]} | {"community"}, set(bp_repos.keys()))
753748
# repo1 has static config that wins over generated branch protection rules
754749
self.assertTrue(bp_repos["repo1"]["protect"])
755750
self.assertNotIn("required_pull_request_reviews", bp_repos["repo1"])

toc/rfc/rfc-0015-branch-protection.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,11 @@ With respect to the approval of pull requests, we propose that the number of app
3737
* 1 approval will be required when a working group has 4 or more people in the approver role.
3838

3939
The automation should allow to override the standard branch protection per respository using a configuration file maintained in this community repository. This allows working group leads e.g. to reduce the number of required approvals if several approvers are temporarily not available.
40+
41+
## Amendments
42+
43+
### Protection by Default
44+
45+
To improve the security posture of the foundation, the branch protection rules defined in this RFC are applied by default to all repositories of all Working Groups. The previous opt-in mechanism via a flag in Working Group charters is removed.
46+
47+
Working Groups can request exceptions for specific repositories by creating a pull request against `orgs/branchprotection.yml`. The pull request description MUST contain a justification for the exception.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Meta
2+
[meta]: #meta
3+
- Name: Service Credential Binding Rotation for Applications
4+
- Start Date: 2025-06-10
5+
- Authors: @beyhan, @stephanme
6+
- Status: Accepted
7+
- RFC Pull Request: [community#1198](https://github.com/cloudfoundry/community/pull/1198)
8+
9+
## Summary
10+
11+
Cloud Controller does not support creating a second binding between an application and a service instance, which prevents seamless rotation of credential bindings. As a result, the only current method to rotate service credential bindings is through blue-green deployments, a process that many developers are hesitant to use solely for credential rotation. That is why, this RFC proposes to add support for multiple service credential bindings per service instance and application. To simplify the rotation process the RFC suggests a new CF API endpoint and CF CLI extensions.
12+
13+
## Problem
14+
15+
Cloud Controller does not support creation of a second binding for a service instance and application which would enable seamless binding credential rotation. Attempting to create a second binding using the CF CLI results in the following error:
16+
17+
```
18+
cf bind-service myApp myService --binding-name second-binding
19+
Binding service instance myService to app myApp in org myOrg / space mySpace as ...
20+
App myApp is already bound to service instance myService.
21+
OK
22+
```
23+
This limitation is not due to the CF CLI but rather a restriction in the CC API, which prevents multiple bindings for the same application. Attempting to create the binding directly via the CF API results in a similar error:
24+
25+
```
26+
cf curl -X POST "/v3/service_credential_bindings" -d '{
27+
"name": "test-second-binding",
28+
"relationships": {
29+
"app": {
30+
"data": {
31+
"guid": "<myApp-guid>"
32+
}
33+
},
34+
"service_instance": {
35+
"data": {
36+
"guid": "<mySerivce-guid>"
37+
}
38+
}
39+
},
40+
"type": "app"
41+
}'
42+
43+
{"errors":[{"detail":"The app is already bound to the service instance","title":"CF-UnprocessableEntity","code":10008}]}
44+
```
45+
As a result, the only way to rotate service credential bindings currently is by performing blue-green deployments. However, many CF application developers are reluctant to push a new version of their application solely for the purpose of credential rotation.
46+
47+
## Proposal
48+
49+
The CC should allow multiple service credential bindings per service instance and application. Only the newest one should be visible in the application VCAP_SERVICES env var (or file-based service binding information) so that existing applications don’t need to change.
50+
51+
### CF Cloud Controller
52+
53+
#### POST /v3/service_credential_bindings
54+
55+
Shall allow the creation of multiple service credential bindings for the same app and service instance under the following conditions:
56+
- service credential bindings are of type `app`
57+
- bindings of type `key` don't have a reference to an application
58+
- multiple service keys for a service instance are already supported
59+
- service credential binding name is not changed
60+
- multiple bindings to the same service instance are intended for credential rotation
61+
- VCAP_SERVICES structure doesn't allow multiple bindings to the same service instance so different binding names don't make sense
62+
63+
The number of multiple service credential bindings for the same app and service instance should be limited. The limit prevents a DoS threat and eventually reminds users to clean up old, likely outdated bindings.
64+
65+
#### GET /v3/service_credential_bindings
66+
67+
The API `GET /v3/service_credential_bindings` will return all service credential bindings of an application including multiple bindings to the same service instance.
68+
69+
### Mapping of service credential bindings into application containers
70+
71+
When creating the service credential binding information like VCAP_SERVICES or file-based one the CC must select the newest one per service instance only that is in state `succeeded`. This must be based on the `created_at` and `last_operation.state` fields from the credential binding.
72+
73+
An application restart or restage as today will be required to activate the new binding.
74+
75+
### CF CLI
76+
77+
Users can create multiple service credential bindings by invoking the `bind-service` command with a `--strategy multiple` option. The default strategy `single` ensures backward compatibility of the `cf bind-service` command.
78+
79+
Example:
80+
```
81+
cf bind-service myApp myService # create initial binding
82+
cf bind-service myApp myService # succeeds with message "App myApp is already bound to service instance myService." No secondary binding is added.
83+
cf bind-service myApp myService --strategy single # same as previous command, 'single' is the default strategy
84+
85+
cf bind-service myApp myService --strategy multiple # adds a secondary binding to the same service instance
86+
cf bind-service myApp myService --strategy multiple # adds a third binding to the same service instance
87+
```
88+
89+
The exact parameter naming can be finalized at implementation time. A `strategy` parameter allows future extension, e.g. for OSBAPI 2.17 support mention in section "Possible Future Work".
90+
91+
`cf unbind-service myApp myService` shall delete all existing service credential bindings for `myApp` to `myService`.
92+
An additional parameter `cf unbind-service --guid <guid>` should support the deletion of a single service credential binding.
93+
94+
`cf service myService` should list all bindings to apps including their `guid` and `created_at` timestamp. This information is helpful to understand which binding will be mapped into the application container.
95+
96+
The cleanup of old service credential bindings should be supported by a new CF CLI command:
97+
```
98+
cf cleanup-outdated-service-bindings myApp [--service-instance myService] [--keep-last 1]
99+
```
100+
The CLI will use the CF API `GET /v3/service_credential_bindings?app_guids=:guid` to list the service instance bindings for an application and should delete all old bindings based on the creation date leaving the newest service bindings. With the `keep-last` parameter, users can keep the x newest bindings per app and service instance. If no service instance name is provided, the CLI should delete the old bindings of all services currently bound to the application.
101+
It is in the responsibility of the user to invoke `cf cleanup-outdated-service-bindings myApp` only after a successfully restage/restart of the app, i.e. when old service credential bindings are not used anymore by any app container.
102+
103+
A full service binding rotation flow with the CF CLI could look like:
104+
```
105+
cf bind-service myApp myService -c {<parameters>} --strategy multiple
106+
cf bind-service myApp myService2 -c {<parameters>} --strategy multiple
107+
cf restage myApp --strategy rolling
108+
cf cleanup-outdated-service-bindings myApp
109+
```
110+
111+
## Possible Future Work
112+
113+
### CC adoption of OSBAPI 2.17
114+
115+
The CC could use the service binding rotation functionality introduced with the OSBAPI 2.17 and introduce a dedicated API endpoint for rotation, e.g. `POST /v3/service_credential_bindings/:guid/actions/duplicate` (good naming to be found).
116+
117+
### Integrate Service Binding Recreation into Rolling Deployment
118+
119+
Here an example how this could look with the CF CLI:
120+
```
121+
cf restage myApp –strategy rolling-with-binding-rotation
122+
```
123+
The result should be that all bindings of the application are recreated after a successful restage.
124+
125+
### Binding Recreation with no App Restart
126+
127+
With the adoption of file-based service bindings in Diego it is technically possible to update a service instance binding for a running application container at runtime without requiring an application restart. Things to consider:
128+
- This will require an extension of the CC and Diego API (https://github.com/cloudfoundry/bbs/blob/main/docs/053-actions.md )
129+
- CF application will need support for service binding credential updates during runtime
130+
- Some service bindings requiring restage can’t be supported
131+
- CC will need a trigger to update the service binding information for the corresponding LRP(s)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Meta
2+
[meta]: #meta
3+
- Name: Shared Concourse Instance
4+
- Start Date: 2025-07-07
5+
- Author(s): @drich10
6+
- Status: Accepted
7+
- RFC Pull Request: [community#1238](https://github.com/cloudfoundry/community/pull/1238)
8+
9+
## Summary
10+
11+
Provide a shared Concourse instance for CI/CD workloads within the CFF.
12+
13+
## Problem
14+
15+
Currently, _most_ teams using Concourse are deploying and managing their own instance. This creates overhead in both engineering time and cloud costs.
16+
17+
## Proposal
18+
19+
The Concourse WG MUST host a shared Concourse for working groups to leverage for CI/CD. This MUST reduce both engineering and cloud expenses. Consolidating Concourse spend into a singular account MAY make it easier to manage the spend and usage. Additionally centralizing management of CI maintenance, MAY remove load on members/leads of working groups managing their own instance(s). Working groups MAY use this instance if they choose to.
20+
21+
### Access
22+
Concourse can use Github teams to manage access control within pipelines and credential systems. As such, working group areas and membership MUST determine roles within the system:
23+
* New role in the Concourse WG to administrate.
24+
* WG execution leads that are onboarded are given adminstration permissions.
25+
* WGs must identify area(s) to give access to.
26+
27+
### Credential Management
28+
Credential access and management MUST be segmented so that WGs cannot access one another's secrets.
29+
* Vault MAY be the secret manager to allow consistent management and separation of secrets between teams on the shared instance.
30+
* We believe most teams currently use Credhub which does not easily allow us to implement the separation that MUST exist between teams.
31+
32+
### Cost Reduction
33+
* Removes the overhead from additional Web and DB instances that come from running multiple instances of Concourse.
34+
* Enables sharing of lesser used worker types such like Windows Workers. Reducing the number of these workers that MUST exist.
35+
36+
### Expectations and Agreements
37+
* Concourse WG leads will be primarily responsible for system availability during the business hours for each individual.
38+
* Concourse WG leads will be responsible for system upgrades.
39+
* WGs onboarded to the shared instance MUST be given sufficient access to operate the system. This includes, but is not limited to:
40+
* IaaS access
41+
* Runbooks and tooling for Concourse deployment
42+
* Support Issues MUST be shared between the Concourse working group maintainers and the concourse supporters
43+
44+
### Timeline
45+
#### Phase 1
46+
* Concourse team creates the new, shared, instance and migrates itself (4-6 weeks from acceptance of this proposal)
47+
48+
#### Phase 2
49+
* Onboard 1 working group to the new instance and refine deployment and operational strategies from initial learnings (4-6 weeks).
50+
* Shut down the concourse owned by the working group.
51+
52+
#### Phase 3
53+
* Open onboarding to the rest of the working groups and migrate their pipelines. Shut down the concourse owned by the working group(s).
54+
* Success criteria:
55+
* 2 or more teams leveraging this instance.
56+
* Each team onboarding MUST either reduce or maintain the current level of infrastructure costs.

0 commit comments

Comments
 (0)