diff --git a/Gemfile b/Gemfile index e35c2fcd770..f9f49de3a3c 100644 --- a/Gemfile +++ b/Gemfile @@ -51,7 +51,7 @@ gem 'vmstat', '~> 2.3' gem 'yajl-ruby' # Rails Components -gem 'actionpack', '~> 6.1.7', '>= 6.1.7.3' +gem 'actionpack', '~> 6.1.7' gem 'actionview', '~> 6.1.7', '>= 6.1.7.3' gem 'activemodel', '~> 6.1.7' gem 'railties', '~> 6.1.7', '>= 6.1.7.3' @@ -61,7 +61,7 @@ gem 'azure-storage-blob', git: 'https://github.com/sethboyles/azure-storage-ruby gem 'fog-aliyun' gem 'fog-aws' gem 'fog-azure-rm', git: 'https://github.com/fog/fog-azure-rm.git', branch: 'fog-arm-cf' -gem 'fog-google', '~> 1.20.0' +gem 'fog-google', '~> 1.21.1' gem 'fog-local' gem 'fog-openstack' gem 'fog-core', '~> 2.1.2' @@ -75,7 +75,6 @@ group :db do end group :operations do - gem 'awesome_print' gem 'pry-byebug' end @@ -91,7 +90,7 @@ group :test do gem 'rspec-rails', '~> 6.0.3' gem 'rspec-wait' gem 'rspec_api_documentation', '>= 6.1.0' - gem 'rubocop', '~> 1.52.1' + gem 'rubocop', '~> 1.53.1' gem 'timecop' gem 'webmock', '> 2.3.1' end diff --git a/Gemfile.lock b/Gemfile.lock index 229d851ade6..5b8467ef214 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,22 +49,22 @@ GIT GEM remote: https://rubygems.org/ specs: - actionpack (6.1.7.3) - actionview (= 6.1.7.3) - activesupport (= 6.1.7.3) + actionpack (6.1.7.4) + actionview (= 6.1.7.4) + activesupport (= 6.1.7.4) rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actionview (6.1.7.3) - activesupport (= 6.1.7.3) + actionview (6.1.7.4) + activesupport (= 6.1.7.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activemodel (6.1.7.3) - activesupport (= 6.1.7.3) - activesupport (6.1.7.3) + activemodel (6.1.7.4) + activesupport (= 6.1.7.4) + activesupport (6.1.7.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -79,7 +79,6 @@ GEM activesupport (>= 3.2) i18n ast (2.4.2) - awesome_print (1.9.2) azure-core (0.1.15) faraday (~> 0.9) faraday_middleware (~> 0.10) @@ -174,7 +173,8 @@ GEM excon (~> 0.58) formatador (~> 0.2) mime-types - fog-google (1.20.0) + fog-google (1.21.1) + addressable (>= 2.7.0) fog-core (< 2.3) fog-json (~> 1.2) fog-xml (~> 0.1.0) @@ -184,7 +184,7 @@ GEM google-apis-monitoring_v3 (~> 0.37) google-apis-pubsub_v1 (~> 0.30) google-apis-sqladmin_v1beta4 (~> 0.38) - google-apis-storage_v1 (~> 0.20) + google-apis-storage_v1 (>= 0.19, < 1) google-cloud-env (~> 1.2) fog-json (1.2.0) fog-core @@ -198,7 +198,7 @@ GEM fog-core nokogiri (>= 1.5.11, < 2.0.0) formatador (0.3.0) - google-apis-compute_v1 (0.66.0) + google-apis-compute_v1 (0.71.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-core (0.11.0) addressable (~> 2.5, >= 2.5.1) @@ -213,11 +213,11 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-monitoring_v3 (0.44.0) + google-apis-monitoring_v3 (0.46.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-pubsub_v1 (0.35.0) + google-apis-pubsub_v1 (0.38.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-sqladmin_v1beta4 (0.47.0) + google-apis-sqladmin_v1beta4 (0.50.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.22.0) google-apis-core (>= 0.11.0, < 2.a) @@ -262,7 +262,7 @@ GEM http-form_data (2.3.0) http_parser.rb (0.6.0) httpclient (2.8.3) - i18n (1.13.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) ipaddress (0.8.3) jaro_winkler (1.5.4) @@ -273,7 +273,7 @@ GEM json_pure (2.6.3) jsonpath (1.1.2) multi_json - jwt (2.7.0) + jwt (2.7.1) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) @@ -283,6 +283,7 @@ GEM jsonpath (~> 1.0) recursive-open-struct (~> 1.1, >= 1.1.1) rest-client (~> 2.0) + language_server-protocol (3.17.0.3) libhoney (2.2.0) addressable (~> 2.0) excon @@ -307,7 +308,7 @@ GEM mime-types-data (3.2023.0218.1) mini_mime (1.1.2) mini_portile2 (2.8.2) - minitest (5.18.0) + minitest (5.18.1) mock_redis (0.34.0) ruby2_keywords ms_rest (0.6.4) @@ -330,7 +331,7 @@ GEM net-ssh (7.1.0) netaddr (2.0.6) netrc (0.11.0) - newrelic_rpm (9.2.2) + newrelic_rpm (9.3.0) nio4r (2.5.8) nokogiri (1.15.2) mini_portile2 (~> 2.8.2) @@ -362,7 +363,7 @@ GEM public_suffix (5.0.1) puma (5.6.5) nio4r (~> 2.0) - racc (1.6.2) + racc (1.7.1) rack (2.2.7) rack-protection (3.0.6) rack @@ -374,9 +375,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (6.1.7.3) - actionpack (= 6.1.7.3) - activesupport (= 6.1.7.3) + railties (6.1.7.4) + actionpack (= 6.1.7.4) + activesupport (= 6.1.7.4) method_source rake (>= 12.2) thor (~> 1.0) @@ -443,8 +444,9 @@ GEM activesupport (>= 3.0.0) mustache (~> 1.0, >= 0.99.4) rspec (~> 3.0) - rubocop (1.52.1) + rubocop (1.53.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) @@ -550,12 +552,11 @@ PLATFORMS x86_64-linux DEPENDENCIES - actionpack (~> 6.1.7, >= 6.1.7.3) + actionpack (~> 6.1.7) actionview (~> 6.1.7, >= 6.1.7.3) activemodel (~> 6.1.7) addressable allowy (>= 2.1.0) - awesome_print azure-storage-blob! byebug cf-copilot (= 0.0.14) @@ -570,7 +571,7 @@ DEPENDENCIES fog-aws fog-azure-rm! fog-core (~> 2.1.2) - fog-google (~> 1.20.0) + fog-google (~> 1.21.1) fog-local fog-openstack googleapis-common-protos (>= 1.3.12) @@ -618,7 +619,7 @@ DEPENDENCIES rspec-rails (~> 6.0.3) rspec-wait rspec_api_documentation (>= 6.1.0) - rubocop (~> 1.52.1) + rubocop (~> 1.53.1) rubyzip (>= 1.3.0) sequel (~> 5.69) sequel_pg diff --git a/app/messages/resource_match_create_message.rb b/app/messages/resource_match_create_message.rb index 31ac953a3e7..2ec2079c6c2 100644 --- a/app/messages/resource_match_create_message.rb +++ b/app/messages/resource_match_create_message.rb @@ -22,54 +22,72 @@ def v2_fingerprints_body private + PERMISSIONS_REGEX = /^[0-7]{3,4}$/ + def each_resource if resources.is_a?(Array) resources.each do |r| checksum_validator(r[:checksum]) size_validator(r[:size_in_bytes]) + mode_validator(r[:mode]) end end end - RESOURCE_ERROR_PREAMBLE = 'array contains at least one resource with a'.freeze + RESOURCE_ERROR_PREAMBLE = 'array contains at least one resource with'.freeze def checksum_validator(checksum) unless checksum.is_a?(Hash) - errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} non-object checksum") unless errors.added?( - :resources, "#{RESOURCE_ERROR_PREAMBLE} non-object checksum" + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} a non-object checksum") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} a non-object checksum" ) return end unless checksum[:value].is_a?(String) - errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} non-string checksum value") unless errors.added?( - :resources, "#{RESOURCE_ERROR_PREAMBLE} non-string checksum value" + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} a non-string checksum value") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} a non-string checksum value" ) return end unless valid_sha1?(checksum[:value]) - errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} non-SHA1 checksum value") unless errors.added?( - :resources, "#{RESOURCE_ERROR_PREAMBLE} non-SHA1 checksum value" + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} a non-SHA1 checksum value") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} a non-SHA1 checksum value" ) - return end end def size_validator(size) unless size.is_a?(Integer) - errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} non-integer size_in_bytes") unless errors.added?( - :resources, "#{RESOURCE_ERROR_PREAMBLE} non-integer size_in_bytes" + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} a non-integer size_in_bytes") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} a non-integer size_in_bytes" ) return end unless size >= 0 - errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} negative size_in_bytes") unless errors.added?( - :resources, "#{RESOURCE_ERROR_PREAMBLE} negative size_in_bytes" + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} a negative size_in_bytes") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} a negative size_in_bytes" + ) + end + end + + def mode_validator(mode) + return if mode.nil? + + unless mode.is_a?(String) + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} a non-string mode") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} a non-string mode" ) return end + + unless PERMISSIONS_REGEX.match?(mode) + errors.add(:resources, "#{RESOURCE_ERROR_PREAMBLE} an incorrect mode") unless errors.added?( + :resources, "#{RESOURCE_ERROR_PREAMBLE} an incorrect mode" + ) + end end def valid_sha1?(value) diff --git a/app/models/runtime/stack.rb b/app/models/runtime/stack.rb index 03156706ca1..aa946ef0bcd 100644 --- a/app/models/runtime/stack.rb +++ b/app/models/runtime/stack.rb @@ -25,8 +25,8 @@ class AppsStillPresentError < StandardError plugin :serialization - export_attributes :name, :description - import_attributes :name, :description + export_attributes :name, :description, :build_rootfs_image, :run_rootfs_image + import_attributes :name, :description, :build_rootfs_image, :run_rootfs_image strip_attributes :name @@ -47,6 +47,14 @@ def default? false end + def build_rootfs_image + super || self.name + end + + def run_rootfs_image + super || self.name + end + def self.configure(file_path) @config_file = if file_path ConfigFile.new(file_path) @@ -83,7 +91,7 @@ def self.populate_from_hash(hash) Steno.logger('cc.stack').warn('stack.populate.collision', hash) end else - create(hash.slice('name', 'description')) + create(hash.slice('name', 'description', 'build_rootfs_image', 'run_rootfs_image')) end end @@ -108,6 +116,8 @@ def default 'stacks' => [{ 'name' => String, 'description' => String, + optional('build_rootfs_image') => String, + optional('run_rootfs_image') => String, }] } end diff --git a/app/presenters/v3/process_presenter.rb b/app/presenters/v3/process_presenter.rb index d68eb4f5c5c..d2a02ef4a37 100644 --- a/app/presenters/v3/process_presenter.rb +++ b/app/presenters/v3/process_presenter.rb @@ -22,6 +22,7 @@ def to_hash guid: process.guid, created_at: process.created_at, updated_at: process.updated_at, + version: process.version, type: process.type, command: redact(process.specified_or_detected_command), instances: process.instances, diff --git a/config/version b/config/version index ee7ed81cf4f..39e99221a67 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -3.140.0 +3.141.0 diff --git a/config/version_v2 b/config/version_v2 index b0436fe4e20..a52928e4bdf 100644 --- a/config/version_v2 +++ b/config/version_v2 @@ -1 +1 @@ -2.205.0 +2.206.0 diff --git a/db/migrations/20230622194039_add_build_and_run_rootfs_images_to_stack.rb b/db/migrations/20230622194039_add_build_and_run_rootfs_images_to_stack.rb new file mode 100644 index 00000000000..b694046d3a5 --- /dev/null +++ b/db/migrations/20230622194039_add_build_and_run_rootfs_images_to_stack.rb @@ -0,0 +1,15 @@ +Sequel.migration do + up do + alter_table :stacks do + add_column :build_rootfs_image, String, size: 255 + add_column :run_rootfs_image, String, size: 255 + end + end + + down do + alter_table :stacks do + drop_column :build_rootfs_image + drop_column :run_rootfs_image + end + end +end diff --git a/docs/v2/info/get_info.html b/docs/v2/info/get_info.html index 015119ea41f..7e6b23f86ed 100644 --- a/docs/v2/info/get_info.html +++ b/docs/v2/info/get_info.html @@ -103,7 +103,7 @@

