Skip to content

Commit d8d800d

Browse files
committed
Add support for uploading buildpacks packaged as cnb (tar) files
- This is a follow on to cloudfoundry#3898 which only supports uploading CNBs as zip or tgz whereas they are released in cnb format Signed-off-by: Tom Kennedy <tom.kennedy@broadcom.com>
1 parent e9fbe7c commit d8d800d

File tree

4 files changed

+55
-4
lines changed

4 files changed

+55
-4
lines changed

app/messages/buildpack_upload_message.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module VCAP::CloudController
44
GZIP_MIME = Regexp.new("\x1F\x8B\x08".force_encoding('binary'))
55
ZIP_MIME = Regexp.new("PK\x03\x04".force_encoding('binary'))
6+
CNB_MIME = Regexp.new("\x75\x73\x74\x61\x72".force_encoding('binary'))
67

78
class BuildpackUploadMessage < BaseMessage
89
class MissingFilePathError < StandardError; end
@@ -53,7 +54,10 @@ def is_archive
5354

5455
return if mime_bits =~ /^#{VCAP::CloudController::GZIP_MIME}/ || mime_bits =~ /^#{VCAP::CloudController::ZIP_MIME}/
5556

56-
errors.add(:base, "#{bits_name} is not a zip or gzip archive")
57+
mime_bits_at_offset = File.read(bits_path, 5, 257)
58+
return if mime_bits_at_offset =~ /^#{VCAP::CloudController::CNB_MIME}/
59+
60+
errors.add(:base, "#{bits_name} is not a zip or gzip archive or cnb file")
5761
end
5862

5963
def missing_file_path

spec/support/test_cnb.rb

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require 'zlib'
2+
require 'rubygems/package'
3+
4+
module TestCnb
5+
def self.create(name, file_count, file_size = 1024, &)
6+
File.open(name, 'wb') do |file|
7+
Gem::Package::TarWriter.new(file) do |tar|
8+
file_count.times do |i|
9+
tar.add_file_simple("test_#{i}", 0o644, file_size) do |f|
10+
f.write('A' * file_size)
11+
end
12+
end
13+
end
14+
end
15+
end
16+
end

spec/unit/lib/cloud_controller/upload_buildpack_spec.rb

+21-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ module VCAP::CloudController
1010
let(:tmpdir) { Dir.mktmpdir }
1111
let(:filename) { 'file.zip' }
1212
let(:tgz_filename) { 'file.tgz' }
13+
let(:cnb_filename) { 'file.cnb' }
1314

1415
let(:sha_valid_zip) { Digester.new(algorithm: OpenSSL::Digest::SHA256).digest_file(valid_zip) }
1516
let(:sha_valid_zip2) { Digester.new(algorithm: OpenSSL::Digest::SHA256).digest_file(valid_zip2) }
1617
let(:sha_valid_tgz) { Digester.new(algorithm: OpenSSL::Digest::SHA256).digest_file(valid_tgz) }
18+
let(:sha_valid_cnb) { Digester.new(algorithm: OpenSSL::Digest::SHA256).digest_file(valid_cnb) }
1719

1820
let(:valid_zip_manifest_stack) { nil }
1921
let(:valid_zip) do
@@ -43,6 +45,13 @@ module VCAP::CloudController
4345
Rack::Test::UploadedFile.new(tgz_file)
4446
end
4547

48+
let(:valid_cnb) do
49+
cnb_name = File.join(tmpdir, cnb_filename)
50+
TestCnb.create(cnb_name, 3, 1024)
51+
cnb_file = File.new(cnb_name)
52+
Rack::Test::UploadedFile.new(cnb_file)
53+
end
54+
4655
let(:staging_timeout) { TestConfig.config_instance.get(:staging, :timeout_in_seconds) }
4756

4857
let(:expected_sha_valid_zip) { "#{buildpack.guid}_#{sha_valid_zip}" }
@@ -152,7 +161,7 @@ module VCAP::CloudController
152161
end
153162
end
154163

155-
context 'lifecycle: cnb' do
164+
context 'lifecycle: gzip cnb' do
156165
let!(:buildpack) { VCAP::CloudController::Buildpack.create_from_hash({ name: 'upload_cnb_buildpack', stack: 'cider', position: 0, lifecycle: 'cnb' }) }
157166
let(:expected_sha_valid_tgz) { "#{buildpack.guid}_#{sha_valid_tgz}" }
158167

@@ -163,6 +172,17 @@ module VCAP::CloudController
163172
end
164173
end
165174

175+
context 'lifecycle: cnb' do
176+
let!(:buildpack) { VCAP::CloudController::Buildpack.create_from_hash({ name: 'upload_cnb_buildpack', stack: 'cider', position: 0, lifecycle: 'cnb' }) }
177+
let(:expected_sha_valid_cnb) { "#{buildpack.guid}_#{sha_valid_cnb}" }
178+
179+
it 'uploads' do
180+
expect(buildpack_blobstore).to receive(:cp_to_blobstore).with(valid_cnb, expected_sha_valid_cnb)
181+
182+
upload_buildpack.upload_buildpack(buildpack, valid_cnb, cnb_filename)
183+
end
184+
end
185+
166186
it 'updates the buildpack filename' do
167187
expect do
168188
upload_buildpack.upload_buildpack(buildpack, valid_zip, filename)

spec/unit/messages/buildpack_upload_message_spec.rb

+13-2
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,31 @@ module VCAP::CloudController
102102
allow(File).to receive(:read).and_return("PX\x03\x04".force_encoding('binary'))
103103
upload_message = BuildpackUploadMessage.new(opts)
104104
expect(upload_message).not_to be_valid
105-
expect(upload_message.errors.full_messages[0]).to include('buildpack.abcd is not a zip or gzip')
105+
expect(upload_message.errors.full_messages[0]).to include('buildpack.abcd is not a zip or gzip archive or cnb file')
106106
end
107107
end
108108

109109
context 'when the file is a tgz' do
110110
let(:opts) { { bits_path: '/tmp/bar', bits_name: 'buildpack.tgz' } }
111111

112-
it 'is not valid' do
112+
it 'is valid' do
113113
allow(File).to receive(:read).and_return("\x1F\x8B\x08".force_encoding('binary'))
114114
upload_message = BuildpackUploadMessage.new(opts)
115115
expect(upload_message).to be_valid
116116
end
117117
end
118118

119+
context 'when the file is a cnb/tar' do
120+
let(:opts) { { bits_path: '/tmp/bar', bits_name: 'buildpack.cnb' } }
121+
122+
it 'is valid' do
123+
values = ["\x0".force_encoding('binary'), "\x75\x73\x74\x61\x72\x00\x30\x30".force_encoding('binary')]
124+
allow(File).to receive(:read).and_return(*values)
125+
upload_message = BuildpackUploadMessage.new(opts)
126+
expect(upload_message).to be_valid
127+
end
128+
end
129+
119130
context 'when the file is empty' do
120131
let(:opts) { { bits_path: '/tmp/bar', bits_name: 'buildpack.zip' } }
121132
let(:stat_double) { instance_double(File::Stat, size: 0) }

0 commit comments

Comments
 (0)