Skip to content

Commit 638d7e1

Browse files
authored
ADR: Use Bosh Storage CLIs for Blobstore Operations (#4443)
* Create 0014-storage-clis-for-blobstore-operations.md * add examples * Add Tasks * add config example * Describe config changes in more detail * Update 0014-storage-clis-for-blobstore-operations.md * remove not needed tasks section
1 parent 8da78dd commit 638d7e1

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# ADR: Introduce Storage CLIs for Blobstore Operations
2+
3+
## Status
4+
5+
📝 **Accepted** - This ADR defines a shared direction for replacing fog-based blobstore implementations.
6+
7+
| Provider | Status | Notes |
8+
|--------------|---------------------------|---------------------------------------------------------------------------------------------------------|
9+
| Azure | 🚧 PoC in Progress | [PoC](https://github.com/cloudfoundry/cloud_controller_ng/pull/4397) done with `bosh-azure-storage-cli` |
10+
| AWS | 🧭 Open for Contribution | |
11+
| GCP | 🧭 Open for Contribution | |
12+
| Alibaba Cloud| 🧭 Open for Contribution | |
13+
14+
15+
## Context
16+
17+
Cloud Controller uses the fog gem family to interface with blobstores like Azure, AWS, GCP, and Alibaba Cloud.
18+
These Ruby gems are largely unmaintained, introducing risks such as:
19+
* Dependency on deprecated SDKs (e.g., Azure SDK for Ruby)
20+
* Blocking Ruby version upgrades
21+
* Potential for unpatched CVEs
22+
23+
Bosh faces similar issues, as it is also written in Ruby and interacts with blobstores. To address this, Bosh introduced standalone CLI tools which shell out from Ruby to handle all blobstore operations:
24+
- https://github.com/cloudfoundry/bosh-azure-storage-cli
25+
- https://github.com/cloudfoundry/bosh-s3cli
26+
- https://github.com/cloudfoundry/bosh-gcscli
27+
- https://github.com/cloudfoundry/bosh-ali-storage-cli
28+
29+
This approach decouples core logic from Ruby gems and has proven to be robust in production.
30+
These CLIs are implemented in Go and use the respective provider SDKs.
31+
All Bosh storage CLIs implement a common interface with the following commands: `put`, `get`, `delete`, `exists`, and `sign`.
32+
33+
A [PoC](https://github.com/cloudfoundry/cloud_controller_ng/pull/4397) has shown that `bosh-azure-storage-cli` can be successfully used in Cloud Controller to push apps.
34+
35+
This ADR does not propose breaking changes to existing Bosh storage CLI commands or their output, but outlines necessary additions to support Cloud Controller use cases. It highlights shared concerns and encourages collaboration between Bosh and Cloud Controller.
36+
37+
## Decision
38+
39+
Cloud Controller will introduce support for CLI based blobstore clients, starting with Azure.
40+
Specifically, we will:
41+
* Add a new blobstore client using `bosh-azure-storage-cli`
42+
* Shell out from Cloud Controller to perform blobstore operations
43+
* Allow opt-in via `blobstore_type` configuration parameter
44+
* Desired configuration format (capi-release perspective):
45+
```YAML
46+
packages:
47+
app_package_directory_key: app-packages
48+
blobstore_type: storage-cli
49+
connection_config:
50+
azure_storage_access_key: <access_key>
51+
azure_storage_account_name: <account_name>
52+
container_name: app-packages
53+
environment: AzureCloud
54+
provider: AzureRM
55+
max_package_size: 1610612736
56+
```
57+
* Field `provider` will be used to determine the corresponding storage CLI blobstore client class (same approach is used for fog)
58+
* The `fog_connection` field will be renamed to `connection_config` to make it independent
59+
* Values from `connection_config` are used to generate the corresponding config file for the Bosh storage CLIs
60+
* Config generation could be moved away from ccng into capi-release to avoid duplication
61+
* During the transition phase existing parameters like `fog_connection` may be reused and/or supported in parallel
62+
* Keep the `fog-azure-rm` backend during the transition
63+
64+
* Algin with Foundational Infrastructure WG - [RFC-0043](https://github.com/cloudfoundry/community/blob/main/toc/rfc/rfc-0043-cc-blobstore-storage-cli.md)
65+
66+
The `bosh-azure-storage-cli` needs to be extended with the following commands:
67+
* `copy`
68+
* `list`
69+
* `properties`
70+
* `ensure-bucket-exists`
71+
72+
Other providers (AWS, GCP, Alibaba Cloud) will follow. Each will require equivalent blobstore clients and support for the above commands.
73+
This will eventually allow us to remove all fog related gems from Cloud Controller.
74+
75+
76+
## Consequences
77+
78+
* Enables the removal of `fog-azure-rm` and all other fog related gems
79+
* Reduces long-term maintenance burden and potential security issues
80+
* Allows providers to be migrated independently
81+
* Increases initial complexity during migration phase
82+
* More maintainers/contributors for the Bosh storage CLIs
83+
84+
85+
* With more consumers, interface changes in the Bosh storage CLIs may require more coordination
86+
87+
## Alternatives Considered
88+
89+
* Replace fog with newer Ruby gems
90+
* → Maintenance risk persists and only a short-term solution
91+
* → Not possible for Azure because the used [azure-sdk-for-ruby](https://github.com/Azure/azure-sdk-for-ruby) is archived
92+
* Implement own blobstore client in Ruby → High development and testing effort
93+
94+
95+
## Out Of Scope
96+
97+
* Support for CDNs (currently supported by fog)
98+
* Performance optimizations
99+
100+
## Example Usage of `bosh-azure-storage-cli`
101+
102+
### [Bosh](https://github.com/cloudfoundry/bosh/blob/main/src/bosh-director/lib/bosh/director/blobstore/azurestoragecli_blobstore_client.rb)
103+
```Ruby
104+
def object_exists?(object_id)
105+
begin
106+
out, err, status = Open3.capture3(@azure_storage_cli_path.to_s, '-c', @config_file.to_s, 'exists', object_id.to_s)
107+
return true if status.exitstatus.zero?
108+
return false if status.exitstatus == 3
109+
rescue Exception => e
110+
raise BlobstoreError, e.inspect
111+
end
112+
raise BlobstoreError, "Failed to check existence of az storage account object, code #{status.exitstatus}, output: '#{out}', error: '#{err}'" unless status.success?
113+
end
114+
```
115+
116+
### [Cloud Controller PoC](https://github.com/cloudfoundry/cloud_controller_ng/pull/4397)
117+
```Ruby
118+
def exists?(blobstore_key)
119+
key = partitioned_key(blobstore_key)
120+
logger.info("[azure-blobstore] [exists?] Checking existence for: #{key}")
121+
status = run_cli('exists', key, allow_nonzero: true)
122+
123+
if status.exitstatus == 0
124+
return true
125+
elsif status.exitstatus == 3
126+
return false
127+
end
128+
129+
false
130+
rescue StandardError => e
131+
logger.error("[azure-blobstore] [exists?] azure-storage-cli exists raised error: #{e.message} for #{key}")
132+
false
133+
end
134+
135+
136+
def run_cli(command, *args, allow_nonzero: false)
137+
logger.info("[azure-blobstore] Running azure-storage-cli: #{@cli_path} -c #{@config_file} #{command} #{args.join(' ')}")
138+
_, stderr, status = Open3.capture3(@cli_path, '-c', @config_file, command, *args)
139+
return status if allow_nonzero
140+
141+
raise "azure-storage-cli #{command} failed: #{stderr}" unless status.success?
142+
143+
status
144+
end
145+
```

0 commit comments

Comments
 (0)