Skip to content

Commit

Permalink
Add delete buildpack cache per app endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
georgessap committed Jun 17, 2024
1 parent c526d5a commit 6366085
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 0 deletions.
13 changes: 13 additions & 0 deletions app/controllers/v3/apps_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,19 @@ def restart
raise CloudController::Errors::ApiError.new_from_details('RunnerUnavailable', 'Unable to communicate with Diego')
end

def clear_buildpack_cache
app, space = AppFetcher.new.fetch(hashed_params[:guid])
app_not_found! unless app && permission_queryer.can_read_from_space?(space.id, space.organization_id)
unauthorized! unless permission_queryer.can_manage_apps_in_active_space?(space.id)
suspended! unless permission_queryer.is_space_active?(space.id)

delete_job = Jobs::V3::BuildpackCacheDelete.new(app.guid)
Jobs::Enqueuer.new(delete_job, queue: Jobs::Queues.generic).enqueue
VCAP::AppLogEmitter.emit(app.guid, "Enqueued job to delete app with guid #{app.guid}")

head :ok, 'Location' => url_builder.build_url(path: "/v3/jobs/#{app.guid}")
end

def builds
message = AppBuildsListMessage.from_params(query_params)
invalid_param!(message.errors.full_messages) unless message.valid?
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
post '/apps/:guid/actions/start', to: 'apps_v3#start'
post '/apps/:guid/actions/stop', to: 'apps_v3#stop'
post '/apps/:guid/actions/restart', to: 'apps_v3#restart'
post '/apps/:guid/actions/clear_buildpack_cache', to: 'apps_v3#clear_buildpack_cache'
get '/apps/:guid/env', to: 'apps_v3#show_env'
get '/apps/:guid/permissions', to: 'apps_v3#show_permissions'
get '/apps/:guid/builds', to: 'apps_v3#builds'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
### Clear buildpack cache

```
Example Request
```

```shell
curl "https://api.example.org/v3/apps/[guid]/actions/clear_buildpack_cache" \
-X POST \
-H "Authorization: bearer [token]"
```

```
Example Response
```

```http
HTTP/1.1 202 Accepted
Location: https://api.example.org/v3/jobs/[guid]
```

This endpoint will delete all of the existing buildpack caches for a specified app.
The buildpack cache is used during staging by buildpacks as a way to
cache certain resources, e.g. downloaded Ruby gems. A user who wants to
decrease the size of their blobstore could use this endpoint to delete
unnecessary blobs.

#### Definition
`POST /v3/apps/:guid/actions/clear_buildpack_cache`

#### Permitted roles
|
--- |
Admin |
Space Developer |
Space Supporter |
1 change: 1 addition & 0 deletions docs/v3/source/index.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ includes:
- resources/apps/start
- resources/apps/stop
- resources/apps/restart
- resources/apps/clear_buildpack_cache
- resources/apps/update_environment_variables
- resources/app_features/header
- resources/app_features/object
Expand Down
75 changes: 75 additions & 0 deletions spec/unit/controllers/v3/apps_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,81 @@
end
end

describe '#clear_buildpack_cache' do
let(:app_model) { VCAP::CloudController::AppModel.make(droplet_guid: droplet.guid, desired_state: 'STARTED') }
let(:droplet) { VCAP::CloudController::DropletModel.make(state: VCAP::CloudController::DropletModel::STAGED_STATE) }
let(:space) { app_model.space }
let(:org) { space.organization }
let(:user) { VCAP::CloudController::User.make }

before do
set_current_user(user)
allow_user_read_access_for(user, spaces: [space])
allow_user_write_access(user, space:)
VCAP::CloudController::BuildpackLifecycleDataModel.make(app: app_model, buildpacks: nil, stack: VCAP::CloudController::Stack.default.name)
end

it 'returns a 200 and the app' do
put :clear_buildpack_cache, params: { guid: app_model.guid }, as: :json

expect(response).to have_http_status :ok
expect(response["Location"]).to eq("http://api2.vcap.me/v3/jobs/#{app_model.guid}")

execute_all_jobs(expected_successes: 1, expected_failures: 0)
end

context 'when the app does not exist' do
it 'raises an API 404 error' do
put :clear_buildpack_cache, params: { guid: 'blah' }, as: :json

expect(response).to have_http_status :not_found
expect(response.body).to include 'ResourceNotFound'
end
end

context 'permissions' do
context 'when the user does not have the write scope' do
before do
set_current_user(VCAP::CloudController::User.make, scopes: ['cloud_controller.read'])
end

it 'raises an ApiError with a 403 code' do
put :clear_buildpack_cache, params: { guid: app_model.guid }, as: :json

expect(response).to have_http_status :forbidden
expect(response.body).to include 'NotAuthorized'
end
end

context 'when the user cannot read the app' do
before do
disallow_user_read_access(user, space:)
end

it 'returns a 404 ResourceNotFound error' do
put :clear_buildpack_cache, params: { guid: app_model.guid }, as: :json

expect(response).to have_http_status :not_found
expect(response.body).to include 'ResourceNotFound'
end
end

context 'when the user can read but cannot write to the app' do
before do
allow_user_read_access_for(user, spaces: [space])
disallow_user_write_access(user, space:)
end

it 'raises ApiError NotAuthorized' do
put :clear_buildpack_cache, params: { guid: app_model.guid }, as: :json

expect(response).to have_http_status :forbidden
expect(response.body).to include 'NotAuthorized'
end
end
end
end

describe '#show_env' do
let(:app_model) { VCAP::CloudController::AppModel.make(environment_variables: { meep: 'moop', beep: 'boop' }) }
let(:space) { app_model.space }
Expand Down

0 comments on commit 6366085

Please sign in to comment.