From a64d542d75f1e20948a86d89d2cc1823283f571e Mon Sep 17 00:00:00 2001 From: JonJagger Date: Thu, 19 Feb 2026 11:03:37 +0000 Subject: [PATCH 1/3] Update ulimits for max-file-size and max-memory --- source/server/runner.rb | 34 ++++++++---- .../languages_start_points.manifests.json | 53 ++++++++++--------- test/server/check_test_metrics.rb | 8 +-- test/server/run_csharp_reqnroll_fsize_test.rb | 31 +++++++++++ 4 files changed, 86 insertions(+), 40 deletions(-) create mode 100644 test/server/run_csharp_reqnroll_fsize_test.rb diff --git a/source/server/runner.rb b/source/server/runner.rb index d548d705..446da389 100644 --- a/source/server/runner.rb +++ b/source/server/runner.rb @@ -169,23 +169,23 @@ def docker_run_cyber_dojo_sh_command(id, image_name, container_name) end def ulimits(image_name) - # [1] Some start-points have large dll files, eg C# dotnet - # [2] The nproc --limit is per user across all containers. See + # [0] We could allow cores as binary files are not tar piped out of the container. + # [1] The nproc --limit is per user across all containers. See + # [2] Some start-points have large DLL files # https://docs.docker.com/engine/reference/commandline/run/#set-ulimits-in-container---ulimit # There is no cpu-ulimit. See # https://github.com/cyber-dojo-retired/runner-stateless/issues/2 options = [ - ulimit('core', 0), # no core file - ulimit('fsize', 256 * MB), # file size [1] - ulimit('locks', 1024), # number of file locks - ulimit('nofile', 1024), # number of files - ulimit('nproc', 1024), # number of processes [2] + ulimit('core', 0), # no core file [0] + ulimit('locks', 1024), # number of file locks + ulimit('nofile', 1024), # number of files + ulimit('nproc', 1024), # number of processes [1] ulimit('stack', 16 * MB), # stack size - '--kernel-memory=768m', # limited - '--memory=768m', # max 768MB ram (same swap) + '--kernel-memory=2g', # limited + '--memory=2g', # max 768MB ram (same swap) '--net=none', # no network '--pids-limit=128', # no fork bombs - '--security-opt=no-new-privileges' # no escalation + '--security-opt=no-new-privileges' # no escalation ] # Special handling of clang/clang++'s -fsanitize=address options << if clang?(image_name) @@ -193,6 +193,14 @@ def ulimits(image_name) else ulimit('data', 4 * GB) # data segment size end + + # C# reqnroll creates very large files [2] + options << if csharp_reqnroll?(image_name) + ulimit('fsize', 2048 * GB) # file size + else + ulimit('fsize', 256 * MB) # file size + end + options.join(SPACE) end @@ -205,6 +213,10 @@ def clang?(image_name) image_name.start_with?('ghcr.io/cyber-dojo-languages/clang') end + def csharp_reqnroll?(image_name) + image_name.start_with?('ghcr.io/cyber-dojo-languages/csharp_reqnroll') + end + # - - - - - - - - - - - - - - - - - - - - - - # temporary file systems # - - - - - - - - - - - - - - - - - - - - - - @@ -226,7 +238,7 @@ def clang?(image_name) # eg, C#'s "dotnet restore" # - - - - - - - - - - - - - - - - - - - - - - - TMP_FS_SANDBOX_DIR = "--tmpfs #{Sandbox::DIR}:exec,size=250M,uid=#{UID},gid=#{GID}".freeze + TMP_FS_SANDBOX_DIR = "--tmpfs #{Sandbox::DIR}:exec,size=250G,uid=#{UID},gid=#{GID}".freeze TMP_FS_TMP_DIR = '--tmpfs /tmp:exec,size=250M,mode=1777'.freeze # Set /tmp sticky-bit def utf8_clean(result) diff --git a/test/data/languages_start_points.manifests.json b/test/data/languages_start_points.manifests.json index caa330d0..84818869 100644 --- a/test/data/languages_start_points.manifests.json +++ b/test/data/languages_start_points.manifests.json @@ -300,43 +300,46 @@ } } }, - "C#, Moq": { - "display_name": "C#, Moq", + "C# 10.0.103, NUnit 4.3.2": { + "display_name": "C# 10.0.103, NUnit 4.3.2", "filename_extension": [ ".cs" ], - "image_name": "cyberdojofoundation/csharp_moq:368990c", - "max_seconds": 10, + "image_name": "ghcr.io/cyber-dojo-languages/csharp_nunit:80e3fae", + "max_seconds": 15, "tab_size": 4, "visible_files": { - "HikerTest.cs": { - "content": "using Moq;\nusing NUnit.Framework;\n\n[TestFixture]\npublic class HikerTest\n{\n [Test]\n public void life_the_universe_and_everything()\n {\n var arthur = new Mock();\n arthur.Setup((foo => foo.Answer())).Returns(6 * 9);\n\n // a simple example to start you off\n Assert.AreEqual(42, arthur.Object.Answer());\n }\n}\n" + "Hiker.cs": { + "content": "public class Hiker\n{\n public static int Answer()\n {\n return 6 * 9;\n }\n}\n" }, - "IHiker.cs": { - "content": "\npublic interface IHiker\n{\n int Answer();\n}\n" + "HikerTest.cs": { + "content": "using NUnit.Framework;\n\npublic class HikerTest\n{\n [Test]\n public void life_the_universe_and_everything()\n {\n // a simple example to start you off\n Assert.That(Hiker.Answer(), Is.EqualTo(42));\n }\n}\n" }, "cyber-dojo.sh": { - "content": "\ntrap tidy_up EXIT\nfunction tidy_up()\n{\n # cyber-dojo returns text files under /sandbox that are\n # created/deleted/changed. In here you can remove any\n # such files you don't want returned to the browser.\n [ ! -f TestResult.xml ] || rm TestResult.xml\n}\n\nMOQ_PATH=/moq/Moq.4.7.99/lib/net45\nCASTLE_PATH=/moq/Castle.Core.4.1.1/lib/net45\nNUNIT_PATH=/moq/lib/net45\n\nexport MONO_PATH=${MOQ_PATH}:${CASTLE_PATH}:${NUNIT_PATH}\n\nmcs -t:library \\\n -r:${MOQ_PATH}/Moq.dll \\\n -r:${CASTLE_PATH}/Castle.Core.dll \\\n -r:${NUNIT_PATH}/nunit.framework.dll \\\n -out:RunTests.dll *.cs\n\nif [ $? -eq 0 ]; then\n NUNIT_RUNNERS_PATH=/moq/tools\n mono ${NUNIT_RUNNERS_PATH}/nunit3-console.exe --noheader ./RunTests.dll\nfi\n" + "content": "# --------------------------------------------------------------\n# Text files under /sandbox are automatically returned...\nsource ~/cyber_dojo_fs_cleaners.sh\n\nfunction cyber_dojo_enter()\n{\n : # 1. Only return _newly_ generated reports.\n #cyber_dojo_reset_dirs ${...}\n}\nfunction cyber_dojo_exit()\n{\n : # 2. Remove text files we don't want returned.\n cyber_dojo_delete_dirs /sandbox/bin \n cyber_dojo_delete_dirs /sandbox/obj\n #cyber_dojo_delete_files ...\n}\ncyber_dojo_enter\ntrap cyber_dojo_exit EXIT SIGTERM\n\ndotnet restore --source /home/sandbox/.nuget/packages/\ndotnet test --no-restore\n" + }, + "dojo.csproj": { + "content": "\n\n \n net10.0\n latest\n enable\n enable\n false\n \n\n \n \n \n \n \n \n \n\n \n \n \n\n" } } }, - "C#, NUnit": { - "display_name": "C#, NUnit", + "C#, Moq": { + "display_name": "C#, Moq", "filename_extension": [ ".cs" ], - "image_name": "cyberdojofoundation/csharp_nunit:1452bb7", + "image_name": "cyberdojofoundation/csharp_moq:368990c", "max_seconds": 10, "tab_size": 4, "visible_files": { - "Hiker.cs": { - "content": "public class Hiker\n{\n public static int Answer()\n {\n return 6 * 9;\n }\n}\n" - }, "HikerTest.cs": { - "content": "using NUnit.Framework;\n\n[TestFixture]\npublic class HikerTest\n{\n [Test]\n public void life_the_universe_and_everything()\n {\n // a simple example to start you off\n Assert.AreEqual(42, Hiker.Answer());\n }\n}\n" + "content": "using Moq;\nusing NUnit.Framework;\n\n[TestFixture]\npublic class HikerTest\n{\n [Test]\n public void life_the_universe_and_everything()\n {\n var arthur = new Mock();\n arthur.Setup((foo => foo.Answer())).Returns(6 * 9);\n\n // a simple example to start you off\n Assert.AreEqual(42, arthur.Object.Answer());\n }\n}\n" + }, + "IHiker.cs": { + "content": "\npublic interface IHiker\n{\n int Answer();\n}\n" }, "cyber-dojo.sh": { - "content": "\ntrap tidy_up EXIT\nfunction tidy_up()\n{\n # cyber-dojo returns text files under /sandbox that are\n # created/deleted/changed. In here you can remove any\n # such files you don't want returned to the browser.\n [ ! -f TestResult.xml ] || rm TestResult.xml\n}\n\nNUNIT_PATH=/nunit/lib/net45\nexport MONO_PATH=${NUNIT_PATH}\n\nmcs -t:library \\\n -r:${NUNIT_PATH}/nunit.framework.dll \\\n -out:RunTests.dll *.cs\n\nif [ $? -eq 0 ]; then\n NUNIT_RUNNERS_PATH=/nunit/tools\n mono ${NUNIT_RUNNERS_PATH}/nunit3-console.exe --noheader ./RunTests.dll\nfi\n" + "content": "\ntrap tidy_up EXIT\nfunction tidy_up()\n{\n # cyber-dojo returns text files under /sandbox that are\n # created/deleted/changed. In here you can remove any\n # such files you don't want returned to the browser.\n [ ! -f TestResult.xml ] || rm TestResult.xml\n}\n\nMOQ_PATH=/moq/Moq.4.7.99/lib/net45\nCASTLE_PATH=/moq/Castle.Core.4.1.1/lib/net45\nNUNIT_PATH=/moq/lib/net45\n\nexport MONO_PATH=${MOQ_PATH}:${CASTLE_PATH}:${NUNIT_PATH}\n\nmcs -t:library \\\n -r:${MOQ_PATH}/Moq.dll \\\n -r:${CASTLE_PATH}/Castle.Core.dll \\\n -r:${NUNIT_PATH}/nunit.framework.dll \\\n -out:RunTests.dll *.cs\n\nif [ $? -eq 0 ]; then\n NUNIT_RUNNERS_PATH=/moq/tools\n mono ${NUNIT_RUNNERS_PATH}/nunit3-console.exe --noheader ./RunTests.dll\nfi\n" } } }, @@ -676,8 +679,8 @@ } } }, - "C++ (g++ 15.2.0), Cucumber-cpp 0.8.0": { - "display_name": "C++ (g++ 15.2.0), Cucumber-cpp 0.8.0", + "C++ (g++ 15.2.0), Cucumber 0.8.0": { + "display_name": "C++ (g++ 15.2.0), Cucumber 0.8.0", "filename_extension": [ ".cpp", ".hpp", @@ -935,12 +938,12 @@ } } }, - "D, unittest": { - "display_name": "D, unittest", + "D 13.3.0, unittest": { + "display_name": "D 13.3.0, unittest", "filename_extension": [ ".d" ], - "image_name": "cyberdojofoundation/d_unittest:c125c91", + "image_name": "ghcr.io/cyber-dojo-languages/d_unittest:8cbc812", "max_seconds": 10, "tab_size": 4, "visible_files": { @@ -1027,13 +1030,13 @@ } } }, - "Fortran, FUnit": { - "display_name": "Fortran, FUnit", + "Fortran 15.2.0, FUnit 0.12.4": { + "display_name": "Fortran 15.2.0, FUnit 0.12.4", "filename_extension": [ ".f90", ".fun" ], - "image_name": "cyberdojofoundation/fortran_funit:f13f8ad", + "image_name": "ghcr.io/cyber-dojo-languages/fortran_funit:b659ce9", "max_seconds": 10, "tab_size": 4, "visible_files": { diff --git a/test/server/check_test_metrics.rb b/test/server/check_test_metrics.rb index ac4f933a..f3338f9e 100644 --- a/test/server/check_test_metrics.rb +++ b/test/server/check_test_metrics.rb @@ -25,21 +25,21 @@ def table_data [ [ nil ], - [ 'test.count', stats['test_count'], '>=', 101 ], + [ 'test.count', stats['test_count'], '>=', 102 ], [ 'test.duration', stats['total_time'], '<=', 10 ], [ nil ], [ 'test.failures', stats['failure_count'], '<=', 0 ], [ 'test.errors', stats['error_count' ], '<=', 0 ], [ 'test.skips', stats['skip_count' ], '<=', 0 ], [ nil ], - [ 'test.lines.total', test_cov['lines' ]['total' ], '<=', 936 ], + [ 'test.lines.total', test_cov['lines' ]['total' ], '<=', 954 ], [ 'test.lines.missed', test_cov['lines' ]['missed'], '<=', 0 ], [ 'test.branches.total', test_cov['branches']['total' ], '<=', 0 ], [ 'test.branches.missed', test_cov['branches']['missed'], '<=', 0 ], [ nil ], - [ 'code.lines.total', code_cov['lines' ]['total' ], '<=', 549 ], + [ 'code.lines.total', code_cov['lines' ]['total' ], '<=', 554 ], [ 'code.lines.missed', code_cov['lines' ]['missed'], '<=', 0 ], - [ 'code.branches.total', code_cov['branches']['total' ], '<=', 66 ], + [ 'code.branches.total', code_cov['branches']['total' ], '<=', 68 ], [ 'code.branches.missed', code_cov['branches']['missed'], '<=', 0 ], ] end diff --git a/test/server/run_csharp_reqnroll_fsize_test.rb b/test/server/run_csharp_reqnroll_fsize_test.rb new file mode 100644 index 00000000..70182a1d --- /dev/null +++ b/test/server/run_csharp_reqnroll_fsize_test.rb @@ -0,0 +1,31 @@ +require_relative '../test_base' + +class RunCSharpReqnRollFSizeTest < TestBase + + test 'Ea3d93', %w[ + csharp_reqnroll image requires very large ulimit file-size + ] do + stdout_tgz = TGZ.of({ 'stderr' => 'any' }) + set_context( + logger: StdoutLoggerSpy.new, + piper: piper = PipeMakerStub.new(stdout_tgz), + process: process = ProcessSpawnerStub.new + ) + image_name = "ghcr.io/cyber-dojo-languages/csharp_reqnroll:1234567" + puller.add(image_name) + command = nil + process.spawn { |cmd| command = cmd } + process.detach { ThreadValueStub.new(42) } + process.kill {} + + run_cyber_dojo_sh({ :image_name => image_name }) + + name = 'fsize' + limit = 2048 * GB + assert command.include?("--ulimit #{name}=#{limit}"), command + end +end + +KB = 1024 +MB = 1024 * KB +GB = 1024 * MB From e6e0113e1e637170db0bbf9b975ed769fde3d770 Mon Sep 17 00:00:00 2001 From: JonJagger Date: Thu, 19 Feb 2026 11:04:45 +0000 Subject: [PATCH 2/3] use --template-file flag on kosli-create-flow so it sets template in Kosli Flow settings page --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fbad4c0e..838a33e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -71,12 +71,12 @@ jobs: run: kosli create flow "${KOSLI_FLOW}" --description="Test runner" + --template-file=.kosli.yml - name: Begin Kosli Trail if: ${{ github.ref == 'refs/heads/main' }} run: kosli begin trail "${KOSLI_TRAIL}" - --template-file=.kosli.yml - name: Write Trail URL to GitHub Step Summary if: ${{ github.ref == 'refs/heads/main' }} From a289ee9b23aa1892d7d2f35dbe6f98741bc6895b Mon Sep 17 00:00:00 2001 From: JonJagger Date: Thu, 19 Feb 2026 11:05:50 +0000 Subject: [PATCH 3/3] Deploy to aws-beta regardless of snyk results --- .github/workflows/main.yml | 1 - .kosli.yml | 3 --- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 838a33e4..8ae1ab31 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -326,7 +326,6 @@ jobs: - rubocop-lint - unit-tests - integration-tests - - snyk-code-scan env: KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.digest }} steps: diff --git a/.kosli.yml b/.kosli.yml index 83f4d126..00b4544e 100644 --- a/.kosli.yml +++ b/.kosli.yml @@ -23,6 +23,3 @@ trail: type: custom:test-metrics - name: integration-test-coverage-metrics type: custom:coverage-metrics - - - name: snyk-code-scan - type: snyk