Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit b8eb81f

Browse files
Allow droplet image to be updated.
- Allow droplet image to be updated if droplet has a docker lifecycle and if the droplet has already been staged. This lays the groundwork for the image controller to be able to patch a droplet with a rebased image reference. [#172542372] Co-authored-by: Piyali Banerjee <pbanerjee@pivotal.io> Co-authored-by: Nat Bennett <nbennett@pivotal.io>
1 parent 53724a6 commit b8eb81f

6 files changed

Lines changed: 151 additions & 41 deletions

File tree

app/actions/droplet_update.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
module VCAP::CloudController
22
class DropletUpdate
3-
class Error < ::StandardError
4-
end
3+
class Error < ::StandardError; end
4+
class InvalidDroplet < StandardError; end
55

66
def update(droplet, message)
77
droplet.db.transaction do
88
droplet.lock!
9+
10+
if message.requested?(:image)
11+
raise InvalidDroplet.new('Droplet image cannot be updated during staging') if droplet.staging?
12+
raise InvalidDroplet.new('Images can only be updated for docker droplets') unless droplet.docker?
13+
14+
droplet.docker_receipt_image = message.image
15+
end
16+
917
LabelsUpdate.update(droplet, message.labels, DropletLabelModel)
1018
AnnotationsUpdate.update(droplet, message.annotations, DropletAnnotationModel)
1119
droplet.save

app/messages/droplet_update_message.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
module VCAP::CloudController
44
class DropletUpdateMessage < MetadataBaseMessage
5-
register_allowed_keys []
5+
register_allowed_keys [:image]
66

7+
validates :image, string: true, allow_nil: true
78
validates_with NoAdditionalKeysValidator
89
end
910
end

spec/request/droplets_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,5 +1463,39 @@
14631463
}
14641464
)
14651465
end
1466+
1467+
context 'when updating the image (on a docker droplet)' do
1468+
let(:app_model) { VCAP::CloudController::AppModel.make(:docker, space_guid: space.guid, name: 'my-docker-app') }
1469+
let(:package_model) { VCAP::CloudController::PackageModel.make(app_guid: app_model.guid) }
1470+
let(:rebased_image_reference) { 'rebased-image-reference' }
1471+
let!(:og_docker_droplet) do
1472+
VCAP::CloudController::DropletModel.make(
1473+
:kpack,
1474+
state: VCAP::CloudController::DropletModel::STAGED_STATE,
1475+
app_guid: app_model.guid,
1476+
package_guid: package_model.guid,
1477+
droplet_hash: 'shalalala',
1478+
sha256_checksum: 'droplet-checksum-sha256',
1479+
)
1480+
end
1481+
before do
1482+
og_docker_droplet.update(docker_receipt_image: 'some-image-reference')
1483+
end
1484+
let(:update_request) do
1485+
{
1486+
image: rebased_image_reference
1487+
}
1488+
end
1489+
it 'updates the image' do
1490+
patch "/v3/droplets/#{og_docker_droplet.guid}", update_request.to_json, developer_headers
1491+
expect(last_response.status).to eq(200), last_response.body
1492+
1493+
og_docker_droplet.reload
1494+
parsed_response = MultiJson.load(last_response.body)
1495+
expect(parsed_response['image']).to eq(
1496+
rebased_image_reference
1497+
)
1498+
end
1499+
end
14661500
end
14671501
end

spec/support/fakes/blueprints.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ module VCAP::CloudController
129129
guid { Sham.guid }
130130
droplet_hash { nil }
131131
sha256_checksum { nil }
132+
docker_receipt_image { nil }
133+
app { AppModel.make(:kpack, droplet_guid: guid) }
132134
state { VCAP::CloudController::DropletModel::STAGED_STATE }
133135
buildpack_lifecycle_data { nil.tap { |_| object.save } }
134136
end

spec/unit/actions/droplet_update_spec.rb

Lines changed: 91 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,102 @@ module VCAP::CloudController
1010
subject(:droplet_update) { DropletUpdate.new }
1111

1212
describe '#update' do
13-
let!(:droplet) { DropletModel.make }
14-
15-
let!(:label) do
16-
VCAP::CloudController::DropletLabelModel.make(
17-
key_prefix: 'indiana.edu',
18-
key_name: 'state',
19-
value: 'Indiana',
20-
resource_guid: droplet.guid
21-
)
22-
end
13+
context 'buildpack droplet update' do
14+
let!(:droplet) { DropletModel.make }
15+
let!(:label) do
16+
VCAP::CloudController::DropletLabelModel.make(
17+
key_prefix: 'indiana.edu',
18+
key_name: 'state',
19+
value: 'Indiana',
20+
resource_guid: droplet.guid
21+
)
22+
end
2323

24-
let!(:annotation) do
25-
VCAP::CloudController::DropletAnnotationModel.make(
26-
key: 'University',
27-
value: 'Toronto',
28-
resource_guid: droplet.guid
29-
)
30-
end
24+
let!(:annotation) do
25+
VCAP::CloudController::DropletAnnotationModel.make(
26+
key: 'University',
27+
value: 'Toronto',
28+
resource_guid: droplet.guid
29+
)
30+
end
3131