Body

"token_endpoint": "http://localhost:8080/uaa", "min_cli_version": null, "min_recommended_cli_version": null, - "api_version": "2.205.0", + "api_version": "2.206.0", "app_ssh_endpoint": "ssh.system.domain.example.com:2222", "app_ssh_host_key_fingerprint": "47:0d:d1:c8:c3:3d:0a:36:d1:49:2f:f2:90:27:31:d0", "app_ssh_oauth_client": null, diff --git a/docs/v3/source/includes/api_resources/_processes.erb b/docs/v3/source/includes/api_resources/_processes.erb index b2eb729391a..d1719730b0a 100644 --- a/docs/v3/source/includes/api_resources/_processes.erb +++ b/docs/v3/source/includes/api_resources/_processes.erb @@ -31,6 +31,7 @@ }, "created_at": "2016-03-23T18:48:22Z", "updated_at": "2016-03-23T18:48:42Z", + "version": "e9df685c-0464-4aa7-b5f0-8ed843077c13", "links": { "self": { "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82" @@ -101,6 +102,7 @@ }, "created_at": "2016-03-23T18:48:22Z", "updated_at": "2016-03-23T18:48:42Z", + "version": "e9df685c-0464-4aa7-b5f0-8ed843077c13", "links": { "self": { "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82" @@ -148,6 +150,7 @@ }, "created_at": "2016-03-23T18:48:22Z", "updated_at": "2016-03-23T18:48:42Z", + "version": "74e513bb-7b9e-445c-84d5-7fea1394e611", "links": { "self": { "href": "https://api.example.org/v3/processes/3fccacd9-4b02-4b96-8d02-8e865865e9eb" diff --git a/docs/v3/source/includes/resources/processes/_object.md.erb b/docs/v3/source/includes/resources/processes/_object.md.erb index bcb24be3926..1cb468e3963 100644 --- a/docs/v3/source/includes/resources/processes/_object.md.erb +++ b/docs/v3/source/includes/resources/processes/_object.md.erb @@ -12,6 +12,7 @@ Name | Type | Description **guid** | _uuid_ | Unique identifier for the process **created_at** | _[timestamp](#timestamps)_ | The time with zone when the object was created **updated_at** | _[timestamp](#timestamps)_ | The time with zone when the object was last updated +**version** | _uuid_ | Random identifier that changes every time the process will be recreated in the runtime. **type** | _string_ | Process type; a unique identifier for processes belonging to an app **command** | _string_ or _null_ | The command used to start the process; use _null_ to revert to the buildpack-detected or procfile-provided start command **instances** | _integer_ | The number of instances to run diff --git a/lib/cloud_controller/diego/buildpack/desired_lrp_builder.rb b/lib/cloud_controller/diego/buildpack/desired_lrp_builder.rb index 19981db51e4..5e1d1e3b495 100644 --- a/lib/cloud_controller/diego/buildpack/desired_lrp_builder.rb +++ b/lib/cloud_controller/diego/buildpack/desired_lrp_builder.rb @@ -38,7 +38,10 @@ def cached_dependencies end def root_fs - "preloaded:#{@stack}" + @stack_obj ||= Stack.find(name: @stack) + raise CloudController::Errors::ApiError.new_from_details('StackNotFound', @stack) unless @stack_obj + + "preloaded:#{@stack_obj.run_rootfs_image}" end def setup diff --git a/lib/cloud_controller/diego/buildpack/staging_action_builder.rb b/lib/cloud_controller/diego/buildpack/staging_action_builder.rb index 56ecffda10a..7b7f9bb05b8 100644 --- a/lib/cloud_controller/diego/buildpack/staging_action_builder.rb +++ b/lib/cloud_controller/diego/buildpack/staging_action_builder.rb @@ -37,7 +37,7 @@ def image_layers layers = [ ::Diego::Bbs::Models::ImageLayer.new( - name: "buildpack-#{stack}-lifecycle", + name: "buildpack-#{lifecycle_stack}-lifecycle", url: LifecycleBundleUriGenerator.uri(config.get(:diego, :lifecycle_bundles)[lifecycle_bundle_key]), destination_path: '/tmp/lifecycle', layer_type: ::Diego::Bbs::Models::ImageLayer::Type::SHARED, @@ -97,7 +97,7 @@ def cached_dependencies ::Diego::Bbs::Models::CachedDependency.new( from: LifecycleBundleUriGenerator.uri(config.get(:diego, :lifecycle_bundles)[lifecycle_bundle_key]), to: '/tmp/lifecycle', - cache_key: "buildpack-#{stack}-lifecycle", + cache_key: "buildpack-#{lifecycle_stack}-lifecycle", ) ] @@ -122,6 +122,13 @@ def cached_dependencies end def stack + @stack ||= Stack.find(name: lifecycle_stack) + raise CloudController::Errors::ApiError.new_from_details('StackNotFound', lifecycle_stack) unless @stack + + "preloaded:#{@stack.build_rootfs_image}" + end + + def lifecycle_stack lifecycle_data[:stack] end diff --git a/lib/cloud_controller/diego/buildpack/task_action_builder.rb b/lib/cloud_controller/diego/buildpack/task_action_builder.rb index 6d56f443a9b..df1792696a5 100644 --- a/lib/cloud_controller/diego/buildpack/task_action_builder.rb +++ b/lib/cloud_controller/diego/buildpack/task_action_builder.rb @@ -13,7 +13,6 @@ def initialize(config, task, lifecycle_data) @config = config @task = task @lifecycle_data = lifecycle_data - @stack = lifecycle_data[:stack] end def action @@ -55,8 +54,8 @@ def action def image_layers return [] unless @config.get(:diego, :enable_declarative_asset_downloads) - destination = @config.get(:diego, :droplet_destinations)[@stack.to_sym] - raise InvalidStack.new("no droplet destination defined for requested stack '#{@stack}'") unless destination + destination = @config.get(:diego, :droplet_destinations)[lifecycle_stack.to_sym] + raise InvalidStack.new("no droplet destination defined for requested stack '#{lifecycle_stack}'") unless destination layers = [ ::Diego::Bbs::Models::ImageLayer.new( @@ -88,7 +87,10 @@ def task_environment_variables end def stack - "preloaded:#{lifecycle_stack}" + @stack ||= Stack.find(name: lifecycle_stack) + raise CloudController::Errors::ApiError.new_from_details('StackNotFound', lifecycle_stack) unless @stack + + "preloaded:#{@stack.run_rootfs_image}" end def cached_dependencies diff --git a/lib/cloud_controller/diego/docker/staging_action_builder.rb b/lib/cloud_controller/diego/docker/staging_action_builder.rb index 297af7074da..bb52466ac39 100644 --- a/lib/cloud_controller/diego/docker/staging_action_builder.rb +++ b/lib/cloud_controller/diego/docker/staging_action_builder.rb @@ -68,7 +68,7 @@ def cached_dependencies end def stack - config.get(:diego, :docker_staging_stack) + "preloaded:#{config.get(:diego, :docker_staging_stack)}" end def task_environment_variables; end diff --git a/lib/cloud_controller/diego/task_recipe_builder.rb b/lib/cloud_controller/diego/task_recipe_builder.rb index 638c1f78866..18aa550655b 100644 --- a/lib/cloud_controller/diego/task_recipe_builder.rb +++ b/lib/cloud_controller/diego/task_recipe_builder.rb @@ -70,7 +70,7 @@ def build_staging_task(config, staging_details) privileged: config.get(:diego, :use_privileged_containers_for_staging), result_file: STAGING_RESULT_FILE, trusted_system_certificates_path: STAGING_TRUSTED_SYSTEM_CERT_PATH, - root_fs: "preloaded:#{action_builder.stack}", + root_fs: action_builder.stack, action: timeout(action_builder.action, timeout_ms: config.get(:staging, :timeout_in_seconds).to_i * 1000), environment_variables: action_builder.task_environment_variables, legacy_download_user: LEGACY_DOWNLOAD_USER, diff --git a/spec/fixtures/config/stacks_include_build_run.yml b/spec/fixtures/config/stacks_include_build_run.yml new file mode 100644 index 00000000000..76bd00e72db --- /dev/null +++ b/spec/fixtures/config/stacks_include_build_run.yml @@ -0,0 +1,16 @@ +default: "default-stack-name" + +stacks: + - name: "cflinuxfs4" + description: "cflinuxfs4" + - name: "default-stack-name" + description: "default-stack-description" + - name: "cider" + description: "cider-description" + build_rootfs_image: "cider-build" + run_rootfs_image: "cider-run" + - name: "separate-build-run-images" + description: "A stack that has different rootfs images for building and running apps" + build_rootfs_image: "build" + run_rootfs_image: "run" + diff --git a/spec/request/processes_spec.rb b/spec/request/processes_spec.rb index ccfebd85dfe..79e4c894c1b 100644 --- a/spec/request/processes_spec.rb +++ b/spec/request/processes_spec.rb @@ -129,6 +129,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => web_process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{web_process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{web_process.guid}/actions/scale", 'method' => 'POST' }, @@ -159,6 +160,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => worker_process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{worker_process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{worker_process.guid}/actions/scale", 'method' => 'POST' }, @@ -417,6 +419,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/actions/scale", 'method' => 'POST' }, @@ -658,6 +661,7 @@ }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/actions/scale", 'method' => 'POST' }, @@ -801,6 +805,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/actions/scale", 'method' => 'POST' }, @@ -1155,6 +1160,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process1.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process1.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process1.guid}/actions/scale", 'method' => 'POST' }, @@ -1189,6 +1195,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process2.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process2.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process2.guid}/actions/scale", 'method' => 'POST' }, @@ -1308,6 +1315,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/actions/scale", 'method' => 'POST' }, @@ -1410,6 +1418,7 @@ }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/actions/scale", 'method' => 'POST' }, @@ -1520,6 +1529,7 @@ 'metadata' => { 'annotations' => {}, 'labels' => {} }, 'created_at' => iso8601, 'updated_at' => iso8601, + 'version' => process.version, 'links' => { 'self' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}" }, 'scale' => { 'href' => "#{link_prefix}/v3/processes/#{process.guid}/actions/scale", 'method' => 'POST' }, diff --git a/spec/request/v2/apps_spec.rb b/spec/request/v2/apps_spec.rb index b5962d78a0f..2e1373b1aea 100644 --- a/spec/request/v2/apps_spec.rb +++ b/spec/request/v2/apps_spec.rb @@ -290,7 +290,9 @@ }, 'entity' => { 'name' => process.stack.name, - 'description' => process.stack.description + 'description' => process.stack.description, + 'build_rootfs_image' => process.stack.name, + 'run_rootfs_image' => process.stack.name, } }, 'routes_url' => "/v2/apps/#{process.guid}/routes", diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d5a355dd6db..85a5ed6726d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -61,7 +61,6 @@ require 'machinist/object' require 'rack/test' require 'timecop' - require 'awesome_print' require 'steno' require 'webmock/rspec' diff --git a/spec/unit/lib/cloud_controller/diego/buildpack/desired_lrp_builder_spec.rb b/spec/unit/lib/cloud_controller/diego/buildpack/desired_lrp_builder_spec.rb index 6096e55b7d8..638ae5daa68 100644 --- a/spec/unit/lib/cloud_controller/diego/buildpack/desired_lrp_builder_spec.rb +++ b/spec/unit/lib/cloud_controller/diego/buildpack/desired_lrp_builder_spec.rb @@ -5,6 +5,11 @@ module Diego module Buildpack RSpec.describe DesiredLrpBuilder do subject(:builder) { DesiredLrpBuilder.new(config, opts) } + before do + Stack.create(name: 'potato-stack') + Stack.create(name: 'stack-thats-not-in-config') + end + let(:stack) { 'potato-stack' } let(:opts) do { @@ -57,6 +62,33 @@ module Buildpack expect(builder.root_fs).to eq('preloaded:potato-stack') end end + + context 'when the stack does not exist' do + let(:stack) { 'does-not-exist' } + + it 'raises an error' do + expect { + builder.root_fs + }.to raise_error CloudController::Errors::ApiError, /The stack could not be found/ + end + end + + context 'when the stack has separate run and build root_fs images' do + let(:stack) { 'two-images-stack' } + + before do + Stack.create( + name: stack, + description: 'a stack with separate build and run rootfses', + run_rootfs_image: 'run-image', + build_rootfs_image: 'build-image', + ) + end + + it 'returns the run root_fs' do + expect(builder.root_fs).to eq('preloaded:run-image') + end + end end describe '#cached_dependencies' do @@ -216,7 +248,7 @@ module Buildpack ) end - context 'when searching for a lifecycle associated with a nonexistant stack' do + context "when searching for a lifecycle associated with a stack that is not configured in the Cloud Controller's lifecycle_bundles config" do let(:lifecycle_bundles) do { "hot-potato": '/path/to/lifecycle.tgz' } end @@ -227,7 +259,7 @@ module Buildpack end end - context 'when searching for a droplet destination associated with a nonexistant stack' do + context "when searching for a droplet destination associated with a stack that is not configured in the Cloud Controller's droplet_destinations config" do let(:droplet_destinations) do { "hot-potato": '/value/from/config/based/on/stack' } end diff --git a/spec/unit/lib/cloud_controller/diego/buildpack/staging_action_builder_spec.rb b/spec/unit/lib/cloud_controller/diego/buildpack/staging_action_builder_spec.rb index 9d41da57890..3634668d940 100644 --- a/spec/unit/lib/cloud_controller/diego/buildpack/staging_action_builder_spec.rb +++ b/spec/unit/lib/cloud_controller/diego/buildpack/staging_action_builder_spec.rb @@ -32,6 +32,7 @@ module Buildpack end end let(:env) { double(:env) } + let(:stack) { 'buildpack-stack' } let(:lifecycle_data) do { app_bits_download_uri: 'http://app_bits_download_uri.example.com/path/to/bits', @@ -39,7 +40,7 @@ module Buildpack build_artifacts_cache_upload_uri: 'http://build_artifacts_cache_upload_uri.example.com/path/to/bits', buildpacks: buildpacks, droplet_upload_uri: 'http://droplet_upload_uri.example.com/path/to/bits', - stack: 'buildpack-stack', + stack: stack, buildpack_cache_checksum: 'bp-cache-checksum', app_bits_checksum: { type: 'sha256', value: 'package-checksum' }, } @@ -51,6 +52,8 @@ module Buildpack allow(LifecycleBundleUriGenerator).to receive(:uri).with('the-buildpack-bundle').and_return('generated-uri') allow(BbsEnvironmentBuilder).to receive(:build).with(env).and_return(generated_environment) TestConfig.override(credhub_api: nil) + + Stack.create(name: 'buildpack-stack') end describe '#action' do @@ -576,8 +579,30 @@ module Buildpack end describe '#stack' do + before do + Stack.create(name: 'separate-build-and-run', run_rootfs_image: 'run-image', build_rootfs_image: 'build-image') + end + it 'returns the stack' do - expect(builder.stack).to eq('buildpack-stack') + expect(builder.stack).to eq('preloaded:buildpack-stack') + end + + context 'when the stack does not exist' do + let(:stack) { 'does-not-exist' } + + it 'raises an error' do + expect { + builder.stack + }.to raise_error CloudController::Errors::ApiError, /The stack could not be found/ + end + end + + context 'when the stack has separate build and run rootfs images' do + let(:stack) { 'separate-build-and-run' } + + it 'returns the build rootfs image' do + expect(builder.stack).to eq('preloaded:build-image') + end end end diff --git a/spec/unit/lib/cloud_controller/diego/buildpack/task_action_builder_spec.rb b/spec/unit/lib/cloud_controller/diego/buildpack/task_action_builder_spec.rb index 6665696909c..2cc51cce647 100644 --- a/spec/unit/lib/cloud_controller/diego/buildpack/task_action_builder_spec.rb +++ b/spec/unit/lib/cloud_controller/diego/buildpack/task_action_builder_spec.rb @@ -235,9 +235,31 @@ module Buildpack end describe '#stack' do + before do + Stack.create(name: 'potato-stack') + Stack.create(name: 'separate-build-and-run', run_rootfs_image: 'run-image', build_rootfs_image: 'build-image') + end + it 'returns the stack' do expect(builder.stack).to eq('preloaded:potato-stack') end + + context 'when the stack does not exist in the database' do + let(:stack) { 'does-not-exist' } + it 'raises an error' do + expect { + builder.stack + }.to raise_error CloudController::Errors::ApiError, /The stack could not be found/ + end + end + + context 'when the stack has separate build and run rootfs images' do + let(:stack) { 'separate-build-and-run' } + + it 'returns the run image name' do + expect(builder.stack).to eq('preloaded:run-image') + end + end end describe '#cached_dependencies' do diff --git a/spec/unit/lib/cloud_controller/diego/docker/staging_action_builder_spec.rb b/spec/unit/lib/cloud_controller/diego/docker/staging_action_builder_spec.rb index 4b9cd992322..ee847cc9dec 100644 --- a/spec/unit/lib/cloud_controller/diego/docker/staging_action_builder_spec.rb +++ b/spec/unit/lib/cloud_controller/diego/docker/staging_action_builder_spec.rb @@ -133,7 +133,7 @@ module Docker describe '#stack' do it 'returns the configured docker_staging_stack' do - expect(builder.stack).to eq('docker-staging-stack') + expect(builder.stack).to eq('preloaded:docker-staging-stack') end end diff --git a/spec/unit/lib/cloud_controller/diego/task_recipe_builder_spec.rb b/spec/unit/lib/cloud_controller/diego/task_recipe_builder_spec.rb index 1407e3fd004..378cdf79700 100644 --- a/spec/unit/lib/cloud_controller/diego/task_recipe_builder_spec.rb +++ b/spec/unit/lib/cloud_controller/diego/task_recipe_builder_spec.rb @@ -124,7 +124,7 @@ module Diego let(:lifecycle_action_builder) do instance_double( Buildpack::StagingActionBuilder, - stack: 'potato-stack', + stack: 'preloaded:potato-stack', action: buildpack_staging_action, task_environment_variables: lifecycle_environment_variables, cached_dependencies: lifecycle_cached_dependencies, @@ -211,7 +211,7 @@ module Diego let(:lifecycle_action_builder) do instance_double( Docker::StagingActionBuilder, - stack: 'docker-stack', + stack: 'preloaded:docker-stack', action: docker_staging_action, task_environment_variables: lifecycle_environment_variables, cached_dependencies: lifecycle_cached_dependencies, diff --git a/spec/unit/messages/resource_match_create_message_spec.rb b/spec/unit/messages/resource_match_create_message_spec.rb index ed3b3d844bd..f2f8e486c35 100644 --- a/spec/unit/messages/resource_match_create_message_spec.rb +++ b/spec/unit/messages/resource_match_create_message_spec.rb @@ -166,6 +166,44 @@ end end + context 'when the v3 mode is not a string' do + let(:params) do + { + resources: [ + { + checksum: { value: '002d760bea1be268e27077412e11a320d0f164d3' }, + size_in_bytes: 36, + mode: 123 + } + ] + } + end + + it 'has the correct error message' do + expect(subject).to be_invalid + expect(subject.errors[:resources]).to include('array contains at least one resource with a non-string mode') + end + end + + context 'when the v3 mode is not a POSIX file permissions string' do + let(:params) do + { + resources: [ + { + checksum: { value: '002d760bea1be268e27077412e11a320d0f164d3' }, + size_in_bytes: 36, + mode: '9876' + } + ] + } + end + + it 'has the correct error message' do + expect(subject).to be_invalid + expect(subject.errors[:resources]).to include('array contains at least one resource with an incorrect mode') + end + end + context 'when there are multiple validation violations' do let(:params) do { diff --git a/spec/unit/models/runtime/stack_spec.rb b/spec/unit/models/runtime/stack_spec.rb index efdf57324d0..02dc8e325b0 100644 --- a/spec/unit/models/runtime/stack_spec.rb +++ b/spec/unit/models/runtime/stack_spec.rb @@ -29,8 +29,8 @@ module VCAP::CloudController end describe 'Serialization' do - it { is_expected.to export_attributes :name, :description } - it { is_expected.to import_attributes :name, :description } + it { is_expected.to export_attributes :name, :description, :build_rootfs_image, :run_rootfs_image } + it { is_expected.to import_attributes :name, :description, :build_rootfs_image, :run_rootfs_image } end describe '.configure' do @@ -70,7 +70,7 @@ module VCAP::CloudController context 'when config was set' do before { Stack.configure(file) } - context 'when there are no stacks' do + context 'when there are no stacks in the database' do before { Stack.dataset.destroy } it 'creates them all' do @@ -83,7 +83,58 @@ module VCAP::CloudController expect(default_stack.description).to eq('default-stack-description') end - context 'when there are existing stacks' do + describe 'build and run rootfs image names' do + context 'when the build or run rootfs image names are blank' do + it 'uses the stack name instead' do + Stack.populate + cflinuxfs4 = Stack.find(name: 'cflinuxfs4') + expect(cflinuxfs4.build_rootfs_image).to eq 'cflinuxfs4' + expect(cflinuxfs4.run_rootfs_image).to eq 'cflinuxfs4' + end + end + + context 'when the build or run rootfs image names are provided' do + it 'sets the field values' do + Stack.configure(File.join(Paths::FIXTURES, 'config/stacks_include_build_run.yml')) + Stack.populate + + separate_images = Stack.find(name: 'separate-build-run-images') + expect(separate_images.build_rootfs_image).to eq 'build' + expect(separate_images.run_rootfs_image).to eq 'run' + end + end + + context 'when an existing stack would have its rootfs images changed' do + before do + Stack.configure(file) + Stack.populate + end + + it 'warns and does not update' do + Stack.configure(File.join(Paths::FIXTURES, 'config/stacks_include_build_run.yml')) + + mock_logger = double + allow(Steno).to receive(:logger).and_return(mock_logger) + + expect(mock_logger).to receive(:warn).with( + 'stack.populate.collision', + { + 'name' => 'cider', + 'description' => 'cider-description', + 'build_rootfs_image' => 'cider-build', + 'run_rootfs_image' => 'cider-run', + }) + + Stack.populate + + cider = Stack.find(name: 'cider') + expect(cider.build_rootfs_image).to eq 'cider' + expect(cider.run_rootfs_image).to eq 'cider' + end + end + end + + context 'when there are existing stacks in the database' do before do Stack.dataset.destroy Stack.populate @@ -94,7 +145,7 @@ module VCAP::CloudController end context 'and the config file would change an existing stack' do - it 'should warn' do + it 'should warn and not update' do cider = Stack.find(name: 'cider') cider.description = 'cider-description has changed' cider.save @@ -105,6 +156,9 @@ module VCAP::CloudController expect(mock_logger).to receive(:warn).with('stack.populate.collision', { 'name' => 'cider', 'description' => 'cider-description' }) Stack.populate + + second_lookup = Stack.find(name: 'cider') + expect(second_lookup.description).to eq('cider-description has changed') end end end diff --git a/spec/unit/presenters/v3/process_presenter_spec.rb b/spec/unit/presenters/v3/process_presenter_spec.rb index affccd4c66b..09bc5aebfa4 100644 --- a/spec/unit/presenters/v3/process_presenter_spec.rb +++ b/spec/unit/presenters/v3/process_presenter_spec.rb @@ -86,6 +86,8 @@ module VCAP::CloudController::Presenters::V3 context('when health_check_type is http') do it 'presents the process as a hash' do expect(result[:guid]).to eq(process.guid) + expect(result[:version]).to be_a_guid + expect(result[:version]).to eq(process.version) expect(result[:instances]).to eq(3) expect(result[:memory_in_mb]).to eq(42) expect(result[:disk_in_mb]).to eq(37) @@ -106,6 +108,8 @@ module VCAP::CloudController::Presenters::V3 let(:health_check_type) { 'port' } it 'presents the process as a hash without a health_check/data/endpoint' do expect(result[:guid]).to eq(process.guid) + expect(result[:version]).to be_a_guid + expect(result[:version]).to eq(process.version) expect(result[:instances]).to eq(3) expect(result[:memory_in_mb]).to eq(42) expect(result[:disk_in_mb]).to eq(37)