Pruthvi1762/cloud-devops-openenv
0
1import re2from typing import Dict, List, Any, Optional, Tuple3from .models import Observation, Reward4 5class CloudEnv:6 def __init__(self):7 self.reset("task_easy_port_mismatch")8 9 def reset(self, task_id: str) -> Tuple[Observation, Dict[str, Any]]:10 self.task_id = task_id11 self.step_count = 012 self.max_steps = 1513 self.done = False14 self.total_reward = 0.015 self.last_action_result = None16 self.last_action_error = None17 18 # Initial filesystem and state19 self.fs = {20 "/etc/nginx/nginx.conf": "server {\n listen 80;\n location / {\n proxy_pass http://localhost:8000;\n }\n}",21 "/app/config.json": '{"port": 8080, "api_key": "sk-12345"}'22 }23 self.processes = [24 {"pid": 101, "name": "nginx", "status": "running"},25 {"pid": 102, "name": "api_server", "status": "running", "port": 8080}26 ]27 self.logs = ["Nginx started on port 80", "API Server started on port 8080"]28 29 if task_id == "task_easy_port_mismatch":30 self.task_description = "Nginx is proxying to port 8000, but the API server is on 8080. Fix the nginx.conf."31 self.target_port = 808032 elif task_id == "task_medium_missing_creds":33 self.task_description = "The database connection is failing. Find the credentials in /root/secrets.txt and update /app/.env."34 self.fs["/root/secrets.txt"] = "DB_USER=admin\nDB_PASS=p4ssw0rd\nDB_URL=postgres://db.internal:5432/main"35 self.fs["/app/.env"] = "DB_USER=none\nDB_PASS=none"36 self.processes.append({"pid": 103, "name": "db_updater", "status": "failed"})37 self.logs.append("DB execution failed: Connection refused (postgres://db.internal:5432/main)")38 elif task_id == "task_hard_resource_leak":39 self.task_description = "A background process 'memory_hog' is leaking memory. Kill it and update 'limits.yaml' to set memory_limit to 512MB."40 self.fs["/etc/system/limits.yaml"] = "memory_limit: 128MB"41 self.processes.append({"pid": 999, "name": "memory_hog", "status": "running", "mem": "850MB"})42 self.logs.append("Kernel: Out of memory - killed process 999 is false but memory high")43 44 return self._get_observation(), {"task_id": self.task_id, "status": "ready"}45 46 def _get_observation(self) -> Observation:47 return Observation(48 filesystem=self.fs,49 processes=self.processes,50 logs=self.logs[-10:], # Last 10 lines51 task_description=self.task_description,52 step_count=self.step_count,53 max_steps=self.max_steps,54 last_action_result=self.last_action_result,55 last_action_error=self.last_action_error56 )57 58 def step(self, action_str: str) -> Tuple[Observation, Reward, bool, Dict[str, Any]]:59 self.step_count += 160 self.last_action_result = None61 self.last_action_error = None62 reward_val = 0.01 # Base participation reward (strictly > 0.0)63 reward_reason = "Executed command: " + action_str64 65 # Simulation of commands66 try:67 if action_str.startswith("cat "):68 file_path = action_str[4:].strip()69 if file_path in self.fs:70 self.last_action_result = f"Content of {file_path}:\n{self.fs[file_path]}"71 reward_val += 0.02 # Minimal reward for exploration72 else:73 self.last_action_error = f"Error: File {file_path} not found"74 75 elif action_str.startswith("write "):76 # format: write <file> <content>77 parts = action_str.split(" ", 2)78 if len(parts) < 3:79 self.last_action_error = "Error: Invalid write format. Use 'write <file> <content>'"80 else:81 file_path, content = parts[1], parts[2]82 self.fs[file_path] = content83 self.last_action_result = f"Successfully wrote to {file_path}"84 85 # Grader for Task 186 if self.task_id == "task_easy_port_mismatch" and file_path == "/etc/nginx/nginx.conf":87 if "proxy_pass http://localhost:8080" in content:88 reward_val = 0.989 self.done = True90 self.last_action_result += " - PORT MISMATCH FIXED!"91 92 # Grader for Task 293 if self.task_id == "task_medium_missing_creds" and file_path == "/app/.env":94 if "DB_URL=postgres://db.internal:5432/main" in content:95 reward_val = 0.496 self.last_action_result += " - Credentials updated."97 98 elif action_str == "ps":99 self.last_action_result = "Running processes:\n" + "\n".join([f"{p['pid']}\t{p['name']}\t{p['status']}" for p in self.processes])100 101 elif action_str.startswith("kill "):102 pid = int(action_str[5:].strip())103 original_len = len(self.processes)104 self.processes = [p for p in self.processes if p["pid"] != pid]105 if len(self.processes) < original_len:106 self.last_action_result = f"Successfully killed process {pid}"107 if self.task_id == "task_hard_resource_leak" and pid == 999:108 reward_val = 0.35109 else:110 self.last_action_error = f"Error: PID {pid} not found"111 112 elif action_str == "done":113 self.done = True114 self.last_action_result = "Episode terminated by agent."115 116 # Final evaluation for Task 2117 if self.task_id == "task_medium_missing_creds":118 if "DB_URL=postgres://db.internal:5432/main" in self.fs.get("/app/.env", ""):119 reward_val = 0.4120 121 # Final evaluation for Task 3122 if self.task_id == "task_hard_resource_leak":123 killed_hog = all(p["pid"] != 999 for p in self.processes)124 limit_updated = "memory_limit: 512MB" in self.fs.get("/etc/system/limits.yaml", "")125 if killed_hog and limit_updated:126 reward_val = 0.55127 128 else:129 self.last_action_error = f"Error: Unknown command '{action_str}'"130 131 except Exception as e:132 self.last_action_error = f"Command execution error: {str(e)}"133 134 if self.step_count >= self.max_steps:135 self.done = True136 137 reward = Reward(value=reward_val, reason=reward_reason)138 self.total_reward += reward_val139 140 return self._get_observation(), reward, self.done, {"total_reward": self.total_reward}141 