CoolFace
Apppublic

sanket3280/code-execution

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
dockerExecutor.js243 linesDownload Raw Back to execution
1const { exec } = require('child_process');2const fs = require('fs').promises;3const path = require('path');4const crypto = require('crypto');5const executionLogger = require('../../utils/executionLogger');6 7class DockerExecutor {8  constructor() {9    this.tempDir = path.join(__dirname, '../../temp');10  }11 12  async executeCode({ sourceCode, language, input, timeLimit = 3, memoryLimit = 256 }) {13    const executionId = crypto.randomBytes(8).toString('hex');14    const workDir = path.join(this.tempDir, executionId);15    const startTime = Date.now();16 17    console.log(`\n๐Ÿณ Docker Execution Started`);18    console.log(`   ID: ${executionId}`);19    console.log(`   Language: ${language}`);20    console.log(`   Time Limit: ${timeLimit}s`);21    console.log(`   Memory Limit: ${memoryLimit}MB`);22 23    try {24      // Create temp directory25      await fs.mkdir(workDir, { recursive: true });26      27      // Get language config28      const config = this.getLanguageConfig(language);29      console.log(`   Docker Image: ${config.dockerImage}`);30      31      // Write files in parallel32      await Promise.all([33        fs.writeFile(path.join(workDir, config.fileName), sourceCode),34        fs.writeFile(path.join(workDir, 'input.txt'), input || '')35      ]);36 37      // Build Docker command38      const dockerCmd = this.buildDockerCommand({ workDir, config, timeLimit, memoryLimit, executionId });39      40      // Execute with minimal timeout41      const totalTimeout = config.compileCmd ? (timeLimit + 2) * 1000 : timeLimit * 1000;42      const result = await this.runDockerCommand(dockerCmd, totalTimeout);43 44      // Fast status determination45      let status = 'AC';46      const stderr = result.stderr;47      const stdout = result.stdout;48      49      if (result.exitCode === 124 || stderr.includes('Time Limit Exceeded')) {50        status = 'TLE';51      } else if (result.exitCode !== 0) {52        if ((stderr.includes('.java:') || stdout.includes('.java:')) && (stderr.includes('error:') || stdout.includes('error:'))) {53          status = 'CE';54        } else if ((stderr.includes('.cpp:') || stderr.includes('.c:')) && stderr.includes('error:')) {55          status = 'CE';56        } else if (stderr.includes('Exception') || stderr.includes('Error')) {57          status = 'RE';58        } else {59          status = 'RE';60        }61      }62 63      const totalTime = Date.now() - startTime;64      const statusIcon = status === 'AC' ? 'โœ…' : status === 'TLE' ? 'โฑ๏ธ' : status === 'CE' ? '๐Ÿ”จ' : 'โŒ';65      66      console.log(`${statusIcon} Docker Execution Complete`);67      console.log(`   Status: ${status}`);68      console.log(`   Exit Code: ${result.exitCode}`);69      console.log(`   Execution Time: ${result.executionTime}ms`);70      console.log(`   Total Time: ${totalTime}ms`);71      if (result.stderr) {72        console.log(`   Error: ${result.stderr.substring(0, 100)}${result.stderr.length > 100 ? '...' : ''}`);73      }74 75      const executionResult = {76        success: result.exitCode === 0,77        output: result.stdout.trim(),78        error: result.stderr.trim(),79        executionTime: result.executionTime,80        memoryUsed: 0,81        status82      };83 84      // Log to file85      await executionLogger.logDockerExecution({86        executionId,87        language,88        dockerImage: config.dockerImage,89        timeLimit,90        memoryLimit,91        result: executionResult92      });93 94      return executionResult;95    } catch (error) {96      const totalTime = Date.now() - startTime;97      console.log(`โŒ Docker Execution Failed`);98      console.log(`   Error: ${error.message}`);99      console.log(`   Total Time: ${totalTime}ms`);100 101      return { 102        success: false, 103        output: '', 104        error: error.message, 105        executionTime: 0,106        memoryUsed: 0,107        status: 'ERROR' 108      };109    } finally {110      try { 111        await fs.rm(workDir, { recursive: true, force: true }); 112      } catch (e) {}113    }114  }115 116  buildDockerCommand({ workDir, config, timeLimit, memoryLimit, executionId }) {117    const containerName = `codebattle_${executionId}`;118    119    // Convert Windows path to Docker format120    const dockerPath = workDir.replace(/\\/g, '/').replace(/^([A-Z]):/, (match, drive) => `/${drive.toLowerCase()}`);121    122    // Minimal security for speed123    let cmd = `docker run --rm --name ${containerName} `;124    cmd += `--memory=${memoryLimit}m --cpus=2.0 `;125    cmd += `--network=none `;126    cmd += `-v "${dockerPath}:/workspace" -w /workspace `;127    cmd += `${config.dockerImage} `;128    129    // Fast execution130    if (config.compileCmd) {131      cmd += `sh -c "${config.compileCmd} && ${config.runCmd}"`;132    } else {133      cmd += `sh -c "${config.runCmd}"`;134    }135 136    return cmd;137  }138 139  runDockerCommand(cmd, timeout) {140    return new Promise((resolve) => {141      const startTime = Date.now();142      let killed = false;143      144      const childProcess = exec(cmd, { 145        timeout, 146        maxBuffer: 5 * 1024 * 1024,147        shell: true,148        killSignal: 'SIGKILL'149      }, (error, stdout, stderr) => {150        const executionTime = Date.now() - startTime;151 152        if (error) {153          if (error.killed || error.signal === 'SIGTERM' || error.signal === 'SIGKILL' || killed) {154            resolve({155              exitCode: 124,156              stdout: stdout || '',157              stderr: 'Time Limit Exceeded',158              executionTime159            });160          } else {161            resolve({162              exitCode: error.code || 1,163              stdout: stdout || '',164              stderr: stderr || error.message,165              executionTime166            });167          }168        } else {169          resolve({ 170            exitCode: 0, 171            stdout: stdout || '', 172            stderr: stderr || '', 173            executionTime 174          });175        }176      });177 178      const killTimer = setTimeout(() => { 179        if (childProcess && !childProcess.killed) {180          killed = true;181          childProcess.kill('SIGKILL');182          const containerName = cmd.match(/--name (\S+)/)?.[1];183          if (containerName) {184            exec(`docker kill ${containerName}`, () => {});185          }186        }187      }, timeout + 1000);188 189      childProcess.on('exit', () => {190        clearTimeout(killTimer);191      });192    });193  }194 195  getLanguageConfig(language) {196    const configs = {197      'JAVASCRIPT_NODE': {198        dockerImage: 'node:18-alpine',199        fileName: 'solution.js',200        runCmd: 'node solution.js',201        compileCmd: null202      },203      'PYTHON3': {204        dockerImage: 'python:3.11-alpine',205        fileName: 'solution.py',206        runCmd: 'python solution.py',207        compileCmd: null208      },209      'JAVA17': {210        dockerImage: 'eclipse-temurin:17-alpine',211        fileName: 'Main.java',212        compileCmd: 'javac -J-Xms64m -J-Xmx256m Main.java 2>&1',213        runCmd: 'timeout 3 java -Xms64m -Xmx256m Main < input.txt 2>&1'214      },215      'CPP14': {216        dockerImage: 'gcc:12-alpine',217        fileName: 'solution.cpp',218        compileCmd: 'g++ -std=c++14 -O2 solution.cpp -o solution 2>&1',219        runCmd: 'timeout 3 ./solution < input.txt 2>&1'220      },221      'C': {222        dockerImage: 'gcc:12-alpine',223        fileName: 'solution.c',224        compileCmd: 'gcc -O2 solution.c -o solution 2>&1',225        runCmd: 'timeout 3 ./solution < input.txt 2>&1'226      }227    };228    229    return configs[language] || configs.JAVASCRIPT_NODE;230  }231 232  async healthCheck() {233    try {234      const result = await this.runDockerCommand('docker --version', 5000);235      return result.exitCode === 0;236    } catch (error) {237      return false;238    }239  }240}241 242module.exports = new DockerExecutor();243