diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml new file mode 100644 index 0000000..9a0e29c --- /dev/null +++ b/.github/workflows/pr_tests.yml @@ -0,0 +1,31 @@ +name: Spec tests + +on: + push: + branches: + - main + + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + run_test_suite: + name: Run test suite + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Earthly + uses: earthly/actions-setup@v1 + with: + version: 0.8.14 + + - name: Run tests + run: | + earthly config global.buildkit_max_parallelism 1 + earthly -P ./test+test-autoinstall + earthly -P ./test+test diff --git a/test/Earthfile b/test/Earthfile index d3cffcd..8c6fa17 100644 --- a/test/Earthfile +++ b/test/Earthfile @@ -84,6 +84,7 @@ test-autoinstall: -e AUTO_RUN_INSTALLER=true \ -v /tmp/sim:/run/sim \ deskpro/docker-product-base:test web \ + && docker exec test /bin/sh -c '/usr/local/bin/is-ready --check-tasks --wait --timeout 60 -v' \ && docker exec test /bin/sh -c 'cd /test/serverspec && rspec spec/scenarios/autoinstall/01_installer_spec.rb' \ && docker stop test \ # second run - verify that the installer doesn't run again @@ -105,6 +106,7 @@ test-automigrations: -e AUTO_RUN_MIGRATIONS=true \ -v /tmp/sim:/run/sim \ deskpro/docker-product-base:test web \ + && docker exec test /bin/sh -c '/usr/local/bin/is-ready --check-tasks --wait --timeout 60 -v' \ && docker exec test /bin/sh -c 'cd /test/serverspec && rspec spec/scenarios/automigrations/01_migrations_spec.rb' \ && docker stop test \ # second run - verify that migrations doesn't run again diff --git a/test/serverspec/spec/always/paths_spec.rb b/test/serverspec/spec/always/paths_spec.rb index 969f097..b60eeb6 100644 --- a/test/serverspec/spec/always/paths_spec.rb +++ b/test/serverspec/spec/always/paths_spec.rb @@ -1,101 +1,210 @@ require 'spec_helper' -describe file('/etc/php/8.3') do - it { should exist } - it { should be_directory } +describe "Default file checks" do + before(:all) do + system('/usr/local/bin/is-ready --check-entrypoint --check-tasks --wait --timeout 60 -v') or raise "is-ready failed" + end + + describe file('/etc/php/8.3') do + it { should exist } + it { should be_directory } + end + + describe file('/usr/local/share/deskpro/container-var-reference.json') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + its(:content_as_json) { should_not be_empty } + end + + describe file('/usr/local/share/deskpro/container-public-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-private-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-setenv-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/phpinfo.php') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/var/log/nginx') do + it { should be_directory } + it { should be_owned_by 'nginx' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/var/log/php') do + it { should be_directory } + it { should be_owned_by 'dp_app' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/var/log/deskpro') do + it { should be_directory } + it { should be_owned_by 'dp_app' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/var/lib/vector') do + it { should be_directory } + it { should be_owned_by 'vector' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/srv/deskpro/INSTANCE_DATA') do + it { should be_directory } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should_not be_writable.by('others') } + end + + describe file('/srv/deskpro/INSTANCE_DATA/deskpro-config.d') do + it { should be_directory } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should_not be_writable.by('others') } + end + + describe file('/srv/deskpro/services/messenger-api/.env') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-var-reference.json') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + its(:content_as_json) { should_not be_empty } + end + + describe file('/usr/local/share/deskpro/container-public-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-private-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-setenv-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/container-var-list') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/usr/local/share/deskpro/phpinfo.php') do + it { should exist } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end + + describe file('/var/log/nginx') do + it { should be_directory } + it { should be_owned_by 'nginx' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/var/log/php') do + it { should be_directory } + it { should be_owned_by 'dp_app' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/var/log/deskpro') do + it { should be_directory } + it { should be_owned_by 'dp_app' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/var/lib/vector') do + it { should be_directory } + it { should be_owned_by 'vector' } + it { should be_grouped_into 'adm' } + + it { should be_readable.by('owner') } + it { should be_readable.by('group') } + it { should be_writable.by('owner') } + it { should_not be_writable.by('others') } + end + + describe file('/srv/deskpro/INSTANCE_DATA') do + it { should be_directory } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should_not be_writable.by('others') } + end + + describe file('/srv/deskpro/INSTANCE_DATA/deskpro-config.d') do + it { should be_directory } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should_not be_writable.by('others') } + end end -describe file('/usr/local/share/deskpro/container-var-reference.json') do - it { should exist } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } - its(:content_as_json) { should_not be_empty } -end - -describe file('/usr/local/share/deskpro/container-public-var-list') do - it { should exist } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } -end - -describe file('/usr/local/share/deskpro/container-private-var-list') do - it { should exist } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } -end - -describe file('/usr/local/share/deskpro/container-setenv-var-list') do - it { should exist } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } -end - -describe file('/usr/local/share/deskpro/container-var-list') do - it { should exist } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } -end - -describe file('/usr/local/share/deskpro/phpinfo.php') do - it { should exist } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } -end - -describe file('/var/log/nginx') do - it { should be_directory } - it { should be_owned_by 'nginx' } - it { should be_grouped_into 'adm' } - - it { should be_readable.by('owner') } - it { should be_readable.by('group') } - it { should be_writable.by('owner') } - it { should_not be_writable.by('others') } -end - -describe file('/var/log/php') do - it { should be_directory } - it { should be_owned_by 'dp_app' } - it { should be_grouped_into 'adm' } - - it { should be_readable.by('owner') } - it { should be_readable.by('group') } - it { should be_writable.by('owner') } - it { should_not be_writable.by('others') } -end - -describe file('/var/log/deskpro') do - it { should be_directory } - it { should be_owned_by 'dp_app' } - it { should be_grouped_into 'adm' } - - it { should be_readable.by('owner') } - it { should be_readable.by('group') } - it { should be_writable.by('owner') } - it { should_not be_writable.by('others') } -end -describe file('/var/lib/vector') do - it { should be_directory } - it { should be_owned_by 'vector' } - it { should be_grouped_into 'adm' } - - it { should be_readable.by('owner') } - it { should be_readable.by('group') } - it { should be_writable.by('owner') } - it { should_not be_writable.by('others') } -end - -describe file('/srv/deskpro/INSTANCE_DATA') do - it { should be_directory } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } - it { should_not be_writable.by('others') } -end - -describe file('/srv/deskpro/INSTANCE_DATA/deskpro-config.d') do - it { should be_directory } - it { should be_owned_by 'root' } - it { should be_grouped_into 'root' } - it { should_not be_writable.by('others') } -end diff --git a/test/serverspec/spec/default_web/utils/is-ready_spec.rb b/test/serverspec/spec/default_web/utils/is-ready_spec.rb index 7260aff..f50046d 100644 --- a/test/serverspec/spec/default_web/utils/is-ready_spec.rb +++ b/test/serverspec/spec/default_web/utils/is-ready_spec.rb @@ -10,6 +10,7 @@ FileUtils.touch('/run/container-ready') FileUtils.remove_file('/run/container-running-installer', true) FileUtils.remove_file('/run/container-running-migrations', true) + FileUtils.remove_file('/run/container-running-entrypoint', true) end it "Blocks when using --wait", :slow do @@ -87,10 +88,24 @@ expect(exit_code).to eq 0 end - it "Post-boot tasks matter with --check-tasks" do + it "Post-boot migration tasks matter with --check-tasks" do FileUtils.touch('/run/container-running-migrations') system('is-ready --check-tasks') exit_code = $?.exitstatus expect(exit_code).to eq 1 end + + it "Post-boot installer tasks matter with --check-tasks" do + FileUtils.touch('/run/container-running-installer') + system('is-ready --check-tasks') + exit_code = $?.exitstatus + expect(exit_code).to eq 1 + end + + it "Post-boot entrypoint tasks matter with --check-tasks" do + FileUtils.touch('/run/container-running-entrypoint') + system('is-ready --check-tasks') + exit_code = $?.exitstatus + expect(exit_code).to eq 1 + end end diff --git a/usr/local/bin/is-ready b/usr/local/bin/is-ready index eecddbf..d212929 100755 --- a/usr/local/bin/is-ready +++ b/usr/local/bin/is-ready @@ -65,7 +65,13 @@ check_ready() { return 1 fi + if [ "$check_tasks" = "1" ]; then + if [ -f "/run/container-running-entrypoint" ]; then + output "Entrypoint initialisation is running" + return 1 + fi + if [ -f "/run/container-running-installer" ]; then output "Installer is running" return 1 diff --git a/usr/local/sbin/entrypoint.sh b/usr/local/sbin/entrypoint.sh index d077c3a..ed69c19 100755 --- a/usr/local/sbin/entrypoint.sh +++ b/usr/local/sbin/entrypoint.sh @@ -56,6 +56,10 @@ else fi main() { + # store the fact that we're running the entrypoint + date -u +"%Y-%m-%dT%H:%M:%SZ" >> /run/container-running-entrypoint + chmod 0644 /run/container-running-entrypoint + # remove sentinel files that may be set from previous boots # (normally set in container-ready.sh - we want to remove them here, early, because they are used in healthcheck) rm -f /run/container-ready /run/container-running-installer /run/container-running-migrations @@ -139,6 +143,9 @@ main() { date -u +"%Y-%m-%dT%H:%M:%SZ" >> /run/container-booted chmod 0644 /run/container-booted + # remove the sentinel file that indicates we're running the entrypoint + rm -f /run/container-running-entrypoint + case "$l_docker_exec" in exec) boot_log_message INFO "Starting services"