|
926 | 926 | end |
927 | 927 | end |
928 | 928 |
|
| 929 | + context 'stack state validation on app update with staging' do |
| 930 | + let(:stager) { instance_double(VCAP::CloudController::Diego::Stager, stage: nil) } |
| 931 | + |
| 932 | + before do |
| 933 | + allow_any_instance_of(VCAP::CloudController::Stagers).to receive(:validate_process) |
| 934 | + allow_any_instance_of(VCAP::CloudController::Stagers).to receive(:stager_for_build).and_return(stager) |
| 935 | + VCAP::CloudController::Buildpack.make |
| 936 | + VCAP::CloudController::PackageModel.make(app: process.app, state: VCAP::CloudController::PackageModel::READY_STATE) |
| 937 | + end |
| 938 | + |
| 939 | + context 'when stack is DISABLED' do |
| 940 | + let(:disabled_stack) { VCAP::CloudController::Stack.make(name: 'cflinuxfs2', state: 'DISABLED', description: 'Migrate to cflinuxfs4') } |
| 941 | + let(:update_params) { Oj.dump({ state: 'STARTED' }) } |
| 942 | + |
| 943 | + before do |
| 944 | + process.app.buildpack_lifecycle_data.update(stack: disabled_stack.name) |
| 945 | + process.update(state: 'STOPPED') |
| 946 | + end |
| 947 | + |
| 948 | + it 'returns 422 with stack validation error when starting app' do |
| 949 | + put "/v2/apps/#{process.guid}", update_params, headers_for(user) |
| 950 | + |
| 951 | + expect(last_response.status).to eq(422) |
| 952 | + parsed_response = Oj.load(last_response.body) |
| 953 | + expect(parsed_response['error_code']).to eq('CF-StackValidationFailed') |
| 954 | + expect(parsed_response['description']).to include('disabled') |
| 955 | + expect(parsed_response['description']).to include('cflinuxfs2') |
| 956 | + end |
| 957 | + end |
| 958 | + |
| 959 | + context 'when stack is DEPRECATED' do |
| 960 | + let(:deprecated_stack) { VCAP::CloudController::Stack.make(name: 'cflinuxfs3', state: 'DEPRECATED', description: 'EOL Dec 2025') } |
| 961 | + let(:update_params) { Oj.dump({ state: 'STARTED' }) } |
| 962 | + |
| 963 | + before do |
| 964 | + process.app.buildpack_lifecycle_data.update(stack: deprecated_stack.name) |
| 965 | + process.app.update(droplet_guid: nil) |
| 966 | + process.update(state: 'STOPPED') |
| 967 | + end |
| 968 | + |
| 969 | + it 'allows starting app with deprecation warning' do |
| 970 | + put "/v2/apps/#{process.guid}", update_params, headers_for(user) |
| 971 | + |
| 972 | + expect(last_response.status).to eq(201) |
| 973 | + expect(last_response.headers['X-Cf-Warnings']).to be_present |
| 974 | + decoded_warning = CGI.unescape(last_response.headers['X-Cf-Warnings']) |
| 975 | + expect(decoded_warning).to include('deprecated') |
| 976 | + expect(decoded_warning).to include('EOL Dec 2025') |
| 977 | + end |
| 978 | + end |
| 979 | + end |
| 980 | + |
929 | 981 | context 'when process memory is being decreased and the new memory allocation is lower than memory of associated sidecars' do |
930 | 982 | let!(:process) do |
931 | 983 | VCAP::CloudController::ProcessModelFactory.make( |
@@ -1563,6 +1615,139 @@ def make_actual_lrp(instance_guid:, index:, state:, error:, since:) |
1563 | 1615 | end |
1564 | 1616 | end |
1565 | 1617 | end |
| 1618 | + |
| 1619 | + |
| 1620 | + context 'stack state validation' do |
| 1621 | + let(:process) { VCAP::CloudController::ProcessModelFactory.make(name: 'maria', space: space, diego: true) } |
| 1622 | + let(:stager) { instance_double(VCAP::CloudController::Diego::Stager, stage: nil) } |
| 1623 | + |
| 1624 | + before do |
| 1625 | + allow_any_instance_of(VCAP::CloudController::Stagers).to receive(:validate_process) |
| 1626 | + allow_any_instance_of(VCAP::CloudController::Stagers).to receive(:stager_for_build).and_return(stager) |
| 1627 | + VCAP::CloudController::Buildpack.make |
| 1628 | + end |
| 1629 | + |
| 1630 | + context 'when stack is DISABLED' do |
| 1631 | + let(:disabled_stack) { VCAP::CloudController::Stack.make(name: 'cflinuxfs2', state: 'DISABLED', description: 'Migrate to cflinuxfs4') } |
| 1632 | + |
| 1633 | + before do |
| 1634 | + process.app.buildpack_lifecycle_data.update(stack: disabled_stack.name) |
| 1635 | + end |
| 1636 | + |
| 1637 | + it 'returns 422 with stack validation error' do |
| 1638 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1639 | + |
| 1640 | + expect(last_response.status).to eq(422) |
| 1641 | + parsed_response = Oj.load(last_response.body) |
| 1642 | + expect(parsed_response['error_code']).to eq('CF-StackValidationFailed') |
| 1643 | + expect(parsed_response['description']).to include('disabled') |
| 1644 | + expect(parsed_response['description']).to include('cannot be used for staging') |
| 1645 | + expect(parsed_response['description']).to include('cflinuxfs2') |
| 1646 | + expect(parsed_response['description']).to include('Migrate to cflinuxfs4') |
| 1647 | + end |
| 1648 | + |
| 1649 | + it 'does not expose stack state field in error response' do |
| 1650 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1651 | + |
| 1652 | + parsed_response = Oj.load(last_response.body) |
| 1653 | + expect(parsed_response['entity']).to be_nil |
| 1654 | + expect(parsed_response['error_code']).to eq('CF-StackValidationFailed') |
| 1655 | + end |
| 1656 | + end |
| 1657 | + |
| 1658 | + context 'when stack is RESTRICTED' do |
| 1659 | + let(:restricted_stack) { VCAP::CloudController::Stack.make(name: 'cflinuxfs3', state: 'RESTRICTED', description: 'No new apps') } |
| 1660 | + |
| 1661 | + before do |
| 1662 | + process.app.buildpack_lifecycle_data.update(stack: restricted_stack.name) |
| 1663 | + end |
| 1664 | + |
| 1665 | + context 'for first build' do |
| 1666 | + before do |
| 1667 | + process.app.builds_dataset.destroy |
| 1668 | + end |
| 1669 | + |
| 1670 | + it 'returns 422 with stack validation error' do |
| 1671 | + expect(process.app.builds_dataset.count).to eq(0) |
| 1672 | + |
| 1673 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1674 | + |
| 1675 | + expect(last_response.status).to eq(422) |
| 1676 | + parsed_response = Oj.load(last_response.body) |
| 1677 | + expect(parsed_response['error_code']).to eq('CF-StackValidationFailed') |
| 1678 | + expect(parsed_response['description']).to include('cannot be used for staging new applications') |
| 1679 | + expect(parsed_response['description']).to include('cflinuxfs3') |
| 1680 | + end |
| 1681 | + end |
| 1682 | + |
| 1683 | + context 'for restaging existing app' do |
| 1684 | + before do |
| 1685 | + VCAP::CloudController::BuildModel.make(app: process.app, state: 'STAGED') |
| 1686 | + end |
| 1687 | + |
| 1688 | + it 'allows restaging without errors' do |
| 1689 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1690 | + |
| 1691 | + expect(last_response.status).to eq(201) |
| 1692 | + end |
| 1693 | + |
| 1694 | + it 'does not include warnings header' do |
| 1695 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1696 | + |
| 1697 | + expect(last_response.headers['X-Cf-Warnings']).to be_nil |
| 1698 | + end |
| 1699 | + end |
| 1700 | + end |
| 1701 | + |
| 1702 | + context 'when stack is DEPRECATED' do |
| 1703 | + let(:deprecated_stack) { VCAP::CloudController::Stack.make(name: 'cflinuxfs3', state: 'DEPRECATED', description: 'EOL Dec 2025') } |
| 1704 | + |
| 1705 | + before do |
| 1706 | + process.app.buildpack_lifecycle_data.update(stack: deprecated_stack.name) |
| 1707 | + end |
| 1708 | + |
| 1709 | + it 'allows restaging with success' do |
| 1710 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1711 | + |
| 1712 | + expect(last_response.status).to eq(201) |
| 1713 | + end |
| 1714 | + |
| 1715 | + it 'includes deprecation warning in X-Cf-Warnings header' do |
| 1716 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1717 | + |
| 1718 | + expect(last_response.headers['X-Cf-Warnings']).to be_present |
| 1719 | + decoded_warning = CGI.unescape(last_response.headers['X-Cf-Warnings']) |
| 1720 | + expect(decoded_warning).to include('deprecated') |
| 1721 | + expect(decoded_warning).to include('cflinuxfs3') |
| 1722 | + expect(decoded_warning).to include('EOL Dec 2025') |
| 1723 | + end |
| 1724 | + |
| 1725 | + it 'does not expose stack state field in response body' do |
| 1726 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1727 | + |
| 1728 | + parsed_response = Oj.load(last_response.body) |
| 1729 | + # The response includes process 'state' (STARTED/STOPPED) which is different from stack 'state' |
| 1730 | + # We verify stack state is not exposed by checking it's not in the stack-related fields |
| 1731 | + expect(parsed_response['entity']['stack_guid']).to be_present |
| 1732 | + expect(parsed_response.to_s).not_to match(/DEPRECATED/) |
| 1733 | + end |
| 1734 | + end |
| 1735 | + |
| 1736 | + context 'when stack is ACTIVE' do |
| 1737 | + let(:active_stack) { VCAP::CloudController::Stack.make(name: 'cflinuxfs5', state: 'ACTIVE') } |
| 1738 | + |
| 1739 | + before do |
| 1740 | + process.app.buildpack_lifecycle_data.update(stack: active_stack.name) |
| 1741 | + end |
| 1742 | + |
| 1743 | + it 'allows restaging without warnings' do |
| 1744 | + post "/v2/apps/#{process.guid}/restage", nil, headers_for(user) |
| 1745 | + |
| 1746 | + expect(last_response.status).to eq(201) |
| 1747 | + expect(last_response.headers['X-Cf-Warnings']).to be_nil |
| 1748 | + end |
| 1749 | + end |
| 1750 | + end |
1566 | 1751 | end |
1567 | 1752 |
|
1568 | 1753 | describe 'PUT /v2/apps/:guid/bits' do |
|
0 commit comments