32-
let(:message) do
33-
VCAP::CloudController::DropletUpdateMessage.new({
34-
metadata: {
35-
labels: {
36-
freaky: 'wednesday',
37-
'indiana.edu/state' => nil,
38-
},
39-
annotations: {
40-
reason: 'add some more annotations',
32+
let(:message) do
33+
VCAP::CloudController::DropletUpdateMessage.new({
34+
metadata: {
35+
labels: {
36+
freaky: 'wednesday',
37+
'indiana.edu/state' => nil,
38+
},
39+
annotations: {
40+
reason: 'add some more annotations',
41+
},
4142
},
42-
},
43-
})
44-
end
43+
})
44+
end
45+
46+
it 'update the droplet record' do
47+
expect(message).to be_valid
48+
updated_droplet = droplet_update.update(droplet, message)
4549

46-
it 'update the droplet record' do
47-
expect(message).to be_valid
48-
updated_droplet = droplet_update.update(droplet, message)
50+
expect(updated_droplet.labels.first.key_name).to eq 'freaky'
51+
expect(updated_droplet.labels.first.value).to eq 'wednesday'
52+
expect(updated_droplet.labels.size).to eq 1
53+
expect(updated_droplet.annotations.size).to eq(2)
54+
expect(Hash[updated_droplet.annotations.map { |a| [a.key, a.value] }]).
55+
to eq({ 'University' => 'Toronto', 'reason' => 'add some more annotations' })
56+
end
57+
context 'trying to update a buildpack droplet image' do
58+
let(:message) do
59+
VCAP::CloudController::DropletUpdateMessage.new({
60+
image: 'some-image-reference',
61+
metadata: {
62+
labels: {
63+
freaky: 'wednesday',
64+
'indiana.edu/state' => nil,
65+
},
66+
annotations: {
67+
reason: 'add some more annotations',
68+
},
69+
},
70+
})
71+
end
72+
it 'returns an error saying that a buildpack droplet image cannot be updated' do
73+
expect(message).to be_valid
74+
expect { droplet_update.update(droplet, message)
75+
}.to raise_error(DropletUpdate::InvalidDroplet, 'Images can only be updated for docker droplets')
76+
end
77+
end
78+
end
79+
context 'docker droplet update' do
80+
let!(:docker_droplet) do
81+
VCAP::CloudController::DropletModel.make(:kpack)
82+
end
4983

50-
expect(updated_droplet.labels.first.key_name).to eq 'freaky'
51-
expect(updated_droplet.labels.first.value).to eq 'wednesday'
52-
expect(updated_droplet.labels.size).to eq 1
53-
expect(updated_droplet.annotations.size).to eq(2)
54-
expect(Hash[updated_droplet.annotations.map { |a| [a.key, a.value] }]).
55-
to eq({ 'University' => 'Toronto', 'reason' => 'add some more annotations' })
84+
let(:message) do
85+
VCAP::CloudController::DropletUpdateMessage.new({
86+
image: 'new-image-reference'
87+
})
88+
end
89+
context 'the image of a staged docker droplet is requested to be updated' do
90+
before do
91+
docker_droplet.update(docker_receipt_image: 'some-image-reference')
92+
end
93+
it 'updates the droplet record with new image reference' do
94+
expect(message).to be_valid
95+
updated_droplet = droplet_update.update(docker_droplet, message)
96+
expect(updated_droplet.docker_receipt_image).to eq 'new-image-reference'
97+
end
98+
end
99+
context 'the image of a staging docker droplet is requested to be updated' do
100+
before do
101+
docker_droplet.update(state: VCAP::CloudController::DropletModel::STAGING_STATE)
102+
end
103+
it 'returns an error saying that a droplet update cannot occur during staging' do
104+
expect(message).to be_valid
105+
expect { droplet_update.update(docker_droplet, message)
106+
}.to raise_error(DropletUpdate::InvalidDroplet, 'Droplet image cannot be updated during staging')
107+
end
108+
end
56109
end
57110
end
58111
end

spec/unit/messages/droplet_update_message_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module VCAP::CloudController
55
RSpec.describe DropletUpdateMessage do
66
let(:body) do
77
{
8+
'image' => 'some-image-reference',
89
'metadata' => {
910
'labels' => {
1011
'potatoes' => 'taterTots'
@@ -17,6 +18,7 @@ module VCAP::CloudController
1718
it 'can parse labels and annotations' do
1819
params =
1920
{
21+
"image": 'new-image-reference',
2022
"metadata": {
2123
"labels": {
2224
"potato": 'mashed'
@@ -30,6 +32,7 @@ module VCAP::CloudController
3032
expect(message).to be_valid
3133
expect(message.labels).to include("potato": 'mashed')
3234
expect(message.annotations).to include("eating": 'potatoes')
35+
expect(message.image).to eq('new-image-reference')
3336
end
3437

3538
it 'validates both bad labels and bad annotations' do
@@ -43,6 +46,15 @@ module VCAP::CloudController
4346
expect(message).not_to be_valid
4447
expect(message.errors_on(:metadata)).to match_array(["'annotations' is not an object", "'labels' is not an object"])
4548
end
49+
50+
it 'validates bad image references' do
51+
params = {
52+
"image": { 'blah': 34234 }
53+
}
54+
message = DropletUpdateMessage.new(params)
55+
expect(message).not_to be_valid
56+
expect(message.errors_on(:image)).to match_array(['must be a string'])
57+
end
4658
end
4759
end
4860
end

0 commit comments

Comments
 (0)