diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 447173a..9bf2e84 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -11,10 +11,10 @@ env: jobs: macos-native-x86_64: - name: 'macOS 13' + name: 'macos-15-intel' # Use latest image, but hardcode version to avoid silent upgrades (and breaks). # See: https://github.com/actions/runner-images#available-images. - runs-on: macos-13 # Use M1 once available https://github.com/github/roadmap/issues/528 + runs-on: macos-15-intel # Use M1 once available https://github.com/github/roadmap/issues/528 steps: - name: Checkout uses: actions/checkout@v4 @@ -22,20 +22,30 @@ jobs: run: clang --version - name: cmake version run: cmake -version - - name: install docker - run: | - brew install colima docker - colima start - sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock - - name: docker version - run: docker ps - + - name: Checkout submodules + run: git submodule update --init --recursive - name: Install Homebrew packages run: | - brew install cmake boost spdlog nlohmann-json llvm curl + # Install required build-time packages but let the setup-docker action manage Colima/Docker + brew install cmake boost spdlog nlohmann-json llvm curl ninja ln -s "$(brew --prefix llvm)/bin/clang-format" "/usr/local/bin/clang-format" ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy" ln -s "$(brew --prefix llvm)/bin/clang-apply-replacements" "/usr/local/bin/clang-apply-replacements" + - name: Setup Docker on macOS + id: setup-docker + uses: douglascamata/setup-docker-macos-action@v1 + with: + # Pass Colima startup options (CPU and memory) via the additional options input + colima-additional-options: '--cpu 4 --memory 8' + colima-network-address: false + - name: Log Docker and Colima versions + run: | + sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock + echo "Colima version: ${{ steps.setup-docker.outputs.colima-version }}" + echo "Docker client version: ${{ steps.setup-docker.outputs.docker-client-version }}" + - name: Verify Docker + run: | + docker ps || { echo "Docker not responding, waiting..."; sleep 10; docker ps; } - name: Build CMAKE directory run: | CMAKE_POLICY_VERSION_MINIMUM=3.5 cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja -S . -B build @@ -46,13 +56,13 @@ jobs: name: "Ubuntu" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Checkout submodules run: git submodule update --init --recursive - name: Create build directory and run CMake run: | sudo apt-get -y update - sudo apt-get install build-essential cmake g++-10 gcc-10 libgtest-dev make libssl-dev python3-dev autotools-dev libicu-dev libbz2-dev libboost-all-dev libspdlog-dev nlohmann-json3-dev llvm curl libcurl4-openssl-dev + sudo apt-get install -y build-essential cmake g++-10 gcc-10 libgtest-dev make libssl-dev python3-dev autotools-dev libicu-dev libbz2-dev libboost-all-dev libspdlog-dev nlohmann-json3-dev llvm curl libcurl4-openssl-dev ninja-build ls g++ --version CMAKE_POLICY_VERSION_MINIMUM=3.5 cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja -S . -B build diff --git a/include/attach_container_cmd.hh b/include/attach_container_cmd.hh new file mode 100644 index 0000000..ee681c3 --- /dev/null +++ b/include/attach_container_cmd.hh @@ -0,0 +1,44 @@ +#ifndef ATTACH_CONTAINER_CMD_HPP +#define ATTACH_CONTAINER_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class AttachContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() = 0; +}; + +namespace attachcontainer { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace attachcontainer + +class AttachContainerCmdImpl : public AttachContainerCmd, + public AbstrDockerCmd { + public: + explicit AttachContainerCmdImpl(std::unique_ptr exec, + const std::string& containerId); + + bool exec() override; + + void close() override {} + + std::string getContainerId() override; + + ~AttachContainerCmdImpl() {} + + private: + std::string m_containerId; +}; + +} // namespace dockercpp::command +#endif /* ATTACH_CONTAINER_CMD_HPP */ diff --git a/include/commit_container_cmd.hh b/include/commit_container_cmd.hh new file mode 100644 index 0000000..ec750b5 --- /dev/null +++ b/include/commit_container_cmd.hh @@ -0,0 +1,48 @@ +#ifndef COMMIT_CONTAINER_CMD_HPP +#define COMMIT_CONTAINER_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class CommitContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() = 0; + virtual std::string getRepository() = 0; +}; + +namespace commitcontainer { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace commitcontainer + +class CommitContainerCmdImpl : public CommitContainerCmd, + public AbstrDockerCmd { + public: + explicit CommitContainerCmdImpl(std::unique_ptr exec, + const std::string& containerId, + const std::string& repository); + + std::string exec() override; + + void close() override {} + + std::string getContainerId() override; + std::string getRepository() override; + + ~CommitContainerCmdImpl() {} + + private: + std::string m_containerId; + std::string m_repository; +}; + +} // namespace dockercpp::command +#endif /* COMMIT_CONTAINER_CMD_HPP */ diff --git a/include/create_container_cmd.hh b/include/create_container_cmd.hh index 4833bb3..d29ab9a 100644 --- a/include/create_container_cmd.hh +++ b/include/create_container_cmd.hh @@ -2,6 +2,7 @@ #define INCLUDE_CREATE_CONTAINER_CMD_HPP #include +#include #include #include "abstr_sync_docker_cmd_exec.hh" @@ -15,6 +16,11 @@ struct CreateContainerRequest { std::string hostName; std::string name; std::vector cmd; + std::map labels; +}; + +struct HostConfig { + std::string hostName; }; class CreateContainerCmd @@ -29,6 +35,10 @@ class CreateContainerCmd CreateContainerCmd& withCmd(const std::vector& cmd); + CreateContainerCmd& withLabels(const std::map& labels); + + CreateContainerCmd& withLabel(const std::string& key, const std::string& value); + CreateContainerRequest getRequest() { return request; } ~CreateContainerCmd() override = default; diff --git a/include/docker_client.hh b/include/docker_client.hh index 1d15c1e..287d5bb 100644 --- a/include/docker_client.hh +++ b/include/docker_client.hh @@ -2,6 +2,7 @@ #define DOCKER_CLIENT_H #include "create_container_cmd.hh" +#include "events_cmd.hh" #include "info_cmd.hh" #include "ping_cmd.hh" #include "pull_image_cmd.hh" @@ -43,6 +44,7 @@ class DockerClient { std::shared_ptr removeContainerCmd(std::string id); + std::shared_ptr eventsCmd(); }; }; // namespace dockercpp diff --git a/include/docker_exception.hh b/include/docker_exception.hh index 588b7c1..5e76f77 100644 --- a/include/docker_exception.hh +++ b/include/docker_exception.hh @@ -1,8 +1,63 @@ #ifndef DOCKER_EXCEPTION_H #define DOCKER_EXCEPTION_H -class DockerException { +#include +#include +namespace dockercpp { + +class DockerException : public std::runtime_error { +public: + DockerException(const std::string& message) + : std::runtime_error(message), statusCode_(0) {} + + DockerException(const std::string& message, int statusCode) + : std::runtime_error(message), statusCode_(statusCode) {} + + DockerException(const std::string& message, int statusCode, const std::string& responseBody) + : std::runtime_error(message), + statusCode_(statusCode), + responseBody_(responseBody) {} + + int getStatusCode() const { return statusCode_; } + const std::string& getResponseBody() const { return responseBody_; } + +private: + int statusCode_; + std::string responseBody_; +}; + +// Specific Docker exceptions +class DockerClientException : public DockerException { +public: + explicit DockerClientException(const std::string& message) + : DockerException(message) {} +}; + +class NotModifiedException : public DockerException { +public: + NotModifiedException(const std::string& message) + : DockerException(message, 304) {} }; +class NotFoundException : public DockerException { +public: + NotFoundException(const std::string& message) + : DockerException(message, 404) {} +}; + +class ConflictException : public DockerException { +public: + ConflictException(const std::string& message) + : DockerException(message, 409) {} +}; + +class InternalServerErrorException : public DockerException { +public: + InternalServerErrorException(const std::string& message) + : DockerException(message, 500) {} +}; + +} // namespace dockercpp + #endif diff --git a/include/events_cmd.hh b/include/events_cmd.hh new file mode 100644 index 0000000..0276b03 --- /dev/null +++ b/include/events_cmd.hh @@ -0,0 +1,130 @@ +#ifndef EVENTS_CMD_HPP +#define EVENTS_CMD_HPP + +#include +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +struct DockerActor { + std::string id; + std::map attributes; +}; + +struct DockerEvent { + std::string type; + std::string action; + std::string id; + std::string from; + int64_t time; + int64_t timeNano; + DockerActor actor; + std::map labels; + std::string status; + std::string scope; +}; + +class EventsCmd : public SynchDockerCmd>, + public std::enable_shared_from_this { + public: + virtual int64_t getSince() const = 0; + virtual int64_t getUntil() const = 0; + virtual std::vector getFilters() const = 0; + + virtual EventsCmd& withSince(int64_t since) = 0; + virtual EventsCmd& withUntil(int64_t until) = 0; + virtual EventsCmd& withContainerFilter(const std::string& container) = 0; + virtual EventsCmd& withEventFilter(const std::string& event) = 0; + virtual EventsCmd& withImageFilter(const std::string& image) = 0; + virtual EventsCmd& withLabelFilter(const std::string& label) = 0; + virtual EventsCmd& withVolumeFilter(const std::string& volume) = 0; + virtual EventsCmd& withNetworkFilter(const std::string& network) = 0; + virtual EventsCmd& withDaemonFilter(const std::string& daemon) = 0; + virtual EventsCmd& withTypeFilter(const std::string& type) = 0; +}; + +namespace events { +class Exec : public exec::DockerCmdSyncExec> { + public: + ~Exec() {} +}; +} // namespace events + +class EventsCmdImpl : public EventsCmd, + public AbstrDockerCmd> { + public: + explicit EventsCmdImpl(std::unique_ptr exec); + + std::vector exec() override; + + void close() override {} + + int64_t getSince() const override { return m_since; } + int64_t getUntil() const override { return m_until; } + std::vector getFilters() const override; + + EventsCmd& withSince(int64_t since) override { + m_since = since; + return *this; + } + + EventsCmd& withUntil(int64_t until) override { + m_until = until; + return *this; + } + + EventsCmd& withContainerFilter(const std::string& container) override { + return withFilter("container", container); + } + + EventsCmd& withEventFilter(const std::string& event) override { + return withFilter("event", event); + } + + EventsCmd& withImageFilter(const std::string& image) override { + return withFilter("image", image); + } + + EventsCmd& withLabelFilter(const std::string& label) override { + return withFilter("label", label); + } + + EventsCmd& withVolumeFilter(const std::string& volume) override { + return withFilter("volume", volume); + } + + EventsCmd& withNetworkFilter(const std::string& network) override { + return withFilter("network", network); + } + + EventsCmd& withDaemonFilter(const std::string& daemon) override { + return withFilter("daemon", daemon); + } + + EventsCmd& withTypeFilter(const std::string& type) override { + return withFilter("type", type); + } + + ~EventsCmdImpl() = default; + + private: + int64_t m_since{0}; + int64_t m_until{0}; + std::map> m_filters; + + EventsCmd& withFilter(const std::string& key, const std::string& value) { + if (!m_filters.contains(key)) { + m_filters[key] = std::vector(); + } + m_filters[key].push_back(value); + return *this; + } +}; + +} // namespace dockercpp::command +#endif /* EVENTS_CMD_HPP */ diff --git a/include/events_cmd_exec.hh b/include/events_cmd_exec.hh new file mode 100644 index 0000000..050a67c --- /dev/null +++ b/include/events_cmd_exec.hh @@ -0,0 +1,25 @@ +#ifndef EVENTS_CMD_EXEC_HH +#define EVENTS_CMD_EXEC_HH + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "events_cmd.hh" + +namespace dockercpp::command::exec { + +class EventsCmdExec : public AbstrSyncDockerCmdExec>, + public events::Exec { + public: + EventsCmdExec(); + + std::vector exec(std::shared_ptr command) override; + + protected: + std::vector execute(std::shared_ptr command) override; +}; + +} // namespace dockercpp::command::exec + +#endif // EVENTS_CMD_EXEC_HH diff --git a/include/exec_create_cmd.hh b/include/exec_create_cmd.hh new file mode 100644 index 0000000..6abf058 --- /dev/null +++ b/include/exec_create_cmd.hh @@ -0,0 +1,87 @@ +#ifndef EXEC_CREATE_CMD_HPP +#define EXEC_CREATE_CMD_HPP + +#include +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +struct ExecCreateResponse { + std::string id; + nlohmann::json warnings; +}; + +class ExecCreateCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() const = 0; + virtual ExecCreateCmd& withContainerId(const std::string& id) = 0; + virtual std::vector getCmd() const = 0; + virtual ExecCreateCmd& withCmd(const std::vector& cmd) = 0; + virtual bool isAttachStdin() const = 0; + virtual ExecCreateCmd& withAttachStdin(bool attachStdin) = 0; + virtual bool isAttachStdout() const = 0; + virtual ExecCreateCmd& withAttachStdout(bool attachStdout) = 0; + virtual bool isAttachStderr() const = 0; + virtual ExecCreateCmd& withAttachStderr(bool attachStderr) = 0; + virtual bool isTty() const = 0; + virtual ExecCreateCmd& withTty(bool tty) = 0; + virtual std::string getUser() const = 0; + virtual ExecCreateCmd& withUser(const std::string& user) = 0; + virtual bool isPrivileged() const = 0; + virtual ExecCreateCmd& withPrivileged(bool privileged) = 0; +}; + +namespace exec { +class CreateExec : public exec::DockerCmdSyncExec { + public: + ~CreateExec() {} +}; +} // namespace exec + +class ExecCreateCmdImpl : public ExecCreateCmd, + public AbstrDockerCmd { + public: + explicit ExecCreateCmdImpl(std::unique_ptr exec); + + ExecCreateResponse exec() override; + + void close() override {} + + std::string getContainerId() const override; + ExecCreateCmd& withContainerId(const std::string& id) override; + std::vector getCmd() const override; + ExecCreateCmd& withCmd(const std::vector& cmd) override; + bool isAttachStdin() const override; + ExecCreateCmd& withAttachStdin(bool attachStdin) override; + bool isAttachStdout() const override; + ExecCreateCmd& withAttachStdout(bool attachStdout) override; + bool isAttachStderr() const override; + ExecCreateCmd& withAttachStderr(bool attachStderr) override; + bool isTty() const override; + ExecCreateCmd& withTty(bool tty) override; + std::string getUser() const override; + ExecCreateCmd& withUser(const std::string& user) override; + bool isPrivileged() const override; + ExecCreateCmd& withPrivileged(bool privileged) override; + + ~ExecCreateCmdImpl() {} + + private: + std::string m_containerId; + std::vector m_cmd; + bool m_attachStdin; + bool m_attachStdout; + bool m_attachStderr; + bool m_tty; + std::string m_user; + bool m_privileged; +}; + +} // namespace dockercpp::command +#endif /* EXEC_CREATE_CMD_HPP */ diff --git a/include/exec_start_cmd.hh b/include/exec_start_cmd.hh new file mode 100644 index 0000000..d3979fa --- /dev/null +++ b/include/exec_start_cmd.hh @@ -0,0 +1,57 @@ +#ifndef EXEC_START_CMD_HPP +#define EXEC_START_CMD_HPP + +#include +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class ExecStartCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getExecId() const = 0; + virtual ExecStartCmd& withExecId(const std::string& execId) = 0; + virtual bool isDetach() const = 0; + virtual ExecStartCmd& withDetach(bool detach) = 0; + virtual bool isTty() const = 0; + virtual ExecStartCmd& withTty(bool tty) = 0; +}; + +namespace exec { +class StartExec : public exec::DockerCmdSyncExec { + public: + ~StartExec() {} +}; +} // namespace exec + +class ExecStartCmdImpl : public ExecStartCmd, + public AbstrDockerCmd { + public: + explicit ExecStartCmdImpl(std::unique_ptr exec); + + void exec() override; + + void close() override {} + + std::string getExecId() const override; + ExecStartCmd& withExecId(const std::string& execId) override; + bool isDetach() const override; + ExecStartCmd& withDetach(bool detach) override; + bool isTty() const override; + ExecStartCmd& withTty(bool tty) override; + + ~ExecStartCmdImpl() {} + + private: + std::string m_execId; + bool m_detach; + bool m_tty; +}; + +} // namespace dockercpp::command +#endif /* EXEC_START_CMD_HPP */ diff --git a/include/kill_container_cmd.hh b/include/kill_container_cmd.hh new file mode 100644 index 0000000..8ff680a --- /dev/null +++ b/include/kill_container_cmd.hh @@ -0,0 +1,44 @@ +#ifndef KILL_CONTAINER_CMD_HPP +#define KILL_CONTAINER_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class KillContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() = 0; +}; + +namespace killcontainer { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace killcontainer + +class KillContainerCmdImpl : public KillContainerCmd, + public AbstrDockerCmd { + public: + explicit KillContainerCmdImpl(std::unique_ptr exec, + const std::string& containerId); + + bool exec() override; + + void close() override {} + + std::string getContainerId() override; + + ~KillContainerCmdImpl() {} + + private: + std::string m_containerId; +}; + +} // namespace dockercpp::command +#endif /* KILL_CONTAINER_CMD_HPP */ diff --git a/include/pause_container_cmd.hh b/include/pause_container_cmd.hh new file mode 100644 index 0000000..8b3c5f7 --- /dev/null +++ b/include/pause_container_cmd.hh @@ -0,0 +1,44 @@ +#ifndef PAUSE_CONTAINER_CMD_HPP +#define PAUSE_CONTAINER_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class PauseContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() = 0; +}; + +namespace pausecontainer { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace pausecontainer + +class PauseContainerCmdImpl : public PauseContainerCmd, + public AbstrDockerCmd { + public: + explicit PauseContainerCmdImpl(std::unique_ptr exec, + const std::string& containerId); + + bool exec() override; + + void close() override {} + + std::string getContainerId() override; + + ~PauseContainerCmdImpl() {} + + private: + std::string m_containerId; +}; + +} // namespace dockercpp::command +#endif /* PAUSE_CONTAINER_CMD_HPP */ diff --git a/include/remove_container_cmd.hh b/include/remove_container_cmd.hh index 4807594..ca22e74 100644 --- a/include/remove_container_cmd.hh +++ b/include/remove_container_cmd.hh @@ -14,8 +14,9 @@ class RemoveContainerCmd public std::enable_shared_from_this { public: virtual void withContainerId(std::string id) = 0; - virtual std::string getContainerId() = 0; + virtual bool hasForceEnabled() const = 0; + virtual RemoveContainerCmd& withForce(bool force) = 0; ~RemoveContainerCmd() {} @@ -43,11 +44,18 @@ class RemoveContainerCmdImpl void close() override {} void withContainerId(std::string id) override; - std::string getContainerId() override; + bool hasForceEnabled() const override { return m_force; } + RemoveContainerCmd& withForce(bool force) override { + m_force = force; + return *this; + } ~RemoveContainerCmdImpl() {} -}; -} // namespace dockercpp::command + +private: + bool m_force{false}; +}; // namespace dockercpp::command +} #endif /* INCLUDE_REMOVE_CONTAINER_CMD_HPP */ diff --git a/include/remove_image_cmd.hh b/include/remove_image_cmd.hh index 9171d8e..d33f353 100644 --- a/include/remove_image_cmd.hh +++ b/include/remove_image_cmd.hh @@ -18,9 +18,9 @@ class RemoveImageCmd : public SynchDockerCmd, virtual bool hasNoPruneEnabled() = 0; - virtual void withForce(bool force) = 0; + virtual RemoveImageCmd& withForce(bool force) = 0; - virtual void withNoPrune(bool force) = 0; + virtual RemoveImageCmd& withNoPrune(bool force) = 0; ~RemoveImageCmd() {} }; @@ -44,9 +44,9 @@ class RemoveImageCmdImpl : public RemoveImageCmd, bool hasNoPruneEnabled() override; - void withForce(bool force) override; + RemoveImageCmd& withForce(bool force) override; - void withNoPrune(bool force) override; + RemoveImageCmd& withNoPrune(bool force) override; std::string exec() override; diff --git a/include/restart_container_cmd.hh b/include/restart_container_cmd.hh new file mode 100644 index 0000000..bed8a25 --- /dev/null +++ b/include/restart_container_cmd.hh @@ -0,0 +1,44 @@ +#ifndef RESTART_CONTAINER_CMD_HPP +#define RESTART_CONTAINER_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class RestartContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() = 0; +}; + +namespace restartcontainer { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace restartcontainer + +class RestartContainerCmdImpl : public RestartContainerCmd, + public AbstrDockerCmd { + public: + explicit RestartContainerCmdImpl(std::unique_ptr exec, + const std::string& containerId); + + bool exec() override; + + void close() override {} + + std::string getContainerId() override; + + ~RestartContainerCmdImpl() {} + + private: + std::string m_containerId; +}; + +} // namespace dockercpp::command +#endif /* RESTART_CONTAINER_CMD_HPP */ diff --git a/include/search_images_cmd.hh b/include/search_images_cmd.hh new file mode 100644 index 0000000..bc392a5 --- /dev/null +++ b/include/search_images_cmd.hh @@ -0,0 +1,60 @@ +#ifndef SEARCH_IMAGES_CMD_HPP +#define SEARCH_IMAGES_CMD_HPP + +#include +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +struct SearchItem { + std::string description; + bool is_official; + bool is_automated; + std::string name; + int64_t star_count; +}; + +class SearchImagesCmd : public SynchDockerCmd>, + public std::enable_shared_from_this { + public: + virtual std::string getTerm() const = 0; + virtual SearchImagesCmd& withTerm(const std::string& term) = 0; + virtual int getLimit() const = 0; + virtual SearchImagesCmd& withLimit(int limit) = 0; +}; + +namespace search { +class Exec : public exec::DockerCmdSyncExec> { + public: + ~Exec() {} +}; +} // namespace search + +class SearchImagesCmdImpl : public SearchImagesCmd, + public AbstrDockerCmd> { + public: + explicit SearchImagesCmdImpl(std::unique_ptr exec); + + std::vector exec() override; + + void close() override {} + + std::string getTerm() const override; + SearchImagesCmd& withTerm(const std::string& term) override; + int getLimit() const override; + SearchImagesCmd& withLimit(int limit) override; + + ~SearchImagesCmdImpl() {} + + private: + std::string m_term; + int m_limit; +}; + +} // namespace dockercpp::command +#endif /* SEARCH_IMAGES_CMD_HPP */ diff --git a/include/start_container_cmd.hh b/include/start_container_cmd.hh index b0a44d5..8ff7628 100644 --- a/include/start_container_cmd.hh +++ b/include/start_container_cmd.hh @@ -35,7 +35,7 @@ class StartContainerCmdImpl : public StartContainerCmd, std::string getContainerId() override; - ~StartContainerCmdImpl() {} + ~StartContainerCmdImpl() = default; private: std::string m_containerId; diff --git a/include/stats_container_cmd.hh b/include/stats_container_cmd.hh new file mode 100644 index 0000000..3b156df --- /dev/null +++ b/include/stats_container_cmd.hh @@ -0,0 +1,59 @@ +#ifndef STATS_CONTAINER_CMD_HPP +#define STATS_CONTAINER_CMD_HPP + +#include +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +struct Stats { + // Add statistics fields based on docker stats output + nlohmann::json usage; + std::string container_id; + std::string name; +}; + +class StatsContainerCmd : public SynchDockerCmd>, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() const = 0; + virtual StatsContainerCmd& withContainerId(const std::string& id) = 0; + virtual bool isNoStream() const = 0; + virtual StatsContainerCmd& withNoStream(bool noStream) = 0; +}; + +namespace stats { +class Exec : public exec::DockerCmdSyncExec> { + public: + ~Exec() {} +}; +} // namespace stats + +class StatsContainerCmdImpl : public StatsContainerCmd, + public AbstrDockerCmd> { + public: + explicit StatsContainerCmdImpl(std::unique_ptr exec); + + std::vector exec() override; + + void close() override {} + + std::string getContainerId() const override; + StatsContainerCmd& withContainerId(const std::string& id) override; + bool isNoStream() const override; + StatsContainerCmd& withNoStream(bool noStream) override; + + ~StatsContainerCmdImpl() {} + + private: + std::string m_containerId; + bool m_noStream; +}; + +} // namespace dockercpp::command +#endif /* STATS_CONTAINER_CMD_HPP */ diff --git a/include/stop_container_cmd.hh b/include/stop_container_cmd.hh index a6b48cc..3bce954 100644 --- a/include/stop_container_cmd.hh +++ b/include/stop_container_cmd.hh @@ -33,7 +33,7 @@ class StopContainerCmdImpl : public StopContainerCmd, std::string getContainerId() override; - ~StopContainerCmdImpl() {} + ~StopContainerCmdImpl() = default; private: std::string m_containerId; diff --git a/include/synch_docker_cmd.hh b/include/synch_docker_cmd.hh index 0d64134..4640620 100644 --- a/include/synch_docker_cmd.hh +++ b/include/synch_docker_cmd.hh @@ -29,9 +29,10 @@ class AbstrDockerCmd : SynchDockerCmd { : m_execution(std::move(execution)){}; RES_T exec() override { + return RES_T(); } - ~AbstrDockerCmd() {} + ~AbstrDockerCmd() = default; protected: std::unique_ptr> m_execution; diff --git a/include/tag_image_cmd.hh b/include/tag_image_cmd.hh new file mode 100644 index 0000000..fd8f1bb --- /dev/null +++ b/include/tag_image_cmd.hh @@ -0,0 +1,52 @@ +#ifndef TAG_IMAGE_CMD_HPP +#define TAG_IMAGE_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class TagImageCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getImageId() = 0; + virtual std::string getRepository() = 0; + virtual std::string getTag() = 0; +}; + +namespace tagimage { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace tagimage + +class TagImageCmdImpl : public TagImageCmd, + public AbstrDockerCmd { + public: + explicit TagImageCmdImpl(std::unique_ptr exec, + const std::string& imageId, + const std::string& repository, + const std::string& tag); + + bool exec() override; + + void close() override {} + + std::string getImageId() override; + std::string getRepository() override; + std::string getTag() override; + + ~TagImageCmdImpl() {} + + private: + std::string m_imageId; + std::string m_repository; + std::string m_tag; +}; + +} // namespace dockercpp::command +#endif /* TAG_IMAGE_CMD_HPP */ diff --git a/include/unpause_container_cmd.hh b/include/unpause_container_cmd.hh new file mode 100644 index 0000000..7b9cb3f --- /dev/null +++ b/include/unpause_container_cmd.hh @@ -0,0 +1,44 @@ +#ifndef UNPAUSE_CONTAINER_CMD_HPP +#define UNPAUSE_CONTAINER_CMD_HPP + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +class UnpauseContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() = 0; +}; + +namespace unpausecontainer { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace unpausecontainer + +class UnpauseContainerCmdImpl : public UnpauseContainerCmd, + public AbstrDockerCmd { + public: + explicit UnpauseContainerCmdImpl(std::unique_ptr exec, + const std::string& containerId); + + bool exec() override; + + void close() override {} + + std::string getContainerId() override; + + ~UnpauseContainerCmdImpl() {} + + private: + std::string m_containerId; +}; + +} // namespace dockercpp::command +#endif /* UNPAUSE_CONTAINER_CMD_HPP */ diff --git a/include/update_container_cmd.hh b/include/update_container_cmd.hh new file mode 100644 index 0000000..24638b9 --- /dev/null +++ b/include/update_container_cmd.hh @@ -0,0 +1,73 @@ +#ifndef UPDATE_CONTAINER_CMD_HPP +#define UPDATE_CONTAINER_CMD_HPP + +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "synch_docker_cmd.hh" + +namespace dockercpp::command { + +struct UpdateContainerResponse { + private: + std::vector warnings; + public: + const std::vector& getWarnings() const { return warnings; } +}; + +class UpdateContainerCmd : public SynchDockerCmd, + public std::enable_shared_from_this { + public: + virtual std::string getContainerId() const = 0; + virtual UpdateContainerCmd& withContainerId(const std::string& id) = 0; + virtual int getCpuShares() const = 0; + virtual UpdateContainerCmd& withCpuShares(int cpuShares) = 0; + virtual int64_t getMemory() const = 0; + virtual UpdateContainerCmd& withMemory(int64_t memory) = 0; + virtual int getMemorySwap() const = 0; + virtual UpdateContainerCmd& withMemorySwap(int memorySwap) = 0; + virtual std::vector getCpusetCpus() const = 0; + virtual UpdateContainerCmd& withCpusetCpus(const std::vector& cpusetCpus) = 0; +}; + +namespace update { +class Exec : public exec::DockerCmdSyncExec { + public: + ~Exec() {} +}; +} // namespace update + +class UpdateContainerCmdImpl : public UpdateContainerCmd, + public AbstrDockerCmd { + public: + explicit UpdateContainerCmdImpl(std::unique_ptr exec); + + UpdateContainerResponse exec() override; + + void close() override {} + + std::string getContainerId() const override; + UpdateContainerCmd& withContainerId(const std::string& id) override; + int getCpuShares() const override; + UpdateContainerCmd& withCpuShares(int cpuShares) override; + int64_t getMemory() const override; + UpdateContainerCmd& withMemory(int64_t memory) override; + int getMemorySwap() const override; + UpdateContainerCmd& withMemorySwap(int memorySwap) override; + std::vector getCpusetCpus() const override; + UpdateContainerCmd& withCpusetCpus(const std::vector& cpusetCpus) override; + + ~UpdateContainerCmdImpl() {} + + private: + std::string m_containerId; + int m_cpuShares; + int64_t m_memory; + int m_memorySwap; + std::vector m_cpusetCpus; +}; + +} // namespace dockercpp::command +#endif /* UPDATE_CONTAINER_CMD_HPP */ diff --git a/include/update_container_cmd_exec.hh b/include/update_container_cmd_exec.hh new file mode 100644 index 0000000..ae2d88e --- /dev/null +++ b/include/update_container_cmd_exec.hh @@ -0,0 +1,24 @@ +#ifndef SRC_STOP_CONTAINER_CMD_EXEC_HPP +#define SRC_STOP_CONTAINER_CMD_EXEC_HPP + +#include "abstr_sync_docker_cmd_exec.hh" +#include "update_container_cmd.hh" + +namespace dockercpp::command::exec { +class UpdateContainerCmdExec + : public AbstrSyncDockerCmdExec, + public update::Exec { + public: + UpdateContainerCmdExec(); + dockercpp::command::UpdateContainerResponse exec( + std::shared_ptr command) override; + + ~UpdateContainerCmdExec() {} + + protected: + dockercpp::command::UpdateContainerResponse execute( + std::shared_ptr command) override; +}; +} // namespace dockercpp::command::exec + +#endif /* SRC_STOP_CONTAINER_CMD_EXEC_HPP */ diff --git a/include/webtarget.hh b/include/webtarget.hh index c501c36..a27315d 100644 --- a/include/webtarget.hh +++ b/include/webtarget.hh @@ -3,6 +3,7 @@ #include #include +#include namespace dockercpp::core { @@ -15,6 +16,8 @@ class InvocationBuilder { std::string post(std::string &json); bool deletehttp(); + + std::pair deletehttp_with_code(); private: std::string m_path; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1634a1..c1020cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,11 @@ add_library(docker_cpp_client docker_object.cc curl_docker_http_client.cc docker_client_config.cc + events_cmd.cc + events_cmd_exec.cc + exec_create_cmd.cc + exec_create_cmd_exec.cc + exec_start_cmd.cc info_cmd.cc info_cmd_exec.cc info.cc @@ -20,6 +25,9 @@ add_library(docker_cpp_client ping_cmd.cc pull_image_cmd.cc pull_image_cmd_exec.cc + search_images_cmd.cc + stats_container_cmd.cc + update_container_cmd.cc webtarget.cc abstr_sync_docker_cmd_exec.cc remove_container_cmd.cc @@ -30,6 +38,8 @@ add_library(docker_cpp_client start_container_cmd_exec.cc stop_container_cmd.cc stop_container_cmd_exec.cc + update_container_cmd_exec.cc + update_container_cmd.cc version.cc version_cmd.cc version_cmd_exec.cc diff --git a/src/create_container_cmd.cc b/src/create_container_cmd.cc index af1e6dd..00fa58c 100644 --- a/src/create_container_cmd.cc +++ b/src/create_container_cmd.cc @@ -25,6 +25,18 @@ CreateContainerCmd& CreateContainerCmd::withCmd( return *this; } +CreateContainerCmd& CreateContainerCmd::withLabels( + const std::map& labels) { + request.labels = labels; + return *this; +} + +CreateContainerCmd& CreateContainerCmd::withLabel( + const std::string& key, const std::string& value) { + request.labels[key] = value; + return *this; +} + CreateContainerCmdImpl::CreateContainerCmdImpl( std::unique_ptr exec, std::string image) : AbstrDockerCmd( @@ -47,6 +59,10 @@ void to_json(nlohmann::json& j, if (!createContainerRequest.cmd.empty()) { j["Cmd"] = createContainerRequest.cmd; } + + if (!createContainerRequest.labels.empty()) { + j["Labels"] = createContainerRequest.labels; + } } } // namespace dockercpp::command diff --git a/src/curl_docker_http_client.cc b/src/curl_docker_http_client.cc index dfce96b..5093cf7 100644 --- a/src/curl_docker_http_client.cc +++ b/src/curl_docker_http_client.cc @@ -94,20 +94,23 @@ http::Response deletecurl(Request &request) { char *url; long response_code; double elapsed; + + curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &elapsed); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - curl_easy_perform(curl); curl_easy_cleanup(curl); curl = nullptr; spdlog::info("Response is {}", response_string); spdlog::info("Response header is {}", header_string); + spdlog::info("Response code {}", response_code); http::Response response(response_code, std::unordered_map(), - std::string{}); + response_string); return response; } @@ -153,11 +156,13 @@ http::Response postcurl(Request &request) { char *url; long response_code; double elapsed; + + curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &elapsed); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - curl_easy_perform(curl); curl_easy_cleanup(curl); curl = nullptr; spdlog::info("Response is {}", response_string); diff --git a/src/docker_client.cc b/src/docker_client.cc index 4b2e3c5..5040cc2 100644 --- a/src/docker_client.cc +++ b/src/docker_client.cc @@ -20,6 +20,7 @@ #include "stop_container_cmd_exec.hh" #include "version_cmd.hh" #include "version_cmd_exec.hh" +#include "events_cmd_exec.hh" namespace dockercpp { @@ -91,4 +92,9 @@ std::shared_ptr DockerClient::removeContainerCmd( std::move(std::make_unique()), id); } +std::shared_ptr DockerClient::eventsCmd() { + return std::make_shared( + std::move(std::make_unique())); +} + } // namespace dockercpp diff --git a/src/events_cmd.cc b/src/events_cmd.cc new file mode 100644 index 0000000..44c9e88 --- /dev/null +++ b/src/events_cmd.cc @@ -0,0 +1,22 @@ +#include "events_cmd.hh" + +namespace dockercpp::command { + +EventsCmdImpl::EventsCmdImpl(std::unique_ptr exec) + : AbstrDockerCmd>(std::move(exec)) {} + +std::vector EventsCmdImpl::exec() { + return m_execution->exec(shared_from_this()); +} + +std::vector EventsCmdImpl::getFilters() const { + std::vector result; + for (const auto& [key, values] : m_filters) { + for (const auto& value : values) { + result.push_back(key + "=" + value); + } + } + return result; +} + +} diff --git a/src/events_cmd_exec.cc b/src/events_cmd_exec.cc new file mode 100644 index 0000000..f68fc9b --- /dev/null +++ b/src/events_cmd_exec.cc @@ -0,0 +1,103 @@ +#include "events_cmd_exec.hh" + +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "webtarget.hh" + +namespace dockercpp::command::exec { + +EventsCmdExec::EventsCmdExec() + : AbstrSyncDockerCmdExec>(), + events::Exec() {} + +std::vector EventsCmdExec::exec(std::shared_ptr command) { + return execute(command); +} + +void EventsCmdExec::from_json(const nlohmann::json& j, DockerEvent& event) { + event.type = j.value("Type", ""); + event.action = j.value("Action", ""); + event.id = j.value("id", ""); + event.from = j.value("from", ""); + event.time = j.value("time", 0); + event.timeNano = j.value("timeNano", 0); + event.status = j.value("status", ""); + event.scope = j.value("scope", ""); + + if (j.contains("Actor") && j["Actor"].is_object()) { + const auto& actor = j["Actor"]; + event.actor.id = actor.value("ID", ""); + + auto attrs = actor.value("Attributes", nlohmann::json::object()); + for (auto it = attrs.begin(); it != attrs.end(); ++it) { + event.actor.attributes[it.key()] = it.value().get(); + } + } + + if (j.contains("Labels") && j["Labels"].is_object()) { + const auto& labels = j["Labels"]; + for (auto it = labels.begin(); it != labels.end(); ++it) { + event.labels[it.key()] = it.value().get(); + } + } +} + +nlohmann::json EventsCmdExec::buildFilterJson(const std::vector& filters) { + nlohmann::json filterJson = nlohmann::json::object(); + for (const auto& filter : filters) { + size_t pos = filter.find('='); + if (pos != std::string::npos) { + std::string key = filter.substr(0, pos); + std::string value = filter.substr(pos + 1); + if (!filterJson.contains(key)) { + filterJson[key] = nlohmann::json::array(); + } + filterJson[key].push_back(value); + } + } + return filterJson; +} + +std::vector EventsCmdExec::execute(std::shared_ptr command) { + core::WebTarget webResource = m_webTarget->path("events"); + + // Add query parameters + if (command->getSince() > 0) { + webResource = webResource.queryParam("since", std::to_string(command->getSince())); + } + if (command->getUntil() > 0) { + webResource = webResource.queryParam("until", std::to_string(command->getUntil())); + } + + // Add filters + auto filters = command->getFilters(); + if (!filters.empty()) { + nlohmann::json filterJson = buildFilterJson(filters); + webResource = webResource.queryParam("filters", filterJson.dump()); + } + + auto response = webResource.request().get(); + std::vector events; + + // Docker events come as newline-delimited JSON + std::istringstream stream(response); + std::string line; + while (std::getline(stream, line)) { + if (line.empty()) continue; + + try { + const nlohmann::json json = nlohmann::json::parse(line); + DockerEvent event; + from_json(json, event); + events.push_back(event); + } catch (const nlohmann::json::exception& e) { + spdlog::error("Failed to parse event JSON: {}", e.what()); + continue; + } + } + + return events; +} + +} // namespace dockercpp::command::exec diff --git a/src/events_cmd_exec.hh b/src/events_cmd_exec.hh new file mode 100644 index 0000000..a337a51 --- /dev/null +++ b/src/events_cmd_exec.hh @@ -0,0 +1,29 @@ +#ifndef EVENTS_CMD_EXEC_HH +#define EVENTS_CMD_EXEC_HH + +#include +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "events_cmd.hh" +#include "webtarget.hh" + +namespace dockercpp::command::exec { + +class EventsCmdExec : public AbstrSyncDockerCmdExec>, + public events::Exec { + public: + EventsCmdExec(); + + std::vector execute(std::shared_ptr command) override; + std::vector exec(std::shared_ptr command) override; + + private: + void from_json(const nlohmann::json& j, DockerEvent& event); + nlohmann::json buildFilterJson(const std::vector& filters); +}; + +} // namespace dockercpp::command::exec + +#endif // EVENTS_CMD_EXEC_HH diff --git a/src/exec_create_cmd.cc b/src/exec_create_cmd.cc new file mode 100644 index 0000000..3caca2d --- /dev/null +++ b/src/exec_create_cmd.cc @@ -0,0 +1,89 @@ +#include "exec_create_cmd.hh" + +namespace dockercpp::command { + +ExecCreateCmdImpl::ExecCreateCmdImpl(std::unique_ptr exec) + : AbstrDockerCmd(std::move(exec)), + m_attachStdin(false), + m_attachStdout(true), + m_attachStderr(true), + m_tty(false), + m_privileged(false) {} + +ExecCreateResponse ExecCreateCmdImpl::exec() { + return m_execution->exec(shared_from_this()); +} + +std::string ExecCreateCmdImpl::getContainerId() const { + return m_containerId; +} + +ExecCreateCmd& ExecCreateCmdImpl::withContainerId(const std::string& id) { + m_containerId = id; + return *this; +} + +std::vector ExecCreateCmdImpl::getCmd() const { + return m_cmd; +} + +ExecCreateCmd& ExecCreateCmdImpl::withCmd(const std::vector& cmd) { + m_cmd = cmd; + return *this; +} + +bool ExecCreateCmdImpl::isAttachStdin() const { + return m_attachStdin; +} + +ExecCreateCmd& ExecCreateCmdImpl::withAttachStdin(bool attachStdin) { + m_attachStdin = attachStdin; + return *this; +} + +bool ExecCreateCmdImpl::isAttachStdout() const { + return m_attachStdout; +} + +ExecCreateCmd& ExecCreateCmdImpl::withAttachStdout(bool attachStdout) { + m_attachStdout = attachStdout; + return *this; +} + +bool ExecCreateCmdImpl::isAttachStderr() const { + return m_attachStderr; +} + +ExecCreateCmd& ExecCreateCmdImpl::withAttachStderr(bool attachStderr) { + m_attachStderr = attachStderr; + return *this; +} + +bool ExecCreateCmdImpl::isTty() const { + return m_tty; +} + +ExecCreateCmd& ExecCreateCmdImpl::withTty(bool tty) { + m_tty = tty; + return *this; +} + +std::string ExecCreateCmdImpl::getUser() const { + return m_user; +} + +ExecCreateCmd& ExecCreateCmdImpl::withUser(const std::string& user) { + m_user = user; + return *this; +} + +bool ExecCreateCmdImpl::isPrivileged() const { + return m_privileged; +} + +ExecCreateCmd& ExecCreateCmdImpl::withPrivileged(bool privileged) { + m_privileged = privileged; + return *this; +} + +} diff --git a/src/exec_create_cmd_exec.cc b/src/exec_create_cmd_exec.cc new file mode 100644 index 0000000..aec3b5a --- /dev/null +++ b/src/exec_create_cmd_exec.cc @@ -0,0 +1,48 @@ +#include "exec_create_cmd_exec.hh" + +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "webtarget.hh" + +namespace dockercpp::command::exec { + +ExecCreateCmdExec::ExecCreateCmdExec() + : AbstrSyncDockerCmdExec(), + CreateExec() {} + +ExecCreateResponse ExecCreateCmdExec::exec(std::shared_ptr command) { + return execute(command); +} + +ExecCreateResponse ExecCreateCmdExec::execute(std::shared_ptr command) { + core::WebTarget webResource = m_webTarget->path("containers") + .path(command->getContainerId()) + .path("exec"); + + nlohmann::json requestJson; + requestJson["AttachStdin"] = command->isAttachStdin(); + requestJson["AttachStdout"] = command->isAttachStdout(); + requestJson["AttachStderr"] = command->isAttachStderr(); + requestJson["Tty"] = command->isTty(); + requestJson["Cmd"] = command->getCmd(); + + if (!command->getUser().empty()) { + requestJson["User"] = command->getUser(); + } + requestJson["Privileged"] = command->isPrivileged(); + + std::string requestStr = requestJson.dump(); + auto response = webResource.request().post(requestStr); + + spdlog::info("converting string {}", response); + + const nlohmann::json json = nlohmann::json::parse(response); + ExecCreateResponse execResponse; + execResponse.id = json.value("Id", ""); + execResponse.warnings = json.value("Warnings", nlohmann::json::array()); + + return execResponse; +} + +} // namespace dockercpp::command::exec diff --git a/src/exec_create_cmd_exec.hh b/src/exec_create_cmd_exec.hh new file mode 100644 index 0000000..0b0211d --- /dev/null +++ b/src/exec_create_cmd_exec.hh @@ -0,0 +1,26 @@ +#ifndef EXEC_CREATE_CMD_EXEC_HH +#define EXEC_CREATE_CMD_EXEC_HH + +#include +#include + +#include "abstr_sync_docker_cmd_exec.hh" +#include "exec_create_cmd.hh" +#include "webtarget.hh" + +namespace dockercpp::command::exec { + +class ExecCreateCmdExec : public AbstrSyncDockerCmdExec, + public command::exec::CreateExec { + public: + ExecCreateCmdExec(); + + ExecCreateResponse exec(std::shared_ptr command) override; + + protected: + ExecCreateResponse execute(std::shared_ptr command) override; +}; + +} // namespace dockercpp::command::exec + +#endif // EXEC_CREATE_CMD_EXEC_HH diff --git a/src/exec_start_cmd.cc b/src/exec_start_cmd.cc new file mode 100644 index 0000000..6f53c4f --- /dev/null +++ b/src/exec_start_cmd.cc @@ -0,0 +1,41 @@ +#include "exec_start_cmd.hh" + +namespace dockercpp::command { + +ExecStartCmdImpl::ExecStartCmdImpl(std::unique_ptr exec) + : AbstrDockerCmd(std::move(exec)), + m_detach(false), + m_tty(false) {} + +void ExecStartCmdImpl::exec() { + return m_execution->exec(shared_from_this()); +} + +std::string ExecStartCmdImpl::getExecId() const { + return m_execId; +} + +ExecStartCmd& ExecStartCmdImpl::withExecId(const std::string& execId) { + m_execId = execId; + return *this; +} + +bool ExecStartCmdImpl::isDetach() const { + return m_detach; +} + +ExecStartCmd& ExecStartCmdImpl::withDetach(bool detach) { + m_detach = detach; + return *this; +} + +bool ExecStartCmdImpl::isTty() const { + return m_tty; +} + +ExecStartCmd& ExecStartCmdImpl::withTty(bool tty) { + m_tty = tty; + return *this; +} + +} diff --git a/src/remove_image_cmd.cc b/src/remove_image_cmd.cc index d7af2e0..d05a4eb 100644 --- a/src/remove_image_cmd.cc +++ b/src/remove_image_cmd.cc @@ -12,12 +12,21 @@ std::string RemoveImageCmdImpl::exec() { std::string RemoveImageCmdImpl::getImageId() { return m_id; } -bool RemoveImageCmdImpl::hasForcedEnabled() { return false; } +bool m_force{false}; +bool m_noPrune{false}; -bool RemoveImageCmdImpl::hasNoPruneEnabled() { return false; } +bool RemoveImageCmdImpl::hasForcedEnabled() { return m_force; } -void RemoveImageCmdImpl::withForce(bool force) {} +bool RemoveImageCmdImpl::hasNoPruneEnabled() { return m_noPrune; } -void RemoveImageCmdImpl::withNoPrune(bool force) {} +RemoveImageCmd& RemoveImageCmdImpl::withForce(bool force) { + m_force = force; + return *this; +} + +RemoveImageCmd& RemoveImageCmdImpl::withNoPrune(bool force) { + m_noPrune = force; + return *this; +} } // namespace dockercpp::command diff --git a/src/remove_image_cmd_exec.cc b/src/remove_image_cmd_exec.cc index f1a3a7a..e9002b0 100644 --- a/src/remove_image_cmd_exec.cc +++ b/src/remove_image_cmd_exec.cc @@ -1,6 +1,9 @@ #include "remove_image_cmd_exec.hh" +#include + #include "abstr_sync_docker_cmd_exec.hh" +#include "docker_exception.hh" #include "webtarget.hh" namespace dockercpp::command::exec { @@ -19,7 +22,15 @@ std::string RemoveImageCmdExec::execute( core::WebTarget webResource = m_webTarget->path("/images/" + command->getImageId()); - return webResource.request().deletehttp() ? "true" : "false"; + auto [response, statusCode] = webResource.request().deletehttp_with_code(); + + if (statusCode == 404) { + throw dockercpp::NotFoundException("No such image: " + command->getImageId()); + } else if (statusCode != 200) { + throw dockercpp::DockerException("Error removing image", statusCode, response); + } + + return response; } } // namespace dockercpp::command::exec diff --git a/src/search_images_cmd.cc b/src/search_images_cmd.cc new file mode 100644 index 0000000..888f4c2 --- /dev/null +++ b/src/search_images_cmd.cc @@ -0,0 +1,31 @@ +#include "search_images_cmd.hh" + +namespace dockercpp::command { + +SearchImagesCmdImpl::SearchImagesCmdImpl(std::unique_ptr exec) + : AbstrDockerCmd>(std::move(exec)), + m_limit(0) {} + +std::vector SearchImagesCmdImpl::exec() { + return m_execution->exec(shared_from_this()); +} + +std::string SearchImagesCmdImpl::getTerm() const { + return m_term; +} + +SearchImagesCmd& SearchImagesCmdImpl::withTerm(const std::string& term) { + m_term = term; + return *this; +} + +int SearchImagesCmdImpl::getLimit() const { + return m_limit; +} + +SearchImagesCmd& SearchImagesCmdImpl::withLimit(int limit) { + m_limit = limit; + return *this; +} + +} diff --git a/src/stats_container_cmd.cc b/src/stats_container_cmd.cc new file mode 100644 index 0000000..919b0bc --- /dev/null +++ b/src/stats_container_cmd.cc @@ -0,0 +1,31 @@ +#include "stats_container_cmd.hh" + +namespace dockercpp::command { + +StatsContainerCmdImpl::StatsContainerCmdImpl(std::unique_ptr exec) + : AbstrDockerCmd>(std::move(exec)), + m_noStream(false) {} + +std::vector StatsContainerCmdImpl::exec() { + return m_execution->exec(shared_from_this()); +} + +std::string StatsContainerCmdImpl::getContainerId() const { + return m_containerId; +} + +StatsContainerCmd& StatsContainerCmdImpl::withContainerId(const std::string& id) { + m_containerId = id; + return *this; +} + +bool StatsContainerCmdImpl::isNoStream() const { + return m_noStream; +} + +StatsContainerCmd& StatsContainerCmdImpl::withNoStream(bool noStream) { + m_noStream = noStream; + return *this; +} + +} diff --git a/src/update_container_cmd.cc b/src/update_container_cmd.cc new file mode 100644 index 0000000..1a6a491 --- /dev/null +++ b/src/update_container_cmd.cc @@ -0,0 +1,60 @@ +#include "update_container_cmd.hh" + +namespace dockercpp::command { + +UpdateContainerCmdImpl::UpdateContainerCmdImpl(std::unique_ptr exec) + : AbstrDockerCmd(std::move(exec)), + m_cpuShares(0), + m_memory(0), + m_memorySwap(0) {} + +UpdateContainerResponse UpdateContainerCmdImpl::exec() { + return m_execution->exec(shared_from_this()); +} + +std::string UpdateContainerCmdImpl::getContainerId() const { + return m_containerId; +} + +UpdateContainerCmd& UpdateContainerCmdImpl::withContainerId(const std::string& id) { + m_containerId = id; + return *this; +} + +int UpdateContainerCmdImpl::getCpuShares() const { + return m_cpuShares; +} + +UpdateContainerCmd& UpdateContainerCmdImpl::withCpuShares(int cpuShares) { + m_cpuShares = cpuShares; + return *this; +} + +int64_t UpdateContainerCmdImpl::getMemory() const { + return m_memory; +} + +UpdateContainerCmd& UpdateContainerCmdImpl::withMemory(int64_t memory) { + m_memory = memory; + return *this; +} + +int UpdateContainerCmdImpl::getMemorySwap() const { + return m_memorySwap; +} + +UpdateContainerCmd& UpdateContainerCmdImpl::withMemorySwap(int memorySwap) { + m_memorySwap = memorySwap; + return *this; +} + +std::vector UpdateContainerCmdImpl::getCpusetCpus() const { + return m_cpusetCpus; +} + +UpdateContainerCmd& UpdateContainerCmdImpl::withCpusetCpus(const std::vector& cpusetCpus) { + m_cpusetCpus = cpusetCpus; + return *this; +} + +} diff --git a/src/update_container_cmd_exec.cc b/src/update_container_cmd_exec.cc new file mode 100644 index 0000000..191e8ad --- /dev/null +++ b/src/update_container_cmd_exec.cc @@ -0,0 +1,31 @@ +#include "update_container_cmd_exec.hh" + +#include "abstr_sync_docker_cmd_exec.hh" +#include "webtarget.hh" + +#include + +namespace dockercpp::command::exec { + +UpdateContainerCmdExec::UpdateContainerCmdExec() + : AbstrSyncDockerCmdExec(), + update::Exec() {} + +dockercpp::command::UpdateContainerResponse UpdateContainerCmdExec::exec( + std::shared_ptr command) { + return execute(command); +} + +dockercpp::command::UpdateContainerResponse UpdateContainerCmdExec::execute( + std::shared_ptr command) { + core::WebTarget webResource = + m_webTarget->path("/containers/" + command->getContainerId() + "/update"); + + std::string emptyBoyd = ""; + // no body + std::string response = webResource.request().post(emptyBoyd); + + return dockercpp::command::UpdateContainerResponse{}; +} + +} // namespace dockercpp::command::exec diff --git a/src/webtarget.cc b/src/webtarget.cc index 8e036ef..e39d7d5 100644 --- a/src/webtarget.cc +++ b/src/webtarget.cc @@ -46,6 +46,12 @@ std::string InvocationBuilder::post(std::string& json) { } bool InvocationBuilder::deletehttp() { + auto [response, code] = deletehttp_with_code(); + return code == 200; +} + +std::pair InvocationBuilder::deletehttp_with_code() { + spdlog::info("Delete requested"); transport::http::Request request = transport::http::Request::make() .withMethod(transport::http::Request::Method::DELETE) @@ -57,10 +63,8 @@ bool InvocationBuilder::deletehttp() { .withConnectTimeout(10) .withReadTimeout(10); - // TODO sort return type - client.execute(request); - - return true; + auto response = client.execute(request); + return {response.getBody(), response.getStatusCode()}; } InvocationBuilder WebTarget::request() { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4960c2c..59f8969 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,13 @@ add_executable( curl_docker_http_client_test.cc create_container_response_test.cc create_container_cmd_test.cc + events_cmd_test.cc + events_cmd_exec_test.cc + exec_create_cmd_test.cc + exec_create_cmd_exec_test.cc + search_images_cmd_test.cc + stats_container_cmd_test.cc + update_container_cmd_test.cc inspect_container_response_test.cc remove_image_cmd_exec_test.cc version_test.cc diff --git a/tests/curl_docker_http_client_test.cc b/tests/curl_docker_http_client_test.cc index 6faa456..b33a197 100644 --- a/tests/curl_docker_http_client_test.cc +++ b/tests/curl_docker_http_client_test.cc @@ -3,20 +3,13 @@ #include #include -#include "../src/version_cmd_exec.hh" #include "docker_client.hh" #include "info.hh" -#include "ping_cmd.hh" -#include "ping_cmd_exec.hh" -#include "remove_image_cmd.hh" #include "version.hh" -#include "version_cmd.hh" namespace dockercpp::transport::test { TEST(Curl_Docker_Client_Test, ping_ok) { - spdlog::info("running boost beast test"); - dockercpp::transport::http::CurlDockerHttpClient client = dockercpp::transport::http::CurlDockerHttpClient::make() .withDockerHost("") @@ -129,7 +122,7 @@ TEST(Curl_Docker_Client_Test, start_and_stop_container) { auto deletecontainer = dockerclient.removeContainerCmd(response.id)->exec(); spdlog::info("delete"); - auto deleteimage = dockerclient.removeImageCmd("busybox")->exec(); + auto deleteimage = dockerclient.removeImageCmd("busybox:1.36")->exec(); } } // namespace dockercpp::transport::test diff --git a/tests/docker_client_test.cc b/tests/docker_client_test.cc index 7ea18c1..d3e4bad 100644 --- a/tests/docker_client_test.cc +++ b/tests/docker_client_test.cc @@ -1,12 +1,10 @@ -#include "docker_client.hh" #include #include -#include - #include "docker_client_config.hh" + namespace dockercpp::test { TEST(Client_Test, BasicAssertions) { diff --git a/tests/events_cmd_exec_test.cc b/tests/events_cmd_exec_test.cc new file mode 100644 index 0000000..67d8c30 --- /dev/null +++ b/tests/events_cmd_exec_test.cc @@ -0,0 +1,22 @@ +#include "events_cmd_exec.hh" + +#include +#include + +#include + +#include "events_cmd.hh" + +namespace dockercpp::command::exec { + +TEST(EventsTest, create) { + auto eventsExec = std::make_unique(); + + auto eventsCmd = std::make_shared(std::move(eventsExec)); + eventsCmd->withSince(1234567890) + .withUntil(2234567890); + + ASSERT_EQ(2234567890, eventsCmd->getUntil()); +} + +} // namespace dockercpp::command::exec diff --git a/tests/events_cmd_test.cc b/tests/events_cmd_test.cc new file mode 100644 index 0000000..f43e816 --- /dev/null +++ b/tests/events_cmd_test.cc @@ -0,0 +1,239 @@ +#include +#include +#include +#include + +#include "docker_client.hh" +#include "events_cmd.hh" +#include "events_cmd_exec.hh" +#include "create_container_cmd.hh" +#include "pull_image_cmd.hh" +#include "start_container_cmd.hh" +#include "stop_container_cmd.hh" +#include "remove_container_cmd.hh" +#include "remove_image_cmd.hh" + +namespace dockercpp::command::test { + +// Unit tests for EventsCmd +class EventsCmdUnitTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test environment + } + + void TearDown() override { + // Clean up after each test + } +}; + +TEST_F(EventsCmdUnitTest, TestEventsCmdWithSince) { + auto eventsExec = std::make_unique(); + auto cmd = std::make_shared(std::move(eventsExec)); + + int64_t testSince = 1234567890; + cmd->withSince(testSince); + + EXPECT_EQ(cmd->getSince(), testSince); +} + +TEST_F(EventsCmdUnitTest, TestEventsCmdWithUntil) { + auto eventsExec = std::make_unique(); + auto cmd = std::make_shared(std::move(eventsExec)); + + int64_t testUntil = 1234567890; + cmd->withUntil(testUntil); + + EXPECT_EQ(cmd->getUntil(), testUntil); +} + +TEST_F(EventsCmdUnitTest, TestEventsCmdChaining) { + auto eventsExec = std::make_unique(); + auto cmd = std::make_shared(std::move(eventsExec)); + + int64_t testSince = 1234567890; + int64_t testUntil = 2234567890; + + cmd->withSince(testSince).withUntil(testUntil); + + EXPECT_EQ(cmd->getSince(), testSince); + EXPECT_EQ(cmd->getUntil(), testUntil); +} + +// Integration tests for EventsCmd +class EventsCmdIT : public ::testing::Test { +protected: + void SetUp() override { + dockerClient = std::make_unique(); + } + + void TearDown() override { + dockerClient.reset(); + } + + std::unique_ptr dockerClient; +}; + +TEST_F(EventsCmdIT, testEventStreamTimeBound) { + // Get the current time for since - add a small buffer + int64_t since = std::time(nullptr) - 2; + + // Pull an image + dockerClient->pullImageCmd("busybox") + ->withTag("1.36") + .exec(); + + // Create a container + auto createResponse = dockerClient->createContainerCmd("busybox:1.36") + ->withCmd(std::vector{"top"}) + .withName("test-events") + .exec(); + + std::string containerId = createResponse.id; + + // Start container + dockerClient->startContainerCmd(containerId)->exec(); + + // Stop container + dockerClient->stopContainerCmd(containerId)->exec(); + + // Remove container + dockerClient->removeContainerCmd(containerId) + ->withForce(true) + .exec(); + + // Remove image + dockerClient->removeImageCmd("busybox:1.36")->exec(); + + // Get events - give it a few extra seconds + int64_t until = std::time(nullptr) + 2; + + auto events = dockerClient->eventsCmd() + ->withSince(since) + .withUntil(until) + .exec(); + + // Verify we got the expected events + bool foundPull = false; + bool foundCreate = false; + bool foundStart = false; + bool foundStop = false; + + for (const auto& event : events) { + if (event.type == "image" && event.action == "pull") { + foundPull = true; + } else if (event.type == "container" && event.action == "create") { + foundCreate = true; + } else if (event.type == "container" && event.action == "start") { + foundStart = true; + } else if (event.type == "container" && event.action == "stop") { + foundStop = true; + } + } + + EXPECT_TRUE(foundPull) << "Should have found image pull event"; + EXPECT_TRUE(foundCreate) << "Should have found container create event"; + EXPECT_TRUE(foundStart) << "Should have found container start event"; + EXPECT_TRUE(foundStop) << "Should have found container stop event"; +} + +TEST_F(EventsCmdIT, testEventStreamWithFilter) { + // Pull image first + dockerClient->pullImageCmd("busybox") + ->withTag("1.36") + .exec(); + + // Get the current time for since + int64_t since = std::time(nullptr); + + // Create container + auto createResponse = dockerClient->createContainerCmd("busybox:1.36") + ->withCmd(std::vector{"top"}) + .withName("test-events-filtered") + .exec(); + + std::string containerId = createResponse.id; + + // Start container + dockerClient->startContainerCmd(containerId)->exec(); + + // Stop container + dockerClient->stopContainerCmd(containerId)->exec(); + + int64_t until = std::time(nullptr); + + // Get events with container filter + auto events = dockerClient->eventsCmd() + ->withSince(since) + .withUntil(until) + .withContainerFilter("test-events-filtered") + .exec(); + + // Should only get events for our container + for (const auto& event : events) { + EXPECT_EQ(event.type, "container"); + EXPECT_EQ(event.actor.attributes.at("name"), "test-events-filtered"); + } + + // Cleanup + dockerClient->removeContainerCmd(containerId) + ->withForce(true) + .exec(); +} + +TEST_F(EventsCmdIT, testEventStreamsMultipleFilters) { + // Pull image first + dockerClient->pullImageCmd("busybox") + ->withTag("1.36") + .exec(); + + // Get the current time for since + int64_t since = std::time(nullptr) - 2; + + // Create container with specific label + auto createResponse = dockerClient->createContainerCmd("busybox:1.36") + ->withCmd(std::vector{"top"}) + .withName("test-events-multi-filter") + .exec(); + + std::string containerId = createResponse.id; + + // Start container + dockerClient->startContainerCmd(containerId)->exec(); + + // Stop container + dockerClient->stopContainerCmd(containerId)->exec(); + + int64_t until = std::time(nullptr) + 2; + + // Get all events in the time range + auto events = dockerClient->eventsCmd() + ->withSince(since) + .withUntil(until) + .exec(); + + // Verify we got some events + EXPECT_FALSE(events.empty()) << "Should have received events"; + + // Verify events match our container + bool foundCreate = false; + bool foundStart = false; + for (const auto& event : events) { + if (event.type == "container" && event.action == "create") { + foundCreate = true; + } else if (event.type == "container" && event.action == "start") { + foundStart = true; + } + } + + EXPECT_TRUE(foundCreate) << "Should have found container create event"; + EXPECT_TRUE(foundStart) << "Should have found container start event"; + + // Cleanup + dockerClient->removeContainerCmd(containerId) + ->withForce(true) + .exec(); +} + +} // namespace dockercpp::command::test + diff --git a/tests/exec_create_cmd_exec_test.cc b/tests/exec_create_cmd_exec_test.cc new file mode 100644 index 0000000..4340a3d --- /dev/null +++ b/tests/exec_create_cmd_exec_test.cc @@ -0,0 +1,25 @@ +#include "../src/exec_create_cmd_exec.hh" + +#include +#include + +#include + +#include "exec_create_cmd.hh" + +namespace dockercpp::command::exec { + +TEST(ExecCreateTest, create) { + auto execCreateExec = std::make_unique(); + + auto execCreateCmd = std::make_shared(std::move(execCreateExec)); + execCreateCmd->withContainerId("test_container_123") + .withCmd({"/bin/bash", "-c", "echo hello"}) + .withUser("testuser") + .withAttachStdin(true) + .withTty(true); + + ASSERT_NO_THROW(execCreateCmd->exec()); +} + +} // namespace dockercpp::command::exec diff --git a/tests/exec_create_cmd_test.cc b/tests/exec_create_cmd_test.cc new file mode 100644 index 0000000..d68043f --- /dev/null +++ b/tests/exec_create_cmd_test.cc @@ -0,0 +1,61 @@ +#include +#include + +#include "exec_create_cmd.hh" +#include "docker_client.hh" +#include "../src/exec_create_cmd_exec.hh" + +using namespace dockercpp::command; + +class ExecCreateCmdTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test environment + } + + void TearDown() override { + // Clean up after each test + } +}; + +TEST_F(ExecCreateCmdTest, TestExecCreateCmdBasicProperties) { + std::unique_ptr exec = std::make_unique(); + ExecCreateCmdImpl cmd(std::move(exec)); + + std::string testContainerId = "test_container_123"; + std::vector testCmd = {"/bin/bash", "-c", "echo hello"}; + std::string testUser = "testuser"; + + cmd.withContainerId(testContainerId) + .withCmd(testCmd) + .withUser(testUser); + + EXPECT_EQ(cmd.getContainerId(), testContainerId); + EXPECT_EQ(cmd.getCmd(), testCmd); + EXPECT_EQ(cmd.getUser(), testUser); +} + +TEST_F(ExecCreateCmdTest, TestExecCreateCmdFlags) { + std::unique_ptr exec = std::make_unique(); + ExecCreateCmdImpl cmd(std::move(exec)); + + // Test default values + EXPECT_FALSE(cmd.isAttachStdin()); + EXPECT_TRUE(cmd.isAttachStdout()); + EXPECT_TRUE(cmd.isAttachStderr()); + EXPECT_FALSE(cmd.isTty()); + EXPECT_FALSE(cmd.isPrivileged()); + + // Test setting values + cmd.withAttachStdin(true) + .withAttachStdout(false) + .withAttachStderr(false) + .withTty(true) + .withPrivileged(true); + + EXPECT_TRUE(cmd.isAttachStdin()); + EXPECT_FALSE(cmd.isAttachStdout()); + EXPECT_FALSE(cmd.isAttachStderr()); + EXPECT_TRUE(cmd.isTty()); + EXPECT_TRUE(cmd.isPrivileged()); +} diff --git a/tests/remove_image_cmd_exec_test.cc b/tests/remove_image_cmd_exec_test.cc index 9a31239..f38cff2 100644 --- a/tests/remove_image_cmd_exec_test.cc +++ b/tests/remove_image_cmd_exec_test.cc @@ -1,21 +1,111 @@ -#include "../src/remove_image_cmd_exec.hh" - -#include #include - #include +#include +#include +#include +#include "docker_client.hh" #include "remove_image_cmd.hh" +#include "pull_image_cmd.hh" +#include "inspect_image_cmd.hh" +#include "../src/remove_image_cmd_exec.hh" + +namespace dockercpp::command::test { + +// Unit tests for RemoveImageCmd +class RemoveImageCmdTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test environment + } + + void TearDown() override { + // Clean up after each test + } +}; + +TEST_F(RemoveImageCmdTest, TestBasicRemoveImageCmd) { + auto removeImageExec = std::make_unique(); + auto cmd = std::make_shared( + std::move(removeImageExec), "test_image"); + + EXPECT_EQ(cmd->getImageId(), "test_image"); +} -namespace dockercpp::command::exec { +// Integration tests for RemoveImageCmd +class RemoveImageCmdIT : public ::testing::Test { +protected: + void SetUp() override { + dockerClient = std::make_unique(); + // Pull an image we know exists + dockerClient->pullImageCmd("busybox") + ->withTag("1.36") + .exec(); + } + + void TearDown() override { + try { + // Try to remove the test image if it exists + dockerClient->removeImageCmd("busybox:1.36") + ->withForce(true) + .exec(); + } catch (...) { + // Ignore any errors during cleanup + } + dockerClient.reset(); + } + + std::unique_ptr dockerClient; +}; + +TEST_F(RemoveImageCmdIT, testRemoveImage) { + // Pull another tag of busybox to ensure we have it + dockerClient->pullImageCmd("busybox") + ->withTag("1.35") + .exec(); + + // Verify the image exists + auto inspectResult = dockerClient->inspectImageCmd("busybox:1.35")->exec(); + EXPECT_FALSE(inspectResult.id.empty()); + + // Remove the image + ASSERT_NO_THROW( + dockerClient->removeImageCmd("busybox:1.35")->exec() + ); + + // Verify the image is gone - should throw exception + EXPECT_THROW( + dockerClient->inspectImageCmd("busybox:1.35")->exec(), + std::exception + ); +} + +TEST_F(RemoveImageCmdIT, testRemoveNonExistingImage) { + // Try to remove a non-existing image + EXPECT_THROW( + dockerClient->removeImageCmd("non_existing_image:latest")->exec(), + std::exception + ); +} -TEST(removeImageTest, create) { - auto removeImageExec = std::make_unique(); +TEST_F(RemoveImageCmdIT, testRemoveImageWithForce) { + // Pull another tag of busybox + dockerClient->pullImageCmd("busybox") + ->withTag("1.34") + .exec(); - auto removeImage = std::make_shared( - std::move(removeImageExec), "busybox"); + // Force remove the image + ASSERT_NO_THROW( + dockerClient->removeImageCmd("busybox:1.34") + ->withForce(true) + .exec() + ); - auto result = removeImage->exec(); + // Verify the image is gone + EXPECT_THROW( + dockerClient->inspectImageCmd("busybox:1.34")->exec(), + std::exception + ); } -} // namespace dockercpp::command::exec +} // namespace dockercpp::command::test diff --git a/tests/search_images_cmd_test.cc b/tests/search_images_cmd_test.cc new file mode 100644 index 0000000..375e78b --- /dev/null +++ b/tests/search_images_cmd_test.cc @@ -0,0 +1,51 @@ +#include +#include + +#include "search_images_cmd.hh" +#include "docker_client.hh" + +using namespace dockercpp::command; + +class SearchImagesCmdTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test environment + } + + void TearDown() override { + // Clean up after each test + } +}; + +// TEST_F(SearchImagesCmdTest, TestSearchImagesCmdWithTerm) { +// std::unique_ptr exec = std::make_unique(); +// SearchImagesCmdImpl cmd(std::move(exec)); + +// std::string testTerm = "nginx"; +// cmd.withTerm(testTerm); + +// EXPECT_EQ(cmd.getTerm(), testTerm); +// } + +// TEST_F(SearchImagesCmdTest, TestSearchImagesCmdWithLimit) { +// std::unique_ptr exec = std::make_unique(); +// SearchImagesCmdImpl cmd(std::move(exec)); + +// int testLimit = 10; +// cmd.withLimit(testLimit); + +// EXPECT_EQ(cmd.getLimit(), testLimit); +// } + +// TEST_F(SearchImagesCmdTest, TestSearchImagesCmdChaining) { +// std::unique_ptr exec = std::make_unique(); +// SearchImagesCmdImpl cmd(std::move(exec)); + +// std::string testTerm = "ubuntu"; +// int testLimit = 5; + +// cmd.withTerm(testTerm).withLimit(testLimit); + +// EXPECT_EQ(cmd.getTerm(), testTerm); +// EXPECT_EQ(cmd.getLimit(), testLimit); +// } diff --git a/tests/stats_container_cmd_test.cc b/tests/stats_container_cmd_test.cc new file mode 100644 index 0000000..149d6ff --- /dev/null +++ b/tests/stats_container_cmd_test.cc @@ -0,0 +1,51 @@ +#include +#include + +#include "stats_container_cmd.hh" +#include "docker_client.hh" + +using namespace dockercpp::command; + +class StatsContainerCmdTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test environment + } + + void TearDown() override { + // Clean up after each test + } +}; + +// TEST_F(StatsContainerCmdTest, TestStatsContainerCmdBasicProperties) { +// std::unique_ptr exec = std::make_unique(); +// StatsContainerCmdImpl cmd(std::move(exec)); + +// std::string testContainerId = "test_container_123"; +// cmd.withContainerId(testContainerId); + +// EXPECT_EQ(cmd.getContainerId(), testContainerId); +// } + +// TEST_F(StatsContainerCmdTest, TestStatsContainerCmdNoStream) { +// std::unique_ptr exec = std::make_unique(); +// StatsContainerCmdImpl cmd(std::move(exec)); + +// // Test default value +// EXPECT_FALSE(cmd.isNoStream()); + +// // Test setting value +// cmd.withNoStream(true); +// EXPECT_TRUE(cmd.isNoStream()); +// } + +// TEST_F(StatsContainerCmdTest, TestStatsContainerCmdChaining) { +// std::unique_ptr exec = std::make_unique(); +// StatsContainerCmdImpl cmd(std::move(exec)); + +// std::string testContainerId = "test_container_123"; +// cmd.withContainerId(testContainerId).withNoStream(true); + +// EXPECT_EQ(cmd.getContainerId(), testContainerId); +// EXPECT_TRUE(cmd.isNoStream()); +// } diff --git a/tests/update_container_cmd_test.cc b/tests/update_container_cmd_test.cc new file mode 100644 index 0000000..1184f79 --- /dev/null +++ b/tests/update_container_cmd_test.cc @@ -0,0 +1,72 @@ +#include +#include + +#include "update_container_cmd.hh" +#include "update_container_cmd_exec.hh" + +using namespace dockercpp::command; + +class UpdateContainerCmdTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test environment + } + + void TearDown() override { + // Clean up after each test + } +}; + +TEST_F(UpdateContainerCmdTest, TestUpdateContainerCmdBasicProperties) { + std::unique_ptr exec = std::make_unique(); + UpdateContainerCmdImpl cmd(std::move(exec)); + + std::string testContainerId = "test_container_123"; + cmd.withContainerId(testContainerId); + + EXPECT_EQ(cmd.getContainerId(), testContainerId); +} + +TEST_F(UpdateContainerCmdTest, TestUpdateContainerCmdResourceLimits) { + std::unique_ptr exec = std::make_unique(); + UpdateContainerCmdImpl cmd(std::move(exec)); + + int testCpuShares = 512; + int64_t testMemory = 1024 * 1024 * 1024; // 1GB + int testMemorySwap = 2048; + + cmd.withCpuShares(testCpuShares) + .withMemory(testMemory) + .withMemorySwap(testMemorySwap); + + EXPECT_EQ(cmd.getCpuShares(), testCpuShares); + EXPECT_EQ(cmd.getMemory(), testMemory); + EXPECT_EQ(cmd.getMemorySwap(), testMemorySwap); +} + +TEST_F(UpdateContainerCmdTest, TestUpdateContainerCmdCpuSet) { + std::unique_ptr exec = std::make_unique(); + UpdateContainerCmdImpl cmd(std::move(exec)); + + std::vector testCpusetCpus = {"0", "1", "2"}; + cmd.withCpusetCpus(testCpusetCpus); + + EXPECT_EQ(cmd.getCpusetCpus(), testCpusetCpus); +} + +TEST_F(UpdateContainerCmdTest, TestUpdateContainerCmdChaining) { + std::unique_ptr exec = std::make_unique(); + UpdateContainerCmdImpl cmd(std::move(exec)); + + std::string testContainerId = "test_container_123"; + int testCpuShares = 256; + int64_t testMemory = 512 * 1024 * 1024; + + cmd.withContainerId(testContainerId) + .withCpuShares(testCpuShares) + .withMemory(testMemory); + + EXPECT_EQ(cmd.getContainerId(), testContainerId); + EXPECT_EQ(cmd.getCpuShares(), testCpuShares); + EXPECT_EQ(cmd.getMemory(), testMemory); +}