diff --git a/app/actions/space_diff_manifest.rb b/app/actions/space_diff_manifest.rb
index a24525d469a..92afd515fcc 100644
--- a/app/actions/space_diff_manifest.rb
+++ b/app/actions/space_diff_manifest.rb
@@ -1,4 +1,5 @@
require 'presenters/v3/app_manifest_presenter'
+require 'messages/app_manifest_message'
require 'json-diff'
module VCAP::CloudController
@@ -62,7 +63,7 @@ def self.filter_manifest_app_hash(manifest_app_hash)
# rubocop:todo Metrics/CyclomaticComplexity
def self.generate_diff(app_manifests, space)
json_diff = []
- recognized_top_level_keys = NamedAppManifestMessage.allowed_keys.map(&:to_s)
+ recognized_top_level_keys = AppManifestMessage.allowed_keys.map(&:to_s)
app_manifests.each_with_index do |manifest_app_hash, index|
manifest_app_hash = SpaceDiffManifest.filter_manifest_app_hash(manifest_app_hash)
existing_app = space.app_models.find { |app| app.name == manifest_app_hash['name'] }
diff --git a/app/controllers/v3/app_manifests_controller.rb b/app/controllers/v3/app_manifests_controller.rb
index 9828cd7eda3..d7b32186039 100644
--- a/app/controllers/v3/app_manifests_controller.rb
+++ b/app/controllers/v3/app_manifests_controller.rb
@@ -5,36 +5,6 @@
class AppManifestsController < ApplicationController
include AppSubResource
- wrap_parameters :body, format: [:yaml]
-
- before_action :validate_content_type!, only: :apply_manifest
-
- def apply_manifest
- message = AppManifestMessage.create_from_yml(parsed_app_manifest_params)
- compound_error!(message.errors.full_messages) unless message.valid?
-
- app, space, org = AppFetcher.new.fetch(hashed_params[:guid])
-
- app_not_found! unless app && permission_queryer.can_read_from_space?(space.guid, org.guid)
- unauthorized! unless permission_queryer.can_write_to_space?(space.guid)
- unsupported_for_docker_apps!(message) if incompatible_with_buildpacks(app.lifecycle_type, message)
-
- apply_manifest_action = AppApplyManifest.new(user_audit_info)
- apply_manifest_job = VCAP::CloudController::Jobs::AppApplyManifestActionJob.new(app.guid, message, apply_manifest_action)
-
- record_apply_manifest_audit_event(app, message, space)
- job = Jobs::Enqueuer.new(apply_manifest_job, queue: Jobs::Queues.generic).enqueue_pollable
- TelemetryLogger.v3_emit(
- 'apply-manifest',
- {
- 'app-id' => app.guid,
- 'user-id' => current_user.guid
- }
- )
-
- head HTTP::ACCEPTED, 'Location' => url_builder.build_url(path: "/v3/jobs/#{job.guid}")
- end
-
def show
app, space, org = AppFetcher.new.fetch(hashed_params[:guid])
@@ -45,44 +15,4 @@ def show
manifest_yaml = manifest_presenter.to_hash.deep_stringify_keys.to_yaml
render status: :ok, plain: manifest_yaml, content_type: YAML_CONTENT_TYPE
end
-
- private
-
- def record_apply_manifest_audit_event(app, message, space)
- audited_request_yaml = { 'applications' => [message.audit_hash] }.to_yaml
- Repositories::AppEventRepository.new.record_app_apply_manifest(app, space, user_audit_info, audited_request_yaml)
- end
-
- def unsupported_for_docker_apps!(manifest)
- error_message = manifest.buildpacks ? 'Buildpacks' : 'Buildpack'
- raise unprocessable(error_message + ' cannot be configured for a docker lifecycle app.')
- end
-
- def incompatible_with_buildpacks(lifecycle_type, manifest)
- lifecycle_type == 'docker' && (manifest.buildpack || manifest.buildpacks)
- end
-
- def compound_error!(error_messages)
- underlying_errors = error_messages.map { |message| unprocessable(message) }
- raise CloudController::Errors::CompoundError.new(underlying_errors)
- end
-
- def validate_content_type!
- if !request_content_type_is_yaml?
- logger.error("Context-type isn't yaml: #{request.content_type}")
- bad_request!('Content-Type must be yaml')
- end
- end
-
- def request_content_type_is_yaml?
- Mime::Type.lookup(request.content_type) == :yaml
- end
-
- def parsed_app_manifest_params
- parsed_application = parsed_yaml['applications'] && parsed_yaml['applications'].first
-
- raise bad_request!('Invalid app manifest') unless parsed_application.present?
-
- parsed_application
- end
end
diff --git a/app/controllers/v3/apps_controller.rb b/app/controllers/v3/apps_controller.rb
index 408b83fb5f0..015a65e7bca 100644
--- a/app/controllers/v3/apps_controller.rb
+++ b/app/controllers/v3/apps_controller.rb
@@ -20,7 +20,6 @@
require 'messages/update_environment_variables_message'
require 'messages/app_manifest_message'
require 'messages/app_builds_list_message'
-require 'messages/named_app_manifest_message'
require 'presenters/v3/app_presenter'
require 'presenters/v3/app_env_presenter'
require 'presenters/v3/app_environment_variables_presenter'
diff --git a/app/controllers/v3/space_manifests_controller.rb b/app/controllers/v3/space_manifests_controller.rb
index b213925bb2b..678ecff30ff 100644
--- a/app/controllers/v3/space_manifests_controller.rb
+++ b/app/controllers/v3/space_manifests_controller.rb
@@ -1,6 +1,6 @@
require 'presenters/v3/app_manifest_presenter'
require 'repositories/app_event_repository'
-require 'messages/named_app_manifest_message'
+require 'messages/app_manifest_message'
require 'actions/app_find_or_create_skeleton'
require 'actions/app_create'
require 'actions/space_diff_manifest'
@@ -15,7 +15,7 @@ def apply_manifest
space_not_found! unless space && permission_queryer.can_read_from_space?(space.guid, space.organization.guid)
unauthorized! unless permission_queryer.can_write_to_space?(space.guid)
- messages = parsed_app_manifests.map { |app_manifest| NamedAppManifestMessage.create_from_yml(app_manifest) }
+ messages = parsed_app_manifests.map { |app_manifest| AppManifestMessage.create_from_yml(app_manifest) }
errors = messages.each_with_index.flat_map { |message, i| errors_for_message(message, i) }
compound_error!(errors) unless errors.empty?
@@ -49,7 +49,7 @@ def diff_manifest
parsed_manifests = parsed_app_manifests.map(&:to_hash)
- messages = parsed_app_manifests.map { |app_manifest| NamedAppManifestMessage.create_from_yml(app_manifest) }
+ messages = parsed_app_manifests.map { |app_manifest| AppManifestMessage.create_from_yml(app_manifest) }
errors = messages.each_with_index.flat_map { |message, i| errors_for_message(message, i) }
compound_error!(errors) unless errors.empty?
diff --git a/app/jobs/app_apply_manifest_action_job.rb b/app/jobs/app_apply_manifest_action_job.rb
deleted file mode 100644
index adea104e724..00000000000
--- a/app/jobs/app_apply_manifest_action_job.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-module VCAP::CloudController
- module Jobs
- class AppApplyManifestActionJob < VCAP::CloudController::Jobs::CCJob
- def initialize(app_guid, apply_manifest_message, apply_manifest_action)
- @app_guid = app_guid
- @apply_manifest_message = apply_manifest_message
- @apply_manifest_action = apply_manifest_action
- end
-
- def perform
- logger = Steno.logger('cc.background')
- logger.info("Applying app manifest to app: #{resource_guid}")
-
- apply_manifest_action.apply(resource_guid, apply_manifest_message)
- rescue AppPatchEnvironmentVariables::InvalidApp,
- AppUpdate::InvalidApp,
- AppApplyManifest::NoDefaultDomain,
- ProcessCreate::InvalidProcess,
- ProcessScale::InvalidProcess,
- ProcessScale::SidecarMemoryLessThanProcessMemory,
- ProcessUpdate::InvalidProcess,
- SidecarCreate::InvalidSidecar,
- SidecarUpdate::InvalidSidecar,
- ManifestRouteUpdate::InvalidRoute,
- Route::InvalidOrganizationRelation,
- AppApplyManifest::Error,
- AppApplyManifest::ServiceBindingError => e
-
- error = CloudController::Errors::ApiError.new_from_details('UnprocessableEntity', e.message)
- error.set_backtrace(e.backtrace)
- raise error
- end
-
- def job_name_in_configuration
- :apply_manifest_job
- end
-
- def max_attempts
- 1
- end
-
- def resource_type
- 'app'
- end
-
- def display_name
- 'app.apply_manifest'
- end
-
- def resource_guid
- @app_guid
- end
-
- private
-
- attr_reader :apply_manifest_action, :apply_manifest_message
- end
- end
-end
diff --git a/app/messages/app_manifest_message.rb b/app/messages/app_manifest_message.rb
index bc6bd7b5c15..0c2f97aa5ec 100644
--- a/app/messages/app_manifest_message.rb
+++ b/app/messages/app_manifest_message.rb
@@ -25,6 +25,7 @@ class AppManifestMessage < BaseMessage
:instances,
:metadata,
:memory,
+ :name,
:no_route,
:processes,
:random_route,
@@ -53,6 +54,7 @@ def self.underscore_keys(hash)
end
end
+ validates :name, presence: { message: 'must not be empty' }, string: true
validate :validate_top_level_web_process!
validate :validate_processes!, if: ->(record) { record.requested?(:processes) }
validate :validate_sidecars!, if: ->(record) { record.requested?(:sidecars) }
diff --git a/app/messages/named_app_manifest_message.rb b/app/messages/named_app_manifest_message.rb
deleted file mode 100644
index fa5d0898549..00000000000
--- a/app/messages/named_app_manifest_message.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'messages/app_manifest_message'
-require 'messages/validators'
-
-module VCAP::CloudController
- class NamedAppManifestMessage < AppManifestMessage
- register_allowed_keys [:name]
-
- validates :name, presence: { message: 'must not be empty' }, string: true
- end
-end
diff --git a/config/routes.rb b/config/routes.rb
index b5b72683af7..92830e1e4ad 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -27,7 +27,6 @@
get '/apps/:guid/ssh_enabled', to: 'app_features#ssh_enabled'
# app manifests
- post '/apps/:guid/actions/apply_manifest', to: 'app_manifests#apply_manifest'
get '/apps/:guid/manifest', to: 'app_manifests#show'
# app revisions
diff --git a/docs/v3/source/includes/experimental_resources/app_manifest/_apply.md b/docs/v3/source/includes/experimental_resources/app_manifest/_apply.md
deleted file mode 100644
index d432db21191..00000000000
--- a/docs/v3/source/includes/experimental_resources/app_manifest/_apply.md
+++ /dev/null
@@ -1,41 +0,0 @@
-### Apply an app manifest
-
-
-```
-Example Request
-```
-
-```shell
-curl "https://api.example.org/v3/apps/[guid]/actions/apply_manifest" \
- -X POST \
- -H "Authorization: bearer [token]" \
- -H "Content-type: application/x-yaml" \
- --data-binary @/path/to/manifest.yml
-```
-
-```
-Example Response
-```
-
-```http
-HTTP/1.1 202 Accepted
-Location: https://api.example.org/v3/jobs/[guid]
-```
-
-Apply changes specified in a manifest to an app and its underlying processes. These changes are additive and will not modify any unspecified properties or remove any existing environment variables, routes, or services.
-
-
-
-#### Definition
-`POST /v3/apps/:guid/actions/apply_manifest`
-
-#### Permitted Roles
- |
---- | ---
-Admin |
-Space Developer |
diff --git a/docs/v3/source/includes/experimental_resources/app_manifest/_header.md b/docs/v3/source/includes/experimental_resources/app_manifest/_header.md
deleted file mode 100644
index 5551ee5f619..00000000000
--- a/docs/v3/source/includes/experimental_resources/app_manifest/_header.md
+++ /dev/null
@@ -1 +0,0 @@
-## App Manifest
diff --git a/docs/v3/source/includes/experimental_resources/app_manifest/_object.md.erb b/docs/v3/source/includes/experimental_resources/app_manifest/_object.md.erb
deleted file mode 100644
index 34ab6b202f9..00000000000
--- a/docs/v3/source/includes/experimental_resources/app_manifest/_object.md.erb
+++ /dev/null
@@ -1,118 +0,0 @@
-The app manifest is a method for applying bulk configuration to an app and its underlying processes.
-### The app manifest specification
-
-```
-Example Manifest
-```
-
-```yaml
----
-applications:
-- buildpacks:
- - ruby_buildpack
- - java_buildpack
- env:
- VAR1: value1
- VAR2: value2
- routes:
- - route: route.example.com
- - route: another-route.example.com
- services:
- - my-service1
- - my-service2
- - name: my-service-with-arbitrary-params
- parameters:
- key1: value1
- key2: value2
- stack: cflinuxfs3
- metadata:
- annotations:
- contact: "bob@example.com jane@example.com"
- labels:
- sensitive: true
- processes:
- - type: web
- command: start-web.sh
- disk_quota: 512M
- health-check-http-endpoint: /healthcheck
- health-check-type: http
- health-check-invocation-timeout: 10
- instances: 3
- memory: 500M
- timeout: 10
- - type: worker
- command: start-worker.sh
- disk_quota: 1G
- health-check-type: process
- instances: 2
- memory: 256M
- timeout: 15
- sidecars:
- - name: authenticator
- process_types: [ 'web', 'worker' ]
- command: bundle exec run-authenticator
- memory: 800M
- - name: upcaser
- process_types: [ 'worker' ]
- command: ./tr-server
- memory: 900M
-
-```
-
-#### App-level configuration
-This configuration is specified at the top-level and applies to all of the app's processes.
-
-Field | Description
----- | -----------
-**buildpacks** | Must be an Array.
a) An empty array, which will automatically select the appropriate default buildpack according to the coding language.
b) An array of one or more URLs pointing to buildpacks.
c) An array of one or more installed buildpack names.
Replaces the legacy `buildpack` field
-**env** | A key-value hash of environment variables to be used for the app when running
-**no-route** | Boolean value; when set to `true`, any routes specified with the `routes` attribute will be ignored and any existing routes will be removed; ignored if `false`
-**processes** | List of configurations for individual process types, see [_Process-level configuration_](#app-manifest-process-level-configuration)
-**random-route** | Boolean value; creates a random route for the app if `true`; ignored if `false`, if `routes` is specified, if the app already has routes, or if `no-route` is specified
-**default-route** | Boolean value; if true, a route for the app will be created using the app name as the hostname and the containing organization's default domain as the domain. If `false`, if `routes` is specified, if the app already has routes, or if `no-route` is specified, this field is ignored and results in noop
-**routes** | An array of route hashes declaring HTTP and TCP routes to be mapped to the app. Each route is created if it does not already exist. Example route hash entry: `- route: www.examplecom/path`
-**services** | An array of service-instances to bind to the app, see [_Service-level configuration_](#app-manifest-service-level-configuration)
-**sidecars** | An array of configurations for individual sidecars, see [_Sidecar-level configuration_](#app-manifest-sidecar-level-configuration)
-**stack** | The root filesystem to use with the buildpack, for example `cflinuxfs3`
-**metadata.labels** | [Labels](#labels) applied to the app
-**metadata.annotations** | [Annotations](#annotations) applied to the app
-**buildpack** | **DEPRECATED in favor of the `buildpacks` field above**
-
-
-#### Process-level configuration
-This configuration is for the individual process. Each process is created if it does not already exist.
-
-With the exception of `type`, process-level fields can also be provided at the top-level and will apply to the `web` process only.
-
-If there is a process with `type: web` defined in the processes section, then all top level process configuration will be ignored.
-
-Field | Description
----- | -----------
-**type** | **(Required)** Process type, the identifier for the processes to be configured
-**command** | The command used to start the process; this overrides start commands from [Procfiles](#procfiles) and buildpacks
-**disk_quota** | The disk limit for all instances of the web process;
this attribute requires a unit of measurement: `B`, `K`, `KB`, `M`, `MB`, `G`, `GB`, `T`, or `TB` in upper case or lower case
-**health-check-http-endpoint** | Endpoint called to determine if the app is healthy
-**health-check-invocation-timeout** | The timeout in seconds for individual health check requests for http and port health checks
-**health-check-type** | Type of health check to perform; `none` is deprecated and an alias to `process`
-**instances** | The number of instances to run
-**memory** | The memory limit for all instances of the web process;
this attribute requires a unit of measurement: `B`, `K`, `KB`, `M`, `MB`, `G`, `GB`, `T`, or `TB` in upper case or lower case
-**timeout** | Time in seconds at which the health-check will report failure
-
-#### Service-level configuration
-This configuration is _creating_ new service bindings between the app and a service instance. The `services` field can
-take either an array of service instance name strings or an array of the following service-level fields.
-
-Field | Description
----- | -----------
-**name** | **(Required)** Service instance name; the name of the service instance to be bound to
-**parameters** | A map of arbitrary key/value pairs to send to the service broker during binding
-
-#### Sidecar-level configuration
-This configuration is for the individual sidecar. Each sidecar is created if it does not already exist.
-
-Field | Description
----- | -----------
-**name** | **(Required)** Sidecar name; the identifier for the sidecars to be configured
-**command** | The command used to start the sidecar
-**process_types** | List of processes to associate sidecar with
-**memory** | Memory in mb that the sidecar will be allocated
diff --git a/docs/v3/source/includes/experimental_resources/sidecars/_header.md b/docs/v3/source/includes/experimental_resources/sidecars/_header.md
index 02739034500..98f63348706 100644
--- a/docs/v3/source/includes/experimental_resources/sidecars/_header.md
+++ b/docs/v3/source/includes/experimental_resources/sidecars/_header.md
@@ -12,7 +12,7 @@ Sidecars are useful for any app processes that need to communicate with another
#### Steps to create a sidecar
-The recommended way to create sidecars for your app is with an [app manifest](#app-manifest).
+The recommended way to create sidecars for your app is with a [manifest](#manifests).
```yaml
sidecars:
diff --git a/docs/v3/source/index.html.md b/docs/v3/source/index.html.md
index f45fe2b4814..f6728477949 100644
--- a/docs/v3/source/index.html.md
+++ b/docs/v3/source/index.html.md
@@ -342,9 +342,6 @@ includes:
- resources/users/update
- resources/users/delete
- experimental_resources/header
- - experimental_resources/app_manifest/header
- - experimental_resources/app_manifest/object
- - experimental_resources/app_manifest/apply
- experimental_resources/app_restart/header
- experimental_resources/app_restart/create
- experimental_resources/revisions/header
diff --git a/lib/cloud_controller/jobs.rb b/lib/cloud_controller/jobs.rb
index 919a87836e5..763a75cee9f 100644
--- a/lib/cloud_controller/jobs.rb
+++ b/lib/cloud_controller/jobs.rb
@@ -1,6 +1,5 @@
require 'jobs/cc_job'
require 'jobs/delete_action_job'
-require 'jobs/app_apply_manifest_action_job'
require 'jobs/space_apply_manifest_action_job'
require 'jobs/enqueuer'
require 'jobs/queues'
diff --git a/spec/request/app_manifests_spec.rb b/spec/request/app_manifests_spec.rb
index 124eb43eb51..8b5185c8b59 100644
--- a/spec/request/app_manifests_spec.rb
+++ b/spec/request/app_manifests_spec.rb
@@ -18,554 +18,6 @@
TestConfig.override(kubernetes: {})
end
- describe 'POST /v3/apps/:guid/actions/apply_manifest' do
- let(:buildpack) { VCAP::CloudController::Buildpack.make }
- let(:service_instance) { VCAP::CloudController::ManagedServiceInstance.make(space: space) }
- let(:client) { instance_double(VCAP::Services::ServiceBrokers::V2::Client, bind: {}) }
- let(:yml_manifest) do
- {
- 'applications' => [
- { 'name' => 'blah',
- 'instances' => 4,
- 'memory' => '2048MB',
- 'disk_quota' => '1.5GB',
- 'buildpack' => buildpack.name,
- 'stack' => buildpack.stack,
- 'command' => 'new-command',
- 'health_check_type' => 'http',
- 'health_check_http_endpoint' => '/health',
- 'timeout' => 42,
- 'env' => {
- 'k1' => 'mangos',
- 'k2' => 'pears',
- 'k3' => 'watermelon'
- },
- 'metadata' => {
- 'annotations' => {
- 'potato' => 'idaho',
- 'juice' => 'newton',
- 'berry' => nil,
- },
- 'labels' => {
- 'potato' => 'yam',
- 'downton' => nil,
- 'myspace.com/songs' => 'missing',
- },
- },
- 'routes' => [
- { 'route' => "https://#{route.host}.#{route.domain.name}" },
- { 'route' => "https://#{second_route.host}.#{second_route.domain.name}/path" }
- ],
- 'services' => [
- service_instance.name
- ]
- }
- ]
- }.to_yaml
- end
-
- before do
- stub_bind(service_instance)
- VCAP::CloudController::LabelsUpdate.update(app_model, { 'potato' => 'french',
- 'downton' => 'abbey road', }, VCAP::CloudController::AppLabelModel)
- VCAP::CloudController::AnnotationsUpdate.update(app_model, { 'potato' => 'baked',
- 'berry' => 'white', }, VCAP::CloudController::AppAnnotationModel)
- end
-
- it 'applies the manifest' do
- web_process = app_model.web_processes.first
- expect(web_process.instances).to eq(1)
-
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
-
- expect(last_response.status).to eq(202)
- job_guid = VCAP::CloudController::PollableJobModel.last.guid
- expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))
-
- Delayed::Worker.new.work_off
- expect(VCAP::CloudController::PollableJobModel.find(guid: job_guid)).to be_complete
-
- web_process.reload
- expect(web_process.instances).to eq(4)
- expect(web_process.memory).to eq(2048)
- expect(web_process.disk_quota).to eq(1536)
- expect(web_process.command).to eq('new-command')
- expect(web_process.health_check_type).to eq('http')
- expect(web_process.health_check_http_endpoint).to eq('/health')
- expect(web_process.health_check_timeout).to eq(42)
-
- app_model.reload
- lifecycle_data = app_model.lifecycle_data
- expect(lifecycle_data.buildpacks).to include(buildpack.name)
- expect(lifecycle_data.stack).to eq(buildpack.stack)
- expect(app_model.environment_variables).to match(
- 'k1' => 'mangos',
- 'k2' => 'pears',
- 'k3' => 'watermelon'
- )
- expect(app_model.routes).to match_array([route, second_route])
-
- expect(app_model.service_bindings.length).to eq 1
- expect(app_model.service_bindings.first.service_instance).to eq service_instance
- expect(app_model).to have_labels(
- { key: 'potato', value: 'yam' },
- { prefix: 'myspace.com', key: 'songs', value: 'missing' },
- )
- expect(app_model).to have_annotations(
- { key: 'potato', value: 'idaho' },
- { key: 'juice', value: 'newton' },
- )
- end
-
- context 'sidecars' do
- let(:yml_manifest) do
- {
- 'applications' => [
- {
- 'name' => 'blah',
- 'sidecars' => sidecars_attributes
- }
- ]
- }.to_yaml
- end
-
- let(:sidecars_attributes) do
- [
- {
- 'process_types' => ['worker'],
- 'command' => 'bundle exec sidecar_for_web_only',
- 'name' => 'my-sidecar',
- 'memory' => 300,
- }
- ]
- end
-
- it 'creates new sidecars' do
- expect {
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- Delayed::Worker.new.work_off
- }.to change { VCAP::CloudController::SidecarModel.count }.from(0).to(1)
-
- expect(last_response.status).to eq(202)
- sidecar = VCAP::CloudController::SidecarModel.last
- expect(sidecar.name).to eq('my-sidecar')
- expect(sidecar.command).to eq('bundle exec sidecar_for_web_only')
- expect(sidecar.process_types).to eq(['worker'])
- expect(sidecar.memory).to eq(300)
- end
-
- context 'when a sidecar already exists' do
- let!(:sidecar) { VCAP::CloudController::SidecarModel.make(name: 'my-sidecar', app: app_model, command: 'rackup', memory: 200) }
- let!(:sidecar_process_type) { VCAP::CloudController::SidecarProcessTypeModel.make(sidecar: sidecar, type: 'web', app_guid: app_model.guid) }
-
- it 'updates based on name' do
- expect {
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- Delayed::Worker.new.work_off
- }.not_to change { VCAP::CloudController::SidecarModel.count }
-
- expect(last_response.status).to eq(202)
- sidecar.reload
- expect(sidecar.name).to eq('my-sidecar')
- expect(sidecar.command).to eq('bundle exec sidecar_for_web_only')
- expect(sidecar.process_types).to eq(['worker'])
- expect(sidecar.memory).to eq(300)
- end
-
- context 'when sidecar name is not provided' do
- let(:sidecars_attributes) do
- [
- {
- 'process_types' => ['worker'],
- 'command' => 'bundle exec sidecar_for_web_only',
- }
- ]
- end
-
- it 'returns 422' do
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- expect(last_response.status).to eq(422)
- end
- end
- end
- context 'telemetry' do
- it 'should log the required fields when the app manifest is applied' do
- Timecop.freeze do
- expected_json = {
- 'telemetry-source' => 'cloud_controller_ng',
- 'telemetry-time' => Time.now.to_datetime.rfc3339,
- 'apply-manifest' => {
- 'api-version' => 'v3',
- 'app-id' => Digest::SHA256.hexdigest(app_model.guid),
- 'user-id' => Digest::SHA256.hexdigest(user.guid)
- }
- }
- expect_any_instance_of(ActiveSupport::Logger).to receive(:info).with(JSON.generate(expected_json))
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- expect(last_response.status).to eq(202), last_response.body
- end
- end
- end
- end
-
- describe 'scaling proceses when there are existing sidecars' do
- let!(:process) { VCAP::CloudController::ProcessModel.make(app: app_model, type: 'web', memory: 400) }
- let!(:sidecar) { VCAP::CloudController::SidecarModel.make(name: 'my-sidecar', app: app_model, command: 'rackup', memory: 200) }
- let!(:sidecar_process_type) { VCAP::CloudController::SidecarProcessTypeModel.make(sidecar: sidecar, type: 'web', app_guid: app_model.guid) }
-
- context 'when the new process memory is more than the cumulative sidecars memory' do
- let(:yml_manifest) do
- {
- 'applications' => [
- { 'name' => app_model.name,
- 'processes' => [{
- 'type' => 'web',
- 'memory' => '300MB'
- }]
- }
- ]
- }.to_yaml
- end
- it 'returns a 200' do
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- Delayed::Worker.new.work_off
- expect(process.reload.memory).to eq 300
- expect(last_response.status).to eq(202)
- end
- end
- end
-
- context 'yaml anchors' do
- let(:yml_manifest) do
- <<~YML
- ---
- applications:
- - name: blah
- processes:
- - type: web
- memory: &default_value 321M
- disk_quota: *default_value
- YML
- end
-
- it 'does NOT accept yaml with anchors' do
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
-
- expect(last_response.status).to eq(400)
- parsed_response = MultiJson.load(last_response.body)
- expect(parsed_response['errors'].first['detail']).to eq('Bad request: Manifest does not support Anchors and Aliases')
- end
- end
-
- context 'service bindings' do
- it 'returns an appropriate error when fail to bind' do
- allow(VCAP::Services::ServiceClientProvider).to receive(:provide).and_return(client)
- allow(client).to receive(:bind).and_raise('Failed')
-
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- Delayed::Worker.new.work_off
-
- job_location = last_response.headers['Location']
- get job_location, nil, user_header
- parsed_response = MultiJson.load(last_response.body)
-
- expect(VCAP::CloudController::ServiceBinding.count).to eq(0)
- expect(parsed_response['errors'].first['detail']).to eq("For service '#{service_instance.name}': Failed")
- end
- end
-
- describe 'audit events' do
- let!(:process) { nil }
-
- let(:yml_manifest) do
- {
- 'applications' => [
- { 'name' => 'blah',
- 'instances' => 4,
- 'memory' => '2048MB',
- 'disk_quota' => '1.5GB',
- 'buildpack' => buildpack.name,
- 'stack' => buildpack.stack,
- 'command' => 'new-command',
- 'health_check_type' => 'http',
- 'health_check_http_endpoint' => '/health',
- 'timeout' => 42,
- 'env' => {
- 'k1' => 'mangos',
- 'k2' => 'pears',
- 'k3' => 'watermelon'
- },
- 'routes' => [
- { 'route' => "https://#{route.host}.#{route.domain.name}" },
- { 'route' => "https://pants.#{second_route.domain.name}/path" }
- ],
- 'services' => [
- service_instance.name
- ]
- }
- ]
- }.to_yaml
- end
-
- it 'creates audit events tagged with metadata.manifest_triggered' do
- expect {
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- Delayed::Worker.new.work_off
- }.to change { VCAP::CloudController::Event.count }.by 10
-
- manifest_triggered_events = VCAP::CloudController::Event.find_all { |event| event.metadata['manifest_triggered'] }
- expect(manifest_triggered_events.map(&:type)).to match_array([
- 'audit.app.process.update',
- 'audit.app.process.create',
- 'audit.app.process.scale',
- 'audit.app.update',
- 'audit.app.update',
- 'audit.app.map-route',
- 'audit.route.create',
- 'audit.app.map-route',
- 'audit.service_binding.create',
- ])
-
- other_events = VCAP::CloudController::Event.find_all { |event| !event.metadata['manifest_triggered'] }
- expect(other_events.map(&:type)).to eq(['audit.app.apply_manifest',])
- end
-
- context 'when no-route is included and the app has existing routes' do
- let(:yml_manifest) do
- {
- 'applications' => [
- { 'name' => 'blah',
- 'no_route' => true,
- }
- ]
- }.to_yaml
- end
-
- before do
- VCAP::CloudController::RouteMappingModel.make(app: app_model, route: route)
- end
-
- it 'creates audit.app.unmap-route audit events including metadata.manifest_triggered' do
- expect {
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- Delayed::Worker.new.work_off
- }.to change { VCAP::CloudController::Event.count }.by 4
-
- manifest_triggered_events = VCAP::CloudController::Event.find_all { |event| event.metadata['manifest_triggered'] }
- expect(manifest_triggered_events.map(&:type)).to match_array([
- 'audit.app.update',
- 'audit.app.update',
- 'audit.app.unmap-route',
- ])
-
- other_events = VCAP::CloudController::Event.find_all { |event| !event.metadata['manifest_triggered'] }
- expect(other_events.map(&:type)).to eq(['audit.app.apply_manifest',])
- end
- end
- end
-
- describe 'multiple processes' do
- let(:web_process) do
- {
- 'type' => 'web',
- 'instances' => 4,
- 'command' => 'new-command',
- 'memory' => '2048MB',
- 'disk_quota' => '256MB',
- 'health-check-type' => 'http',
- 'health-check-http-endpoint' => '/test',
- 'timeout' => 10,
- }
- end
-
- let(:worker_process) do
- {
- 'type' => 'worker',
- 'instances' => 2,
- 'command' => 'bar',
- 'memory' => '512MB',
- 'disk_quota' => '1024M',
- 'health-check-type' => 'port',
- 'timeout' => 150
- }
- end
-
- let(:yml_manifest) do
- {
- 'applications' => [
- {
- 'name' => 'blah',
- 'processes' => [web_process, worker_process]
- }
- ]
- }.to_yaml
- end
-
- context 'when all the process types already exist' do
- let!(:process2) { VCAP::CloudController::ProcessModel.make(app: app_model, type: 'worker') }
-
- it 'applies the manifest' do
- web_process = app_model.web_processes.first
- expect(web_process.instances).to eq(1)
-
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
-
- expect(last_response.status).to eq(202)
- job_guid = VCAP::CloudController::PollableJobModel.last.guid
- expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))
-
- Delayed::Worker.new.work_off
- background_job = VCAP::CloudController::PollableJobModel.find(guid: job_guid)
- expect(background_job).to be_complete, "Failed due to: #{background_job.cf_api_error}"
-
- web_process.reload
- expect(web_process.instances).to eq(4)
- expect(web_process.memory).to eq(2048)
- expect(web_process.disk_quota).to eq(256)
- expect(web_process.command).to eq('new-command')
- expect(web_process.health_check_type).to eq('http')
- expect(web_process.health_check_http_endpoint).to eq('/test')
- expect(web_process.health_check_timeout).to eq(10)
-
- process2.reload
- expect(process2.instances).to eq(2)
- expect(process2.memory).to eq(512)
- expect(process2.disk_quota).to eq(1024)
- expect(process2.command).to eq('bar')
- expect(process2.health_check_type).to eq('port')
- expect(process2.health_check_timeout).to eq(150)
- end
- end
-
- context 'when some of the process types do NOT exist for the app yet' do
- it 'creates the processes and applies the manifest' do
- web_process = app_model.web_processes.first
- expect(web_process.instances).to eq(1)
-
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
-
- expect(last_response.status).to eq(202)
- job_guid = VCAP::CloudController::PollableJobModel.last.guid
- expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))
-
- Delayed::Worker.new.work_off
- background_job = VCAP::CloudController::PollableJobModel.find(guid: job_guid)
- expect(background_job).to be_complete, "Failed due to: #{background_job.cf_api_error}"
-
- web_process.reload
- expect(web_process.instances).to eq(4)
- expect(web_process.memory).to eq(2048)
- expect(web_process.disk_quota).to eq(256)
- expect(web_process.command).to eq('new-command')
- expect(web_process.health_check_type).to eq('http')
- expect(web_process.health_check_http_endpoint).to eq('/test')
- expect(web_process.health_check_timeout).to eq(10)
-
- process2 = VCAP::CloudController::ProcessModel.find(app_guid: app_model.guid, type: 'worker')
- expect(process2.instances).to eq(2)
- expect(process2.memory).to eq(512)
- expect(process2.disk_quota).to eq(1024)
- expect(process2.command).to eq('bar')
- expect(process2.health_check_type).to eq('port')
- expect(process2.health_check_timeout).to eq(150)
- end
- end
- end
-
- describe 'multiple buildpacks' do
- let(:buildpack) { VCAP::CloudController::Buildpack.make }
- let(:buildpack2) { VCAP::CloudController::Buildpack.make }
- let(:yml_manifest) do
- {
- 'applications' => [
- {
- 'name' => 'blah',
- 'buildpacks' => [buildpack.name, buildpack2.name]
- }
- ]
- }.to_yaml
- end
-
- it 'applies the manifest' do
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
-
- expect(last_response.status).to eq(202)
- job_guid = VCAP::CloudController::PollableJobModel.last.guid
- expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))
-
- Delayed::Worker.new.work_off
- background_job = VCAP::CloudController::PollableJobModel.find(guid: job_guid)
- expect(background_job).to be_complete, "Failed due to: #{background_job.cf_api_error}"
-
- app_model.reload
- lifecycle_data = app_model.lifecycle_data
- expect(lifecycle_data.buildpacks).to eq([buildpack.name, buildpack2.name])
- end
- end
-
- describe 'kpack app' do
- let(:app_model) { VCAP::CloudController::AppModel.make(:kpack, space: space, name: 'blah') }
- let(:yml_manifest) do
- {
- 'applications' => [
- {
- 'name' => 'blah',
- 'buildpacks' => ['packeto-buildpacks/go', 'packeto-buildpacks/nodejs']
- }
- ]
- }.to_yaml
- end
-
- it 'applies the manifest' do
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
-
- expect(last_response.status).to eq(202)
- job_guid = VCAP::CloudController::PollableJobModel.last.guid
- expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))
-
- Delayed::Worker.new.work_off
- background_job = VCAP::CloudController::PollableJobModel.find(guid: job_guid)
- expect(background_job).to be_complete, "Failed due to: #{background_job.cf_api_error}"
-
- app_model.reload
- lifecycle_data = app_model.lifecycle_data
- expect(lifecycle_data.buildpacks).to eq(['packeto-buildpacks/go', 'packeto-buildpacks/nodejs'])
- expect(lifecycle_data).to be_a(VCAP::CloudController::KpackLifecycleDataModel)
- end
- end
-
- context 'when a deployment is in progress' do
- before do
- TestConfig.override(temporary_disable_deployments: false)
- deployment = VCAP::CloudController::DeploymentModelTestFactory.make(
- state: VCAP::CloudController::DeploymentModel::DEPLOYING_STATE,
- app: app_model,
- )
- expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYING_STATE)
- post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)
- expect(last_response.status).to eq(202)
- end
-
- context 'when the manifest attempts to update/scale non-web processes' do
- let(:yml_manifest) do
- { 'applications' =>
- [{ 'name' => 'blah',
- 'processes' => [{ 'type' => 'worker', 'instances' => '3', 'command' => 'echo hi' }]
- }]
- }.to_yaml
- end
-
- it 'succeeds' do
- Delayed::Worker.new.work_off
- job = VCAP::CloudController::PollableJobModel.last
-
- expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job.guid}))
- expect(job.state).to eq('COMPLETE')
- end
- end
- end
- end
-
describe 'GET /v3/apps/:guid/manifest' do
let(:app_model) { VCAP::CloudController::AppModel.make(space: space, environment_variables: { 'one' => 'tomato', 'two' => 'potato' }) }
diff --git a/spec/unit/actions/app_find_or_create_skeleton_spec.rb b/spec/unit/actions/app_find_or_create_skeleton_spec.rb
index 92126318f5b..ebb987edbb1 100644
--- a/spec/unit/actions/app_find_or_create_skeleton_spec.rb
+++ b/spec/unit/actions/app_find_or_create_skeleton_spec.rb
@@ -10,7 +10,7 @@ module VCAP::CloudController
subject(:action) { AppFindOrCreateSkeleton.new(user_audit_info) }
context 'when the app exists' do
- let(:message) { NamedAppManifestMessage.create_from_yml({ name: name }) }
+ let(:message) { AppManifestMessage.create_from_yml({ name: name }) }
let!(:app) { AppModel.make(name: name, space: space) }
it 'returns the existing app' do
@@ -20,7 +20,7 @@ module VCAP::CloudController
context 'when the app does not exist' do
context 'when the app is a buildpack app' do
- let(:message) { NamedAppManifestMessage.create_from_yml({ name: name }) }
+ let(:message) { AppManifestMessage.create_from_yml({ name: name }) }
it 'creates the app' do
app = nil
@@ -34,7 +34,7 @@ module VCAP::CloudController
end
context 'when the app is a docker app' do
- let(:message) { NamedAppManifestMessage.create_from_yml({ name: name, docker: { image: 'my/image' } }) }
+ let(:message) { AppManifestMessage.create_from_yml({ name: name, docker: { image: 'my/image' } }) }
it 'creates the app' do
app = nil
diff --git a/spec/unit/controllers/v3/app_manifests_controller_spec.rb b/spec/unit/controllers/v3/app_manifests_controller_spec.rb
index d90629cbe94..5b12c499cf8 100644
--- a/spec/unit/controllers/v3/app_manifests_controller_spec.rb
+++ b/spec/unit/controllers/v3/app_manifests_controller_spec.rb
@@ -2,608 +2,6 @@
require 'permissions_spec_helper'
RSpec.describe AppManifestsController, type: :controller do
- describe '#apply_manifest' do
- let(:app_model) { VCAP::CloudController::AppModel.make }
- let(:space) { app_model.space }
- let(:org) { space.organization }
- let(:user) { VCAP::CloudController::User.make }
- let(:app_apply_manifest_action) { instance_double(VCAP::CloudController::AppApplyManifest) }
- let(:request_body) { { 'applications' => [{ 'name' => 'blah', 'instances' => 2 }] } }
-
- before do
- set_current_user_as_role(role: 'admin', org: org, space: space, user: user)
- allow(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to receive(:new).and_call_original
- allow(VCAP::CloudController::AppApplyManifest).to receive(:new).and_return(app_apply_manifest_action)
- request.headers['CONTENT_TYPE'] = 'application/x-yaml'
- end
-
- context 'permissions' do
- describe 'authorization' do
- role_to_expected_http_response = {
- 'admin' => 202,
- 'admin_read_only' => 403,
- 'global_auditor' => 403,
- 'space_developer' => 202,
- 'space_manager' => 403,
- 'space_auditor' => 403,
- 'org_manager' => 403,
- 'org_auditor' => 404,
- 'org_billing_manager' => 404,
- }.freeze
-
- role_to_expected_http_response.each do |role, expected_return_value|
- context "as an #{role}" do
- it "returns #{expected_return_value}" do
- set_current_user_as_role(role: role, org: org, space: space, user: user)
-
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(expected_return_value), "role #{role}: expected #{expected_return_value}, got: #{response.status}"
- end
- end
- end
- end
- end
-
- context 'when the request body is invalid' do
- context 'when the yaml is missing an applications array' do
- let(:request_body) { { 'name' => 'blah', 'instances' => 4 } }
-
- it 'returns a 400' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
- expect(response.status).to eq(400)
- end
- end
-
- context 'when the requested applications array is empty' do
- let(:request_body) { { 'applications' => [] } }
-
- it 'returns a 400' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
- expect(response.status).to eq(400)
- end
-
- context 'when the app does not exist' do
- let(:request_body) { { 'applications' => [{ 'name' => 'blah', 'instances' => 1, 'memory' => '4MB' }] } }
-
- it 'returns a 404' do
- post :apply_manifest, params: { guid: 'no-such-app-guid' }, body: request_body.to_yaml, as: :yaml
- expect(response.status).to eq(404)
- end
- end
- end
-
- context 'when specified manifest fails validations' do
- let(:request_body) do
- { 'applications' => [{ 'name' => 'blah', 'instances' => -1, 'memory' => '10NOTaUnit',
- 'command' => '', 'env' => 42,
- 'health-check-http-endpoint' => '/endpoint',
- 'health-check-invocation-timeout' => -22,
- 'health-check-type' => 'foo',
- 'timeout' => -42,
- 'random-route' => -42,
- 'routes' => [{ 'route' => 'garbage' }],
- }] }
- end
-
- it 'returns a 422 and validation errors' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
- expect(response.status).to eq(422)
- errors = parsed_body['errors']
- expect(errors.size).to eq(10)
- expect(errors.map { |h| h.reject { |k, _| k == 'test_mode_info' } }).to match_array([
- {
- 'detail' => 'Process "web": Memory must use a supported unit: B, K, KB, M, MB, G, GB, T, or TB',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Process "web": Instances must be greater than or equal to 0',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Process "web": Command must be between 1 and 4096 characters',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Env must be an object of keys and values',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Process "web": Health check type must be "http" to set a health check HTTP endpoint',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Process "web": Health check type must be "port", "process", or "http"',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Process "web": Health check invocation timeout must be greater than or equal to 1',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Process "web": Timeout must be greater than or equal to 1',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => "The route 'garbage' is not a properly formed URL",
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }, {
- 'detail' => 'Random-route must be a boolean',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }
- ])
- end
- end
-
- context 'when the request payload is not yaml' do
- let(:request_body) { { 'applications' => [{ 'name' => 'blah', 'instances' => 1 }] } }
- before do
- allow(CloudController::Errors::ApiError).to receive(:new_from_details).and_call_original
- request.headers['CONTENT_TYPE'] = 'text/plain'
- end
-
- it 'returns a 400' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml
- expect(response.status).to eq(400)
- # Verify we're getting the InvalidError we're expecting
- expect(CloudController::Errors::ApiError).to have_received(:new_from_details).with('BadRequest', 'Content-Type must be yaml').exactly :once
- end
- end
- end
-
- context 'when the request body includes a buildpack' do
- let!(:php_buildpack) { VCAP::CloudController::Buildpack.make(name: 'php_buildpack') }
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'instances' => 4, 'buildpack' => 'php_buildpack' }] }
- end
-
- it 'sets the buildpack' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.buildpack).to eq 'php_buildpack'
- expect(action).to eq app_apply_manifest_action
- end
- end
-
- context 'and the value of buildpack is \"null\"' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'instances' => 4, 'buildpack' => 'null' }] }
- end
-
- it 'should autodetect the buildpack' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq(1)
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |_, message, _|
- expect(message.app_update_message.buildpack_data.buildpacks).to eq([])
- end
- end
- end
-
- context 'for a docker app' do
- let(:app_model) { VCAP::CloudController::AppModel.make(:docker) }
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'buildpack' => 'php_buildpack' }] }
- end
-
- it 'returns an error' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(422)
- errors = parsed_body['errors']
- expect(errors.size).to eq(1)
- expect(errors.map { |h| h.reject { |k, _| k == 'test_mode_info' } }).to match_array([
- {
- 'detail' => 'Buildpack cannot be configured for a docker lifecycle app.',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }
- ])
- end
- end
- end
-
- context 'when the request body includes a buildpacks' do
- let!(:php_buildpack) { VCAP::CloudController::Buildpack.make(name: 'php_buildpack') }
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'instances' => 4, 'buildpacks' => ['php_buildpack'] }] }
- end
-
- it 'sets the buildpacks' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.buildpacks).to eq ['php_buildpack']
- expect(action).to eq app_apply_manifest_action
- end
- end
-
- context 'for a docker app' do
- let(:app_model) { VCAP::CloudController::AppModel.make(:docker) }
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'buildpacks' => ['php_buildpack'] }] }
- end
-
- it 'returns an error' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(422)
- errors = parsed_body['errors']
- expect(errors.size).to eq(1)
- expect(errors.map { |h| h.reject { |k, _| k == 'test_mode_info' } }).to match_array([
- {
- 'detail' => 'Buildpacks cannot be configured for a docker lifecycle app.',
- 'title' => 'CF-UnprocessableEntity',
- 'code' => 10008
- }
- ])
- end
- end
- end
-
- context 'when the request body includes a stack' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'stack' => 'cflinuxfs3' }] }
- end
-
- it 'sets the stack' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, format: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.stack).to eq 'cflinuxfs3'
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes a command' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'command' => 'run-me.sh' }] }
- end
-
- it 'sets the command' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.command).to eq 'run-me.sh'
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes metadata' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah',
- 'metadata' => {
- 'labels' => {
- 'potato' => 'idaho',
- 'myspace.com/songs' => 'missing',
- },
- 'annotations' => {
- 'potato' => 'yam',
- 'juice' => 'newton',
- },
- },
- }] }.to_yaml
- end
-
- it 'applies the metadata' do
- post :apply_manifest, body: request_body, params: { guid: app_model.guid }
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.app_update_message.labels).to eq({
- potato: 'idaho',
- 'myspace.com/songs': 'missing' })
- expect(message.app_update_message.annotations).to eq({
- potato: 'yam',
- juice: 'newton', })
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes a health-check-type' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'health-check-type' => 'process' }] }
- end
-
- it 'sets the command' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.health_check_type).to eq 'process'
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes a health-check-http-endpoint' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'health-check-http-endpoint' => '/health' }] }
- end
-
- it 'sets the command' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.health_check_http_endpoint).to eq '/health'
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes a health-check-invocation-timeout' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'health-check-invocation-timeout' => 55 }] }
- end
-
- it 'sets the command' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.health_check_invocation_timeout).to eq 55
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes a timeout' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'timeout' => 9001 }] }
- end
-
- it 'sets the command' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.timeout).to eq 9001
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes an environment variable' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'env' => { 'KEY100' => 'banana' } }] }
- end
-
- it 'sets the environment' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.env).to eq({ KEY100: 'banana' })
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes a valid route' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'routes' => [{ 'route' => 'potato.yolo.io' }] }] }
- end
-
- it 'sets the route' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.routes).to eq([{ route: 'potato.yolo.io' }])
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes service-bindings as an array of strings' do
- let(:service_binder) { instance_double(ServiceBindingCreate, :create) }
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'services' => %w/shell exxon/ }]
- }
- end
-
- it 'binds the named services to the app' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.services).to match_array(%w/shell exxon/)
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body includes service-bindings as an array of hashes' do
- let(:service_binder) { instance_double(ServiceBindingCreate, :create) }
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah', 'services' => [
- {
- 'name' => 'has_parameters',
- 'parameters' => {
- 'foo' => 'bar'
- }
- },
- {
- 'name' => 'no_parameters'
- }
- ] }]
- }
- end
-
- it 'binds the named services to the app' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.services).to match_array([
- { name: 'has_parameters', parameters: { foo: 'bar' } },
- { name: 'no_parameters' }
- ])
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- context 'when the request body contains a process' do
- let(:request_body) do
- { 'applications' =>
- [{ 'name' => 'blah',
- 'processes' => [{ 'type' => 'worker', 'instances' => '2', 'command' => 'echo hi' }]
- }]
- }
- end
- it 'sets the instances' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.processes).to include(instances: '2', command: 'echo hi', type: 'worker')
- expect(action).to eq app_apply_manifest_action
- end
- end
- end
-
- it 'successfully scales the app in a background job' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(response.status).to eq(202)
- app_apply_manifest_jobs = Delayed::Job.where(Sequel.lit("handler like '%AppApplyManifest%'"))
- expect(app_apply_manifest_jobs.count).to eq 1
-
- expect(VCAP::CloudController::Jobs::AppApplyManifestActionJob).to have_received(:new) do |app_guid, message, action|
- expect(app_guid).to eq app_model.guid
- expect(message.instances).to eq 2
- expect(action).to eq app_apply_manifest_action
- end
- end
-
- it 'creates a job to track the applying the app manifest and returns it in the location header' do
- set_current_user_as_role(role: 'admin', org: org, space: space, user: user)
-
- expect {
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
- }.to change {
- VCAP::CloudController::PollableJobModel.count
- }.by(1)
-
- job = VCAP::CloudController::PollableJobModel.last
- enqueued_job = Delayed::Job.last
- expect(job.delayed_job_guid).to eq(enqueued_job.guid)
- expect(job.operation).to eq('app.apply_manifest')
- expect(job.state).to eq('PROCESSING')
- expect(job.resource_guid).to eq(app_model.guid)
- expect(job.resource_type).to eq('app')
-
- expect(response.status).to eq(202)
- expect(response.headers['Location']).to include "#{link_prefix}/v3/jobs/#{job.guid}"
- end
-
- describe 'emitting an audit event' do
- let(:app_event_repository) { instance_double(VCAP::CloudController::Repositories::AppEventRepository) }
- let(:request_body) do
- { 'applications' => [{ 'name' => 'blah', 'buildpacks' => ['ruby_buildpack', 'go_buildpack'] }] }
- end
-
- before do
- allow(VCAP::CloudController::Repositories::AppEventRepository).
- to receive(:new).and_return(app_event_repository)
- allow(app_event_repository).to receive(:record_app_apply_manifest)
- end
-
- it 'emits an "App Apply Manifest" audit event' do
- post :apply_manifest, params: { guid: app_model.guid }, body: request_body.to_yaml, as: :yaml
-
- expect(app_event_repository).to have_received(:record_app_apply_manifest).
- with(app_model, app_model.space, instance_of(VCAP::CloudController::UserAuditInfo), request_body.to_yaml)
- end
- end
- end
-
describe '#show' do
let(:app_model) { VCAP::CloudController::AppModel.make }
let(:space) { app_model.space }
diff --git a/spec/unit/jobs/app_apply_manifest_action_job_spec.rb b/spec/unit/jobs/app_apply_manifest_action_job_spec.rb
deleted file mode 100644
index 208db5821bd..00000000000
--- a/spec/unit/jobs/app_apply_manifest_action_job_spec.rb
+++ /dev/null
@@ -1,147 +0,0 @@
-require 'spec_helper'
-
-module VCAP::CloudController
- module Jobs
- RSpec.describe AppApplyManifestActionJob, job_context: :worker do
- let(:user) { User.make(admin: true) }
- let(:apply_manifest_action) { instance_double(AppApplyManifest) }
- let(:app) { AppModel.make(name: Sham.guid) }
- let(:parsed_app_manifest) { AppManifestMessage.create_from_yml({ name: 'blah', instances: 4, routes: [{ route: 'foo.example.com' }] }) }
- subject(:job) { AppApplyManifestActionJob.new(app.guid, parsed_app_manifest, apply_manifest_action) }
-
- before do
- allow(apply_manifest_action).to receive(:apply).and_return([])
- end
-
- it { is_expected.to be_a_valid_job }
-
- it 'knows its job name' do
- expect(job.job_name_in_configuration).to equal(:apply_manifest_job)
- end
-
- it 'calls the apply action' do
- job.perform
-
- expect(apply_manifest_action).to have_received(:apply).with(app.guid, parsed_app_manifest)
- end
-
- context 'when the apply manifest action fails' do
- before do
- allow(apply_manifest_action).to receive(:apply).and_raise(StandardError)
- end
-
- it 'bubbles up the error' do
- expect { job.perform }.to raise_error(StandardError)
- end
- end
-
- context 'when an AppApplyManifest::NoDefaultDomain error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(AppApplyManifest::NoDefaultDomain, 'some sub-action failed!')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /some sub-action failed!/)
- end
- end
-
- context 'when an AppUpdate::InvalidApp error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(AppUpdate::InvalidApp, 'Specified unknown buildpack name')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /unknown buildpack name/)
- end
- end
-
- context 'when a ProcessCreate::InvalidProcess error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(ProcessCreate::InvalidProcess, 'Invalid health check type')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Invalid health check type/)
- end
- end
-
- context 'when a ProcessUpdate::InvalidProcess error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(ProcessUpdate::InvalidProcess, 'Invalid health check type')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Invalid health check type/)
- end
- end
-
- context 'when a SidecarCreate::InvalidSidecar error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(SidecarCreate::InvalidSidecar, 'command presence, command is not present')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /command presence, command is not present/)
- end
- end
-
- context 'when a SidecarUpdate::InvalidSidecar error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(SidecarUpdate::InvalidSidecar, 'Invalid field on sidecar')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Invalid field on sidecar/)
- end
- end
-
- context 'when an AppPatchEnvironmentVariables::InvalidApp error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(AppPatchEnvironmentVariables::InvalidApp, 'Invalid env varz')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Invalid env varz/)
- end
- end
-
- context 'when an ManifestRouteUpdate::InvalidRoute error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(ManifestRouteUpdate::InvalidRoute, 'Invalid route')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Invalid route/)
- end
- end
-
- context 'when a Route::InvalidOrganizationRelation error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(Route::InvalidOrganizationRelation, 'Organization cannot use domain hello.there')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Organization cannot use domain hello\.there/)
- end
- end
-
- context 'when an ServiceBindingCreate::InvalidServiceBinding error occurs' do
- it 'wraps the error in an ApiError' do
- allow(apply_manifest_action).to receive(:apply).and_raise(AppApplyManifest::ServiceBindingError, 'Invalid binding name')
- expect {
- job.perform
- }.to raise_error(CloudController::Errors::ApiError, /Invalid binding name/)
- end
- end
-
- describe '#resource_type' do
- it 'returns a display name' do
- expect(job.resource_type).to eq('app')
- end
- end
-
- describe '#display_name' do
- it 'returns a display name for this action' do
- expect(job.display_name).to eq('app.apply_manifest')
- end
- end
-
- describe '#resource_guid' do
- it 'returns the given app guid' do
- expect(job.resource_guid).to eq(app.guid)
- end
- end
- end
- end
-end
diff --git a/spec/unit/jobs/space_apply_manifest_action_job_spec.rb b/spec/unit/jobs/space_apply_manifest_action_job_spec.rb
index 066aabeee51..f761cf44137 100644
--- a/spec/unit/jobs/space_apply_manifest_action_job_spec.rb
+++ b/spec/unit/jobs/space_apply_manifest_action_job_spec.rb
@@ -11,8 +11,8 @@ module Jobs
let(:app2) { AppModel.make(name: 'cut', space: space) }
let(:app_guid_message_hash) do
{
- app1.guid => NamedAppManifestMessage.create_from_yml({ name: app1.name, instances: 4, routes: [{ route: 'foo.example.com' }] }),
- app2.guid => NamedAppManifestMessage.create_from_yml({ name: app2.name, instances: 5 }),
+ app1.guid => AppManifestMessage.create_from_yml({ name: app1.name, instances: 4, routes: [{ route: 'foo.example.com' }] }),
+ app2.guid => AppManifestMessage.create_from_yml({ name: app2.name, instances: 5 }),
}
end
diff --git a/spec/unit/messages/app_manifest_message_spec.rb b/spec/unit/messages/app_manifest_message_spec.rb
index 8ca0787c525..775abd61e61 100644
--- a/spec/unit/messages/app_manifest_message_spec.rb
+++ b/spec/unit/messages/app_manifest_message_spec.rb
@@ -13,40 +13,52 @@ module VCAP::CloudController
end
end
+ context 'when name is not specified' do
+ let(:params_from_yaml) { { instances: 3, memory: '2G' } }
+
+ it 'is not valid' do
+ message = AppManifestMessage.create_from_yml(params_from_yaml)
+
+ expect(message).to_not be_valid
+ expect(message.errors).to have(2).items
+ expect(message.errors.full_messages[0]).to match(/^Name must not be empty/)
+ end
+ end
+
describe 'memory' do
context 'when memory unit is not part of expected set of values' do
- let(:params_from_yaml) { { memory: '200INVALID' } }
+ let(:params_from_yaml) { { name: 'eugene', memory: '200INVALID' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include(
'Process "web": Memory must use a supported unit: B, K, KB, M, MB, G, GB, T, or TB')
end
end
context 'when memory is not a positive amount' do
- let(:params_from_yaml) { { memory: '-1MB' } }
+ let(:params_from_yaml) { { name: 'eugene', memory: '-1MB' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Memory must be greater than 0MB')
end
end
context 'when memory is in bytes' do
- let(:params_from_yaml) { { memory: '-35B' } }
+ let(:params_from_yaml) { { name: 'eugene', memory: '-35B' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Memory must be greater than 0MB')
end
end
@@ -54,13 +66,13 @@ module VCAP::CloudController
describe 'disk_quota' do
context 'when disk_quota unit is not part of expected set of values' do
- let(:params_from_yaml) { { disk_quota: '200INVALID' } }
+ let(:params_from_yaml) { { name: 'eugene', disk_quota: '200INVALID' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include(
'Process "web": Disk quota must use a supported unit: B, K, KB, M, MB, G, GB, T, or TB'
)
@@ -68,25 +80,25 @@ module VCAP::CloudController
end
context 'when disk_quota is not a positive amount' do
- let(:params_from_yaml) { { disk_quota: '-1MB' } }
+ let(:params_from_yaml) { { name: 'eugene', disk_quota: '-1MB' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Disk quota must be greater than 0MB')
end
end
context 'when disk_quota is not numeric' do
- let(:params_from_yaml) { { disk_quota: 'gerg herscheiser' } }
+ let(:params_from_yaml) { { name: 'eugene', disk_quota: 'gerg herscheiser' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Disk quota is not a number')
end
end
@@ -95,7 +107,7 @@ module VCAP::CloudController
describe 'buildpack' do
context 'when providing a valid buildpack name' do
let(:buildpack) { Buildpack.make }
- let(:params_from_yaml) { { buildpack: buildpack.name } }
+ let(:params_from_yaml) { { name: 'eugene', buildpack: buildpack.name } }
it 'is valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
@@ -105,37 +117,37 @@ module VCAP::CloudController
end
context 'when the buildpack is not a string' do
- let(:params_from_yaml) { { buildpack: 99 } }
+ let(:params_from_yaml) { { name: 'eugene', buildpack: 99 } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Buildpack must be a string')
end
end
context 'when the buildpack has fewer than 0 characters' do
- let(:params_from_yaml) { { buildpack: '' } }
+ let(:params_from_yaml) { { name: 'eugene', buildpack: '' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Buildpack must be between 1 and 4096 characters')
end
end
context 'when the buildpack has more than 4096 characters' do
- let(:params_from_yaml) { { buildpack: 'a' * 4097 } }
+ let(:params_from_yaml) { { name: 'eugene', buildpack: 'a' * 4097 } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Buildpack must be between 1 and 4096 characters')
end
end
@@ -145,7 +157,7 @@ module VCAP::CloudController
context 'when providing valid buildpack names' do
let(:buildpack) { Buildpack.make }
let(:buildpack2) { Buildpack.make }
- let(:params_from_yaml) { { buildpacks: [buildpack.name, buildpack2.name] } }
+ let(:params_from_yaml) { { name: 'eugene', buildpacks: [buildpack.name, buildpack2.name] } }
it 'is valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
@@ -156,33 +168,33 @@ module VCAP::CloudController
context 'when one of the buildpacks is not a string' do
let(:buildpack) { Buildpack.make }
- let(:params_from_yaml) { { buildpacks: [buildpack.name, 99] } }
+ let(:params_from_yaml) { { name: 'eugene', buildpacks: [buildpack.name, 99] } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Buildpacks can only contain strings')
end
end
context 'when both buildpack and buildpacks are requested' do
let(:buildpack) { Buildpack.make }
- let(:params_from_yaml) { { buildpacks: [buildpack.name], buildpack: 'some-buildpack' } }
+ let(:params_from_yaml) { { name: 'eugene', buildpacks: [buildpack.name], buildpack: 'some-buildpack' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Buildpack and Buildpacks fields cannot be used together.')
end
end
end
describe 'docker' do
- let(:params_from_yaml) { { docker: { image: 'my/image' } } }
+ let(:params_from_yaml) { { name: 'eugene', docker: { image: 'my/image' } } }
context 'when docker is enabled' do
before do
@@ -205,7 +217,7 @@ module VCAP::CloudController
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Feature Disabled: I am a banana')
end
end
@@ -213,7 +225,7 @@ module VCAP::CloudController
describe 'stack' do
context 'when providing a valid stack name' do
- let(:params_from_yaml) { { stack: 'cflinuxfs3' } }
+ let(:params_from_yaml) { { name: 'eugene', stack: 'cflinuxfs3' } }
it 'is valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
@@ -224,13 +236,13 @@ module VCAP::CloudController
end
context 'when the stack is not a string' do
- let(:params_from_yaml) { { stack: 99 } }
+ let(:params_from_yaml) { { name: 'eugene', stack: 99 } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Stack must be a string')
end
end
@@ -238,37 +250,37 @@ module VCAP::CloudController
describe 'instances' do
context 'when instances is not an number' do
- let(:params_from_yaml) { { instances: 'silly string thing' } }
+ let(:params_from_yaml) { { name: 'eugene', instances: 'silly string thing' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Instances is not a number')
end
end
context 'when instances is not an integer' do
- let(:params_from_yaml) { { instances: 3.5 } }
+ let(:params_from_yaml) { { name: 'eugene', instances: 3.5 } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Instances must be an integer')
end
end
context 'when instances is not a positive integer' do
- let(:params_from_yaml) { { instances: -1 } }
+ let(:params_from_yaml) { { name: 'eugene', instances: -1 } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Process "web": Instances must be greater than or equal to 0')
end
end
@@ -278,13 +290,14 @@ module VCAP::CloudController
context 'when env is not an object' do
let(:params_from_yaml) do
{
+ name: 'eugene',
env: 'im a non-hash'
}
end
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Env must be an object of keys and values')
end
end
@@ -292,6 +305,7 @@ module VCAP::CloudController
context 'when env has bad keys' do
let(:params_from_yaml) do
{
+ name: 'eugene',
env: {
"": 'null-key',
VCAP_BAD_KEY: 1,
@@ -303,7 +317,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(4)
+ expect(message.errors).to have(4).items
expect(message.errors.full_messages).to match_array([
'Env cannot set PORT',
'Env cannot start with VCAP_',
@@ -316,8 +330,9 @@ module VCAP::CloudController
describe 'routes' do
context 'when all routes are valid' do
let(:params_from_yaml) do
- { routes:
- [
+ {
+ name: 'eugene',
+ routes: [
{ route: 'existing.example.com' },
{ route: 'new.example.com' },
]
@@ -333,7 +348,9 @@ module VCAP::CloudController
context 'when a route uri is invalid' do
let(:params_from_yaml) do
- { routes:
+ {
+ name: 'eugene',
+ routes:
[
{ route: 'blah' },
{ route: 'anotherblah' },
@@ -351,7 +368,7 @@ module VCAP::CloudController
end
context 'when routes are malformed' do
- let(:params_from_yaml) { { routes: ['blah'] } }
+ let(:params_from_yaml) { { name: 'eugene', routes: ['blah'] } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
@@ -362,7 +379,7 @@ module VCAP::CloudController
end
context 'when no-route is specified' do
- let(:params_from_yaml) { { 'no-route' => no_route_val } }
+ let(:params_from_yaml) { { name: 'eugene', 'no-route' => no_route_val } }
context 'when no-route is true' do
let(:no_route_val) { true }
@@ -386,6 +403,7 @@ module VCAP::CloudController
context 'when no-route is true and routes are specified' do
let(:params_from_yaml) do
{
+ name: 'eugene',
no_route: true,
routes:
[
@@ -402,7 +420,7 @@ module VCAP::CloudController
end
context 'when random_route is specified' do
- let(:params_from_yaml) { { 'random_route' => random_route_val } }
+ let(:params_from_yaml) { { name: 'eugene', 'random_route' => random_route_val } }
context 'when random_route is true' do
let(:random_route_val) { true }
@@ -426,6 +444,7 @@ module VCAP::CloudController
context 'when random_route is true and routes are specified' do
let(:params_from_yaml) do
{
+ name: 'eugene',
random_route: true,
routes:
[
@@ -442,7 +461,7 @@ module VCAP::CloudController
end
context 'when default_route is specified' do
- let(:params_from_yaml) { { 'default_route' => default_route_val } }
+ let(:params_from_yaml) { { name: 'eugene', 'default_route' => default_route_val } }
context 'when default_route is true' do
let(:default_route_val) { true }
@@ -466,6 +485,7 @@ module VCAP::CloudController
context 'when default_route is true and routes are specified' do
let(:params_from_yaml) do
{
+ name: 'eugene',
default_route: true,
routes:
[
@@ -486,6 +506,7 @@ module VCAP::CloudController
context 'when services is not an array' do
let(:params_from_yaml) do
{
+ name: 'eugene',
services: 'string'
}
end
@@ -493,7 +514,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Services must be a list of service instances')
end
end
@@ -501,45 +522,45 @@ module VCAP::CloudController
describe 'processes' do
context 'when processes is not an array' do
- let(:params_from_yaml) { { processes: 'string' } }
+ let(:params_from_yaml) { { name: 'eugene', processes: 'string' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Processes must be an array of process configurations')
end
end
context 'when any process does not have a type' do
- let(:params_from_yaml) { { processes: [{ 'instances' => 3 }] } }
+ let(:params_from_yaml) { { name: 'eugene', processes: [{ 'instances' => 3 }] } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('All Processes must specify a type')
end
end
context 'when any process has a blank type' do
- let(:params_from_yaml) { { processes: [{ 'type' => '', 'instances' => 3 }, { 'type' => nil, 'instances' => 2 }] } }
+ let(:params_from_yaml) { { name: 'eugene', processes: [{ 'type' => '', 'instances' => 3 }, { 'type' => nil, 'instances' => 2 }] } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('All Processes must specify a type')
end
end
context 'when any process fails validation' do
- let(:params_from_yaml) { { processes: [{ 'type' => 'totally-a-type', 'instances' => -1, 'timeout' => -5 }] } }
+ let(:params_from_yaml) { { name: 'eugene', processes: [{ 'type' => 'totally-a-type', 'instances' => -1, 'timeout' => -5 }] } }
it 'has the type of the process in the error message' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(2)
+ expect(message.errors).to have(2).items
expect(message.errors.full_messages).to include('Process "totally-a-type": Instances must be greater than or equal to 0')
expect(message.errors.full_messages).to include('Process "totally-a-type": Timeout must be greater than or equal to 1')
end
@@ -572,13 +593,16 @@ module VCAP::CloudController
}
end
let(:params_from_yaml) do
- { processes: [process1, process2] }
+ {
+ name: 'eugene',
+ processes: [process1, process2]
+ }
end
it 'includes the type of the process in the error message' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(16)
+ expect(message.errors).to have(16).items
expect(message.errors.full_messages).to match_array([
'Process "type1": Command must be between 1 and 4096 characters',
'Process "type1": Disk quota must use a supported unit: B, K, KB, M, MB, G, GB, T, or TB',
@@ -602,15 +626,15 @@ module VCAP::CloudController
end
context 'when there is more than one process with the same type' do
- let(:params_from_yaml) { { processes: [{ 'type' => 'foo', 'instances' => 3 }, { 'type' => 'foo', 'instances' => 1 },
- { 'type' => 'bob', 'instances' => 5 }, { 'type' => 'bob', 'instances' => 1 }
+ let(:params_from_yaml) { { name: 'eugene', processes: [{ 'type' => 'foo', 'instances' => 3 }, { 'type' => 'foo', 'instances' => 1 },
+ { 'type' => 'bob', 'instances' => 5 }, { 'type' => 'bob', 'instances' => 1 }
] }
}
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(2)
+ expect(message.errors).to have(2).items
expect(message.errors.full_messages).to include('Process "foo" may only be present once')
expect(message.errors.full_messages).to include('Process "bob" may only be present once')
end
@@ -619,12 +643,12 @@ module VCAP::CloudController
describe 'sidecars' do
context 'when sidecars is not an array' do
- let(:params_from_yaml) { { sidecars: 'string' } }
+ let(:params_from_yaml) { { name: 'eugene', sidecars: 'string' } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Sidecars must be an array of sidecar configurations')
end
end
@@ -632,6 +656,7 @@ module VCAP::CloudController
context 'when sidecars name is empty string' do
let(:params_from_yaml) do
{
+ name: 'eugene',
sidecars: [{ name: '', command: 'rackup', process_types: ['web'] }]
}
end
@@ -639,7 +664,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Sidecar name can\'t be blank')
end
end
@@ -647,6 +672,7 @@ module VCAP::CloudController
context 'when sidecars command is empty string' do
let(:params_from_yaml) do
{
+ name: 'eugene',
sidecars: [{ name: 'my_sidecar', command: '', process_types: ['web'] }]
}
end
@@ -654,7 +680,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Sidecar "my_sidecar": Command can\'t be blank')
end
end
@@ -662,6 +688,7 @@ module VCAP::CloudController
context 'when sidecars name is not supplied' do
let(:params_from_yaml) do
{
+ name: 'eugene',
sidecars: [{ command: 'rackup', process_types: ['web'] }]
}
end
@@ -669,7 +696,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(2)
+ expect(message.errors).to have(2).items
expect(message.errors.full_messages).to include('Sidecar name can\'t be blank')
expect(message.errors.full_messages).to include('Sidecar name must be a string')
end
@@ -678,6 +705,7 @@ module VCAP::CloudController
context 'when sidecars memory is not numeric' do
let(:params_from_yaml) do
{
+ name: 'eugene',
sidecars: [{ command: 'rackup', process_types: ['web'], name: 'sylvester', memory: 'selective' }]
}
end
@@ -685,7 +713,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Sidecar "sylvester": Memory in mb is not a number')
end
end
@@ -693,6 +721,7 @@ module VCAP::CloudController
context 'when the sidecars are valid' do
let(:params_from_yaml) do
{
+ name: 'eugene',
sidecars: [{ command: 'rackup', process_types: ['web'], name: 'sylvester', memory: '38M' },
{ command: 'rackup', process_types: ['web'], name: 'cookie', memory: '2G' },
]
@@ -711,13 +740,14 @@ module VCAP::CloudController
context 'when metadata is not an object' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: 'im a non-hash'
}
end
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Metadata must be an object')
end
end
@@ -725,6 +755,7 @@ module VCAP::CloudController
context 'when metadata.labels is not an object' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: {
labels: 'im a non-hash'
}
@@ -733,7 +764,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors_on(:metadata)).to include("'labels' is not an object")
end
end
@@ -741,6 +772,7 @@ module VCAP::CloudController
context 'when metadata.labels has invalid keys' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: {
labels: { nil => 'value' }
}
@@ -749,7 +781,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors_on(:metadata)).to match_array([
'label key error: key cannot be empty string',
])
@@ -759,6 +791,7 @@ module VCAP::CloudController
context 'when metadata.labels has invalid values' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: {
labels: {
'k1' => 'no spaces or ! allowed',
@@ -769,7 +802,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors_on(:metadata)).to match_array([
"label value error: 'no spaces or ! allowed' contains invalid characters",
])
@@ -779,6 +812,7 @@ module VCAP::CloudController
context 'when metadata.annotations is not an object' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: {
annotations: 'im a non-hash'
}
@@ -787,7 +821,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors_on(:metadata)).to include("'annotations' is not an object")
end
end
@@ -795,6 +829,7 @@ module VCAP::CloudController
context 'when metadata.annotations has invalid keys' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: {
annotations: { 'x' * 1000 => 'value' }
}
@@ -803,7 +838,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors_on(:metadata)).to match_array([
"annotation key error: 'xxxxxxxx...' is greater than 63 characters",
])
@@ -813,6 +848,7 @@ module VCAP::CloudController
context 'when metadata.annotations has invalid values' do
let(:params_from_yaml) do
{
+ name: 'eugene',
metadata: {
annotations: {
'too-large-value' => 'oversize-' + 'x' * 5000
@@ -823,7 +859,7 @@ module VCAP::CloudController
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).to_not be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors_on(:metadata)).to match_array([
"annotation value error: 'oversize...' is greater than 5000 characters"
])
@@ -838,13 +874,13 @@ module VCAP::CloudController
end
let(:buildpack) { Buildpack.make }
- let(:params_from_yaml) { { buildpack: buildpack.name, docker: { image: 'my/image' } } }
+ let(:params_from_yaml) { { name: 'eugene', buildpack: buildpack.name, docker: { image: 'my/image' } } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Cannot specify both buildpack(s) and docker keys')
end
end
@@ -856,13 +892,13 @@ module VCAP::CloudController
let(:buildpack) { Buildpack.make }
let(:buildpack2) { Buildpack.make }
- let(:params_from_yaml) { { buildpacks: [buildpack.name, buildpack2.name], docker: { image: 'my/image' } } }
+ let(:params_from_yaml) { { name: 'eugene', buildpacks: [buildpack.name, buildpack2.name], docker: { image: 'my/image' } } }
it 'is not valid' do
message = AppManifestMessage.create_from_yml(params_from_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(1)
+ expect(message.errors).to have(1).items
expect(message.errors.full_messages).to include('Cannot specify both buildpack(s) and docker keys')
end
end
@@ -871,6 +907,7 @@ module VCAP::CloudController
context 'when there are multiple errors' do
let(:params_from_yaml) do
{
+ name: 'eugene',
instances: -1,
memory: 120,
disk_quota: '-120KB',
@@ -930,10 +967,11 @@ module VCAP::CloudController
context 'with processes' do
let(:parsed_yaml) do
- { name: 'blah', processes: [
- { type: 'web', 'health-check-type': 'port', disk_quota: '23M' },
- { type: 'worker', 'health-check-type': 'port', disk_quota: '23M' },
- ] }
+ {
+ 'name' => 'eugene', name: 'blah', processes: [
+ { type: 'web', 'health-check-type': 'port', disk_quota: '23M' },
+ { type: 'worker', 'health-check-type': 'port', disk_quota: '23M' },
+ ] }
end
it 'converts the processes keys into snake case for all processes' do
@@ -954,10 +992,11 @@ module VCAP::CloudController
context 'with environment variables' do
let(:parsed_yaml) do
- { name: 'blah', env: { ':ENV_VAR' => 'hunter1' }, processes: [
- { type: 'web', env: { ':ENV_VAR' => 'hunter2' } },
- { type: 'worker', env: { ':ENV_VAR' => 'hunter3' } },
- ] }
+ {
+ 'name' => 'eugene', name: 'blah', env: { ':ENV_VAR' => 'hunter1' }, processes: [
+ { type: 'web', env: { ':ENV_VAR' => 'hunter2' } },
+ { type: 'worker', env: { ':ENV_VAR' => 'hunter3' } },
+ ] }
end
it 'does NOT try to underscore them (so they do NOT get lowercased)' do
@@ -977,10 +1016,11 @@ module VCAP::CloudController
context 'with services' do
let(:parsed_yaml) do
- { name: 'blah', services: ['hadoop'], processes: [
- { type: 'web', services: ['greenplumbdb'] },
- { type: 'worker', services: ['riak'] },
- ] }
+ {
+ 'name' => 'eugene', name: 'blah', services: ['hadoop'], processes: [
+ { type: 'web', services: ['greenplumbdb'] },
+ { type: 'worker', services: ['riak'] },
+ ] }
end
it 'does NOT try to underscore the service names (they are strings not hashes)' do
@@ -1129,7 +1169,7 @@ module VCAP::CloudController
describe '#manifest_process_scale_messages' do
context 'from app-level attributes' do
- let(:parsed_yaml) { { 'disk_quota' => '1000GB', 'memory' => '200GB', instances: 5 } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'disk_quota' => '1000GB', 'memory' => '200GB', instances: 5 } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1143,7 +1183,7 @@ module VCAP::CloudController
end
context 'it handles bytes' do
- let(:parsed_yaml) { { 'disk_quota' => '7340032B', 'memory' => '3145728B', instances: 8 } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'disk_quota' => '7340032B', 'memory' => '3145728B', instances: 8 } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1157,7 +1197,7 @@ module VCAP::CloudController
end
context 'it handles exactly 1MB' do
- let(:parsed_yaml) { { 'disk_quota' => '1048576B', 'memory' => '1048576B', instances: 8 } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'disk_quota' => '1048576B', 'memory' => '1048576B', instances: 8 } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1171,13 +1211,13 @@ module VCAP::CloudController
end
context 'it complains about 1MB - 1' do
- let(:parsed_yaml) { { 'disk_quota' => '1048575B', 'memory' => '1048575B', instances: 8 } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'disk_quota' => '1048575B', 'memory' => '1048575B', instances: 8 } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(2)
+ expect(message.errors).to have(2).items
expect(message.errors.full_messages).to match_array([
'Process "web": Memory must be greater than 0MB',
'Process "web": Disk quota must be greater than 0MB'])
@@ -1196,7 +1236,7 @@ module VCAP::CloudController
end
context 'from nested process attributes' do
- let(:parsed_yaml) { { 'processes' => [{ 'type' => 'web', 'disk_quota' => '1000GB', 'memory' => '200GB', instances: 5 }] } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'processes' => [{ 'type' => 'web', 'disk_quota' => '1000GB', 'memory' => '200GB', instances: 5 }] } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1210,7 +1250,7 @@ module VCAP::CloudController
end
context 'it handles bytes' do
- let(:parsed_yaml) { { 'processes' => [{ 'type' => 'web', 'disk_quota' => '7340032B', 'memory' => '3145728B', instances: 8 }] } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'processes' => [{ 'type' => 'web', 'disk_quota' => '7340032B', 'memory' => '3145728B', instances: 8 }] } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1224,7 +1264,7 @@ module VCAP::CloudController
end
context 'it handles exactly 1MB' do
- let(:parsed_yaml) { { 'processes' => [{ 'type' => 'web', 'disk_quota' => '1048576B', 'memory' => '1048576B', instances: 8 }] } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'processes' => [{ 'type' => 'web', 'disk_quota' => '1048576B', 'memory' => '1048576B', instances: 8 }] } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1238,13 +1278,13 @@ module VCAP::CloudController
end
context 'it complains about 1MB - 1' do
- let(:parsed_yaml) { { 'processes' => [{ 'type' => 'web', 'disk_quota' => '1048575B', 'memory' => '1048575B', instances: 8 }] } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'processes' => [{ 'type' => 'web', 'disk_quota' => '1048575B', 'memory' => '1048575B', instances: 8 }] } }
it 'returns a ManifestProcessScaleMessage containing mapped attributes' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
expect(message).not_to be_valid
- expect(message.errors.count).to eq(2)
+ expect(message.errors).to have(2).items
expect(message.errors.full_messages).to match_array([
'Process "web": Memory must be greater than 0MB',
'Process "web": Disk quota must be greater than 0MB'])
@@ -1253,8 +1293,10 @@ module VCAP::CloudController
context 'when processes and app-level process properties are specified' do
context 'there is a web process type on the process level' do
- let(:parsed_yaml) { { 'memory' => '5GB',
- instances: 1,
+ let(:parsed_yaml) { {
+ 'name' => 'eugene',
+ 'memory' => '5GB',
+ 'instances' => 1,
'disk_quota' => '30GB',
'processes' => [{ 'type' => 'web', 'disk_quota' => '1000GB', 'memory' => '200GB', instances: 5 }] }
}
@@ -1272,8 +1314,10 @@ module VCAP::CloudController
end
context 'there is not a web process type on the process level' do
- let(:parsed_yaml) { { 'memory' => '5GB',
- instances: 1,
+ let(:parsed_yaml) { {
+ 'name' => 'eugene',
+ 'memory' => '5GB',
+ 'instances' => 1,
'disk_quota' => '30GB',
'processes' => [{ 'type' => 'worker', 'disk_quota' => '1000GB', 'memory' => '200GB', instances: 5 }] }
}
@@ -1303,6 +1347,7 @@ module VCAP::CloudController
context 'from app-level attributes' do
let(:parsed_yaml) do
{
+ 'name' => 'eugene',
'command' => command,
'health-check-type' => health_check_type,
'health-check-http-endpoint' => health_check_http_endpoint,
@@ -1332,7 +1377,7 @@ module VCAP::CloudController
context 'health checks' do
context 'deprecated health check type none' do
- let(:parsed_yaml) { { "health-check-type": 'none' } }
+ let(:parsed_yaml) { { 'name' => 'eugene', "health-check-type": 'none' } }
it 'is converted to process' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1344,7 +1389,7 @@ module VCAP::CloudController
context 'health check timeout without other health check parameters' do
let(:health_check_timeout) { 10 }
- let(:parsed_yaml) { { "timeout": health_check_timeout } }
+ let(:parsed_yaml) { { name: 'eugene', timeout: health_check_timeout } }
it 'sets the health check timeout in the message' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1356,7 +1401,7 @@ module VCAP::CloudController
context 'health check invocation timeout without other health check parameters' do
let(:health_check_invocation_timeout) { 2493 }
- let(:parsed_yaml) { { "health_check_invocation_timeout": health_check_invocation_timeout } }
+ let(:parsed_yaml) { { name: 'eugene', health_check_invocation_timeout: health_check_invocation_timeout } }
it 'sets the health check timeout in the message' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1367,7 +1412,7 @@ module VCAP::CloudController
end
context 'when health check type is port' do
- let(:parsed_yaml) { { 'health-check-type' => 'port' } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'health-check-type' => 'port' } }
it 'does not set the endpoint' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1388,7 +1433,7 @@ module VCAP::CloudController
end
context 'when the health check endpoint is not specified' do
- let(:parsed_yaml) { { 'health-check-type' => 'http' } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'health-check-type' => 'http' } }
it 'returns nil as the endpoint' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1398,7 +1443,7 @@ module VCAP::CloudController
end
context 'when the health check type is nonsense' do
- let(:parsed_yaml) { { 'health-check-type' => 'nonsense' } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'health-check-type' => 'nonsense' } }
it 'returns the error' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1448,7 +1493,7 @@ module VCAP::CloudController
context 'when no parameters are specified' do
let(:parsed_yaml) do
- {}
+ { 'name' => 'eugene' }
end
it 'does not set a command or health_check_type field' do
@@ -1462,6 +1507,7 @@ module VCAP::CloudController
context 'from nested process attributes' do
let(:parsed_yaml) do
{
+ 'name' => 'eugene',
'processes' => [{
'type' => type,
'command' => command,
@@ -1492,7 +1538,7 @@ module VCAP::CloudController
context 'health checks' do
context 'deprecated health check type none' do
- let(:parsed_yaml) { { "health-check-type": 'none' } }
+ let(:parsed_yaml) { { name: 'eugene', "health-check-type": 'none' } }
it 'is converted to process' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1504,7 +1550,7 @@ module VCAP::CloudController
context 'health check timeout without other health check parameters' do
let(:health_check_timeout) { 10 }
- let(:parsed_yaml) { { "timeout": health_check_timeout } }
+ let(:parsed_yaml) { { name: 'eugene', timeout: health_check_timeout } }
it 'sets the health check timeout in the message' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1515,7 +1561,7 @@ module VCAP::CloudController
end
context 'when health check type is not http and endpoint is not specified' do
- let(:parsed_yaml) { { 'health-check-type' => 'port' } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'health-check-type' => 'port' } }
it 'does not default endpoint to "/"' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1529,7 +1575,7 @@ module VCAP::CloudController
context 'command' do
context 'when command is not requested' do
- let(:parsed_yaml) { { 'processes' => [{ 'type' => 'web' }] } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'processes' => [{ 'type' => 'web' }] } }
it 'does not set the command field in the process update message' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
@@ -1575,7 +1621,9 @@ module VCAP::CloudController
context 'when processes and app-level process properties are specified' do
context 'there is a web process type on the process level' do
- let(:parsed_yaml) { { 'command' => 'ignoreme',
+ let(:parsed_yaml) { {
+ 'name' => 'eugene',
+ 'command' => 'ignoreme',
'health_check_http_endpoint' => '/not-here',
'health_check_type' => 'http',
'timeout' => 5,
@@ -1596,7 +1644,9 @@ module VCAP::CloudController
end
context 'there is not a web process type on the process level' do
- let(:parsed_yaml) { { 'command' => 'ignoreme',
+ let(:parsed_yaml) { {
+ 'name' => 'eugene',
+ 'command' => 'ignoreme',
'health_check_http_endpoint' => '/not-here',
'health_check_type' => 'http',
'timeout' => 5,
@@ -1755,7 +1805,7 @@ module VCAP::CloudController
end
describe '#app_update_environment_variables_message' do
- let(:parsed_yaml) { { 'env' => { 'foo' => 'bar', 'baz' => 4.44444444444, 'qux' => false } } }
+ let(:parsed_yaml) { { 'name' => 'eugene', 'env' => { 'foo' => 'bar', 'baz' => 4.44444444444, 'qux' => false } } }
it 'returns a UpdateEnvironmentVariablesMessage containing the env vars' do
message = AppManifestMessage.create_from_yml(parsed_yaml)
expect(message).to be_valid
@@ -1768,6 +1818,7 @@ module VCAP::CloudController
context 'when no-route value is not a boolean' do
let(:parsed_yaml) do
{
+ 'name' => 'eugene',
'no-route' => 3,
'routes' =>
[
@@ -1785,7 +1836,7 @@ module VCAP::CloudController
context 'when no routes are specified' do
let(:parsed_yaml) do
- {}
+ { 'name' => 'eugene' }
end
it 'does not set the routes in the message' do
diff --git a/spec/unit/messages/named_app_manifest_message_spec.rb b/spec/unit/messages/named_app_manifest_message_spec.rb
deleted file mode 100644
index cc7180733cb..00000000000
--- a/spec/unit/messages/named_app_manifest_message_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-require 'messages/named_app_manifest_message'
-
-module VCAP::CloudController
- RSpec.describe NamedAppManifestMessage do
- describe 'validations' do
- context 'when unexpected keys are requested' do
- let(:params) { { name: 'hawaiian', instances: 3, memory: '2G' } }
-
- it 'is valid' do
- message = NamedAppManifestMessage.create_from_yml(params)
-
- expect(message).to be_valid
- end
- end
-
- context 'when name is not specified' do
- let(:params) { { instances: 3, memory: '2G' } }
-
- it 'is not valid' do
- message = NamedAppManifestMessage.create_from_yml(params)
-
- expect(message).to_not be_valid
- expect(message.errors.full_messages[0]).to match(/^Name must not be empty/)
- end
- end
- end
- end
-end