#include #include #include #include #include #include #include #include #include using namespace std; class DockerManager { public: void pullImage(); void runContainerInteractive(); void listContainers() const; void listImages() const; void startInteractive(); void startDetached(); void deleteImage(); void stopContainer(); void removeContainer(); void execShell(); void execDetachedCommand(); void createDockerfile(); void spinUpMySQL(); void showContainerIP(); private: static void runCommand(const string& cmd); vector> getContainerList() const; string selectContainer(const string& prompt); }; // ---------------- Core Utility ---------------- void DockerManager::runCommand(const string& cmd) { system(cmd.c_str()); } vector> DockerManager::getContainerList() const { vector> containers; array buffer{}; string result; FILE* pipe = popen("docker ps -a --format '{{.ID}} {{.Names}}'", "r"); if (!pipe) return containers; while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { result = buffer.data(); stringstream ss(result); string id, name; ss >> id >> name; if (!id.empty() && !name.empty()) containers.emplace_back(id, name); } pclose(pipe); return containers; } string DockerManager::selectContainer(const string& prompt) { auto containers = getContainerList(); if (containers.empty()) { cout << "No containers available.\n"; return ""; } cout << "\nAvailable Containers:\n"; int i = 1; for (const auto& c : containers) cout << i++ << ". " << c.second << " (" << c.first.substr(0, 12) << ")\n"; int choice; cout << prompt << " (1-" << containers.size() << "): "; cin >> choice; if (choice < 1 || choice > static_cast(containers.size())) { cout << "Invalid selection.\n"; return ""; } return containers[choice - 1].first; } // ---------------- Docker Actions ---------------- void DockerManager::pullImage() { string image; cout << "Enter Docker image to pull (e.g., alpine): "; cin >> image; runCommand("docker pull " + image); } void DockerManager::runContainerInteractive() { string image; cout << "Enter Docker image to run interactively (e.g., alpine): "; cin >> image; int portCount; cout << "How many port mappings? "; cin >> portCount; vector ports; for (int i = 0; i < portCount; ++i) { string port; cout << "Enter mapping #" << (i + 1) << " (format host:container, e.g., 8080:80): "; cin >> port; ports.push_back("-p " + port); } string portArgs; for (const auto& p : ports) portArgs += p + " "; cout << "\nPort Forwarding Explanation:\n" << " '-p hostPort:containerPort' exposes the container’s port to the host.\n" << " Example: '-p 8080:80' allows access via http://localhost:8080\n\n"; runCommand("docker run -it " + portArgs + image + " /bin/sh"); } void DockerManager::listContainers() const { runCommand("docker ps -a"); } void DockerManager::listImages() const { runCommand("docker images"); } void DockerManager::startInteractive() { string id = selectContainer("Select container to start interactively"); if (!id.empty()) runCommand("docker start -ai " + id); } void DockerManager::startDetached() { string id = selectContainer("Select container to start detached"); if (!id.empty()) runCommand("docker start " + id); } void DockerManager::deleteImage() { string image; cout << "Enter image name or ID to delete: "; cin >> image; runCommand("docker rmi " + image); } void DockerManager::stopContainer() { string id = selectContainer("Select container to stop"); if (!id.empty()) runCommand("docker stop " + id); } void DockerManager::removeContainer() { string id = selectContainer("Select container to remove"); if (!id.empty()) runCommand("docker rm " + id); } void DockerManager::execShell() { string id = selectContainer("Select running container for shell access"); if (!id.empty()) runCommand("docker exec -it " + id + " /bin/sh"); } void DockerManager::execDetachedCommand() { string id = selectContainer("Select container to run command in (detached)"); if (id.empty()) return; // Flush any leftover newline before using getline cin.ignore(numeric_limits::max(), '\n'); string command; cout << "Enter command to execute inside the container: "; getline(cin, command); if (command.empty()) { cout << "No command entered. Aborting.\n"; return; } string escapedCommand; escapedCommand.reserve(command.size() * 2); for (char c : command) { if (c == '"' || c == '\\') escapedCommand += '\\'; escapedCommand += c; } cout << "Executing command in detached mode...\n"; runCommand("docker exec -d " + id + " /bin/sh -c \"" + escapedCommand + "\""); cout << "Command dispatched.\n"; } void DockerManager::spinUpMySQL() { string port, password, version; cout << "Enter port mapping (e.g., 3306:3306): "; cin >> port; cout << "Enter MySQL root password: "; cin >> password; cout << "Enter MySQL version tag (e.g., 8): "; cin >> version; cout << "\nLaunching MySQL container (accessible via localhost:" << port.substr(0, port.find(':')) << ")\n"; runCommand("docker run -p " + port + " --name mysql-container -e MYSQL_ROOT_PASSWORD=" + password + " -d mysql:" + version); } // ---------------- Parsed IP Feature ---------------- void DockerManager::showContainerIP() { string id = selectContainer("Select container to view IP"); if (id.empty()) return; string command = "docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' " + id; array buffer{}; string ip; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { cout << "Failed to inspect container.\n"; return; } if (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { ip = buffer.data(); } pclose(pipe); if (ip.empty() || ip == "\n") cout << "No IP address found (container may be stopped or not attached to a network).\n"; else cout << "Container IP Address: " << ip; } void DockerManager::createDockerfile() { string baseImage, bashScriptPath, outputFile, imageName; cout << "Enter base Docker image (e.g., ubuntu:22.04): "; cin >> baseImage; cout << "Enter path to bash script to convert (e.g., setup.sh): "; cin >> bashScriptPath; if (!filesystem::exists(bashScriptPath)) { cout << "Error: Bash script not found.\n"; return; } cout << "Enter output Dockerfile name (e.g., Dockerfile or Dockerfile_app): "; cin >> outputFile; if (outputFile.empty()) outputFile = "Dockerfile"; ifstream scriptFile(bashScriptPath); ofstream dockerfile(outputFile); if (!scriptFile.is_open() || !dockerfile.is_open()) { cout << "Error: Unable to open file(s).\n"; return; } // Write Dockerfile header dockerfile << "FROM " << baseImage << "\n"; dockerfile << "WORKDIR /app\n\n"; dockerfile << "# Auto-generated by Tux-Dock\n"; string line; while (getline(scriptFile, line)) { if (line.empty()) continue; // Skip comments or shebang if (line.rfind("#", 0) == 0 || line.rfind("#!", 0) == 0) continue; dockerfile << "RUN " << line << "\n"; } dockerfile << "\nCMD [\"/bin/bash\"]\n"; dockerfile.close(); scriptFile.close(); cout << "Dockerfile created successfully: " << outputFile << "\n"; cout << "Enter image name to build from this Dockerfile (e.g., myimage): "; cin >> imageName; if (imageName.empty()) { cout << "No image name provided. Skipping build.\n"; return; } cout << "Building Docker image '" << imageName << "'...\n"; runCommand("docker build -t " + imageName + " -f " + outputFile + " ."); cout << "Docker build command executed.\n"; } // ---------------- Menu ---------------- int main() { DockerManager docker; int option; while (true) { cout << "\nTux-Dock: Docker Management Menu\n" << "----------------------------------\n" << "1. Pull Docker Image\n" << "2. Run/Create Interactive Container\n" << "3. List All Containers\n" << "4. List All Images\n" << "5. Start Container Interactively (boot new session)\n" << "6. Start Detached Container Session\n" << "7. Delete Docker Image\n" << "8. Stop Container\n" << "9. Remove Container\n" << "10. Attach Shell to Running Container\n" << "11. Run Detached Command in Container\n" << "12. Spin Up MySQL Container\n" << "13. Get Container IP Address\n" << "14. Create Dockerfile & Build Image from Bash Script\n" << "15. Exit\n" << "----------------------------------\n" << "Choose an option: "; cin >> option; switch (option) { case 1: docker.pullImage(); break; case 2: docker.runContainerInteractive(); break; case 3: docker.listContainers(); break; case 4: docker.listImages(); break; case 5: docker.startInteractive(); break; case 6: docker.startDetached(); break; case 7: docker.deleteImage(); break; case 8: docker.stopContainer(); break; case 9: docker.removeContainer(); break; case 10: docker.execShell(); break; case 11: docker.execDetachedCommand(); break; case 12: docker.spinUpMySQL(); break; case 13: docker.showContainerIP(); break; case 14: docker.createDockerfile(); break; case 15: cout << "Exiting Tux-Dock.\n"; return 0; default: cout << "Invalid option.\n"; } } }