244 lines
7.3 KiB
C++
244 lines
7.3 KiB
C++
#include <iostream>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <cstdlib>
|
||
#include <sstream>
|
||
#include <array>
|
||
|
||
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 spinUpMySQL();
|
||
void showContainerIP();
|
||
|
||
private:
|
||
static void runCommand(const string& cmd);
|
||
vector<pair<string, string>> getContainerList() const;
|
||
string selectContainer(const string& prompt);
|
||
};
|
||
|
||
// ---------------- Core Utility ----------------
|
||
|
||
void DockerManager::runCommand(const string& cmd) {
|
||
system(cmd.c_str());
|
||
}
|
||
|
||
vector<pair<string, string>> DockerManager::getContainerList() const {
|
||
vector<pair<string, string>> containers;
|
||
array<char, 256> 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<int>(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<string> 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::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<char, 128> 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;
|
||
}
|
||
|
||
// ---------------- 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. Spin Up MySQL Container\n"
|
||
<< "12. Get Container IP Address\n"
|
||
<< "13. 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.spinUpMySQL(); break;
|
||
case 12: docker.showContainerIP(); break;
|
||
case 13:
|
||
cout << "Exiting Tux-Dock.\n";
|
||
return 0;
|
||
default:
|
||
cout << "Invalid option.\n";
|
||
}
|
||
}
|
||
}
|
||
|