ruby_llm-docker 0.0.1 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +255 -11
- data/Rakefile +3 -3
- data/examples/docker_chat.rb +228 -0
- data/examples/list_containers.rb +36 -0
- data/examples/test_chat.rb +81 -0
- data/lib/ruby_llm/docker/build_image.rb +111 -0
- data/lib/ruby_llm/docker/copy_to_container.rb +196 -0
- data/lib/ruby_llm/docker/create_container.rb +146 -0
- data/lib/ruby_llm/docker/create_network.rb +131 -0
- data/lib/ruby_llm/docker/create_volume.rb +129 -0
- data/lib/ruby_llm/docker/exec_container.rb +185 -0
- data/lib/ruby_llm/docker/fetch_container_logs.rb +132 -0
- data/lib/ruby_llm/docker/list_containers.rb +59 -0
- data/lib/ruby_llm/docker/list_images.rb +79 -0
- data/lib/ruby_llm/docker/list_networks.rb +81 -0
- data/lib/ruby_llm/docker/list_volumes.rb +88 -0
- data/lib/ruby_llm/docker/pull_image.rb +127 -0
- data/lib/ruby_llm/docker/push_image.rb +153 -0
- data/lib/ruby_llm/docker/recreate_container.rb +151 -0
- data/lib/ruby_llm/docker/remove_container.rb +120 -0
- data/lib/ruby_llm/docker/remove_image.rb +142 -0
- data/lib/ruby_llm/docker/remove_network.rb +120 -0
- data/lib/ruby_llm/docker/remove_volume.rb +127 -0
- data/lib/ruby_llm/docker/run_container.rb +104 -0
- data/lib/ruby_llm/docker/start_container.rb +97 -0
- data/lib/ruby_llm/docker/stop_container.rb +109 -0
- data/lib/ruby_llm/docker/tag_image.rb +139 -0
- data/lib/ruby_llm/docker/version.rb +2 -2
- data/lib/ruby_llm/docker.rb +53 -3
- data/sig/ruby_llm/docker.rbs +1 -1
- metadata +90 -5
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# RubyLLM tool for listing Docker volumes.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides functionality to list all Docker volumes on the system,
|
|
8
|
+
# including both named volumes and anonymous volumes. It returns comprehensive
|
|
9
|
+
# information about volume configuration, drivers, mount points, and usage.
|
|
10
|
+
#
|
|
11
|
+
# == Features
|
|
12
|
+
#
|
|
13
|
+
# - List all Docker volumes on the system
|
|
14
|
+
# - Comprehensive volume metadata
|
|
15
|
+
# - No configuration required
|
|
16
|
+
# - Read-only operation
|
|
17
|
+
# - Includes named and anonymous volumes
|
|
18
|
+
#
|
|
19
|
+
# == Volume Information Included
|
|
20
|
+
#
|
|
21
|
+
# The response typically includes:
|
|
22
|
+
# - **Volume Name**: Unique identifier for the volume
|
|
23
|
+
# - **Driver**: Volume driver (local, nfs, etc.)
|
|
24
|
+
# - **Mountpoint**: Physical location on host filesystem
|
|
25
|
+
# - **Labels**: User-defined metadata labels
|
|
26
|
+
# - **Options**: Driver-specific configuration options
|
|
27
|
+
# - **Scope**: Volume scope (local, global)
|
|
28
|
+
# - **CreatedAt**: Volume creation timestamp
|
|
29
|
+
#
|
|
30
|
+
# == Volume Types
|
|
31
|
+
#
|
|
32
|
+
# Docker manages different types of volumes:
|
|
33
|
+
# - **Named Volumes**: User-created persistent volumes
|
|
34
|
+
# - **Anonymous Volumes**: Automatically created temporary volumes
|
|
35
|
+
# - **Bind Mounts**: Direct host directory mounts (not shown in volume list)
|
|
36
|
+
# - **tmpfs Mounts**: Memory-based temporary filesystems (not shown)
|
|
37
|
+
#
|
|
38
|
+
# == Security Considerations
|
|
39
|
+
#
|
|
40
|
+
# This is a read-only operation that reveals storage information:
|
|
41
|
+
# - Exposes data storage architecture
|
|
42
|
+
# - Shows volume naming patterns
|
|
43
|
+
# - May reveal application data locations
|
|
44
|
+
# - Could aid in data discovery attacks
|
|
45
|
+
#
|
|
46
|
+
# While generally safe, consider access control:
|
|
47
|
+
# - Limit exposure of volume inventory
|
|
48
|
+
# - Be cautious with sensitive volume names
|
|
49
|
+
# - Monitor for unauthorized volume discovery
|
|
50
|
+
# - Consider data classification implications
|
|
51
|
+
#
|
|
52
|
+
# == Example Usage
|
|
53
|
+
#
|
|
54
|
+
# # List all volumes
|
|
55
|
+
# ListVolumes.call(server_context: context)
|
|
56
|
+
#
|
|
57
|
+
# @example Usage in data management
|
|
58
|
+
# # Get available volumes before container creation
|
|
59
|
+
# volumes_response = ListVolumes.call(server_context: context)
|
|
60
|
+
# # Use volume information to select appropriate storage
|
|
61
|
+
#
|
|
62
|
+
# @see CreateVolume
|
|
63
|
+
# @see RemoveVolume
|
|
64
|
+
# @see Docker::Volume.all
|
|
65
|
+
# @since 0.1.0
|
|
66
|
+
class ListVolumes < RubyLLM::Tool
|
|
67
|
+
description 'List Docker volumes'
|
|
68
|
+
|
|
69
|
+
# List all Docker volumes available on the system.
|
|
70
|
+
#
|
|
71
|
+
# This method retrieves information about all Docker volumes, including
|
|
72
|
+
# both named volumes created by users and anonymous volumes created
|
|
73
|
+
# automatically by containers. The information includes comprehensive
|
|
74
|
+
# metadata for each volume.
|
|
75
|
+
#
|
|
76
|
+
# @return [String] comprehensive volume information
|
|
77
|
+
#
|
|
78
|
+
# @example List all volumes
|
|
79
|
+
# response = tool.execute
|
|
80
|
+
# # Returns detailed info for all Docker volumes
|
|
81
|
+
#
|
|
82
|
+
# @see Docker::Volume.all
|
|
83
|
+
def execute
|
|
84
|
+
::Docker::Volume.all.map(&:info).to_s
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# RubyLLM tool for pulling Docker images from registries.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides the ability to download Docker images from Docker
|
|
8
|
+
# registries (like Docker Hub) to the local system. It supports flexible
|
|
9
|
+
# tag specification and handles various image naming conventions.
|
|
10
|
+
#
|
|
11
|
+
# == Features
|
|
12
|
+
#
|
|
13
|
+
# - Pull images from any accessible Docker registry
|
|
14
|
+
# - Flexible tag specification (explicit or default)
|
|
15
|
+
# - Support for official and user repositories
|
|
16
|
+
# - Automatic latest tag handling
|
|
17
|
+
# - Comprehensive error handling
|
|
18
|
+
# - Progress tracking through Docker daemon
|
|
19
|
+
#
|
|
20
|
+
# == Security Considerations
|
|
21
|
+
#
|
|
22
|
+
# Pulling images can introduce security risks:
|
|
23
|
+
# - **Malicious Images**: Images may contain malware or backdoors
|
|
24
|
+
# - **Vulnerable Software**: Images may have known security vulnerabilities
|
|
25
|
+
# - **Untrusted Sources**: Images from unknown publishers may be compromised
|
|
26
|
+
# - **Supply Chain Attacks**: Legitimate-looking images may be malicious
|
|
27
|
+
# - **Resource Consumption**: Large images can consume significant disk space
|
|
28
|
+
#
|
|
29
|
+
# Security recommendations:
|
|
30
|
+
# - Only pull images from trusted registries and publishers
|
|
31
|
+
# - Verify image signatures when available
|
|
32
|
+
# - Scan pulled images for vulnerabilities
|
|
33
|
+
# - Use specific tags rather than 'latest'
|
|
34
|
+
# - Monitor registry access and authentication
|
|
35
|
+
# - Regularly update and patch images
|
|
36
|
+
#
|
|
37
|
+
# == Tag Handling
|
|
38
|
+
#
|
|
39
|
+
# The tool handles tags intelligently:
|
|
40
|
+
# - If image includes tag (e.g., "nginx:1.21"), use as specified
|
|
41
|
+
# - If separate tag provided, append to image name
|
|
42
|
+
# - If no tag specified, default to "latest"
|
|
43
|
+
# - Supports all Docker tag conventions
|
|
44
|
+
#
|
|
45
|
+
# == Example Usage
|
|
46
|
+
#
|
|
47
|
+
# # Pull latest version
|
|
48
|
+
# PullImage.call(
|
|
49
|
+
# server_context: context,
|
|
50
|
+
# from_image: "nginx"
|
|
51
|
+
# )
|
|
52
|
+
#
|
|
53
|
+
# # Pull specific version
|
|
54
|
+
# PullImage.call(
|
|
55
|
+
# server_context: context,
|
|
56
|
+
# from_image: "postgres:13"
|
|
57
|
+
# )
|
|
58
|
+
#
|
|
59
|
+
# # Pull with separate tag parameter
|
|
60
|
+
# PullImage.call(
|
|
61
|
+
# server_context: context,
|
|
62
|
+
# from_image: "redis",
|
|
63
|
+
# tag: "7-alpine"
|
|
64
|
+
# )
|
|
65
|
+
#
|
|
66
|
+
# @see BuildImage
|
|
67
|
+
# @see ListImages
|
|
68
|
+
# @see Docker::Image.create
|
|
69
|
+
# @since 0.1.0
|
|
70
|
+
class PullImage < RubyLLM::Tool
|
|
71
|
+
description 'Pull a Docker image'
|
|
72
|
+
|
|
73
|
+
param :from_image, desc: 'Image name to pull (e.g., "ubuntu" or "ubuntu:22.04")'
|
|
74
|
+
param :tag, desc: 'Tag to pull (optional, defaults to "latest" if not specified in from_image)', required: false
|
|
75
|
+
|
|
76
|
+
# Pull a Docker image from a registry.
|
|
77
|
+
#
|
|
78
|
+
# This method downloads the specified image from a Docker registry to
|
|
79
|
+
# the local system. It handles tag resolution automatically and provides
|
|
80
|
+
# feedback on the pull operation status.
|
|
81
|
+
#
|
|
82
|
+
# @param from_image [String] image name (may include registry and tag)
|
|
83
|
+
# @param server_context [Object] RubyLLM context (unused but required)
|
|
84
|
+
# @param tag [String, nil] specific tag to pull (overrides tag in from_image)
|
|
85
|
+
#
|
|
86
|
+
# @return [RubyLLM::Tool::Response] pull operation results with image ID
|
|
87
|
+
#
|
|
88
|
+
# @raise [Docker::Error::NotFoundError] if image doesn't exist in registry
|
|
89
|
+
# @raise [StandardError] for network or authentication failures
|
|
90
|
+
#
|
|
91
|
+
# @example Pull official image
|
|
92
|
+
# response = PullImage.call(
|
|
93
|
+
# server_context: context,
|
|
94
|
+
# from_image: "ubuntu:22.04"
|
|
95
|
+
# )
|
|
96
|
+
#
|
|
97
|
+
# @example Pull from custom registry
|
|
98
|
+
# response = PullImage.call(
|
|
99
|
+
# server_context: context,
|
|
100
|
+
# from_image: "registry.example.com/myapp",
|
|
101
|
+
# tag: "v1.0"
|
|
102
|
+
# )
|
|
103
|
+
#
|
|
104
|
+
# @see Docker::Image.create
|
|
105
|
+
def execute(from_image:, tag: nil)
|
|
106
|
+
# If tag is provided separately, append it to from_image
|
|
107
|
+
# If from_image already has a tag (contains :), use as-is
|
|
108
|
+
# Otherwise default to :latest
|
|
109
|
+
image_with_tag = if tag
|
|
110
|
+
"#{from_image}:#{tag}"
|
|
111
|
+
elsif from_image.include?(':')
|
|
112
|
+
from_image
|
|
113
|
+
else
|
|
114
|
+
"#{from_image}:latest"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
image = ::Docker::Image.create('fromImage' => image_with_tag)
|
|
118
|
+
|
|
119
|
+
"Image #{image_with_tag} pulled successfully. ID: #{image.id}"
|
|
120
|
+
rescue ::Docker::Error::NotFoundError
|
|
121
|
+
"Image #{image_with_tag} not found"
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
"Error pulling image: #{e.message}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'open3'
|
|
5
|
+
|
|
6
|
+
module RubyLLM
|
|
7
|
+
module Docker
|
|
8
|
+
# RubyLLM tool for pushing Docker images to registries.
|
|
9
|
+
#
|
|
10
|
+
# This tool provides the ability to upload Docker images to Docker registries
|
|
11
|
+
# such as Docker Hub, private registries, or cloud-based container registries.
|
|
12
|
+
# It uses the Docker CLI to leverage native credential handling and push
|
|
13
|
+
# capabilities.
|
|
14
|
+
#
|
|
15
|
+
# == Features
|
|
16
|
+
#
|
|
17
|
+
# - Push images to any accessible Docker registry
|
|
18
|
+
# - Flexible image and tag specification
|
|
19
|
+
# - Native Docker credential handling
|
|
20
|
+
# - Support for private registries
|
|
21
|
+
# - Comprehensive error handling
|
|
22
|
+
# - Validation of registry-compatible names
|
|
23
|
+
#
|
|
24
|
+
# == ⚠️ Security Considerations ⚠️
|
|
25
|
+
#
|
|
26
|
+
# Pushing images involves significant security risks:
|
|
27
|
+
# - **Credential Exposure**: Registry credentials may be exposed
|
|
28
|
+
# - **Data Exfiltration**: Images may contain sensitive application data
|
|
29
|
+
# - **Intellectual Property**: Source code and binaries may be exposed
|
|
30
|
+
# - **Supply Chain Risk**: Malicious actors could access pushed images
|
|
31
|
+
# - **Registry Access**: Unauthorized access to registry accounts
|
|
32
|
+
#
|
|
33
|
+
# Critical security measures:
|
|
34
|
+
# - Verify registry authentication and authorization
|
|
35
|
+
# - Scan images for secrets before pushing
|
|
36
|
+
# - Use private registries for sensitive applications
|
|
37
|
+
# - Implement image signing and verification
|
|
38
|
+
# - Monitor registry access and downloads
|
|
39
|
+
# - Regularly audit pushed image contents
|
|
40
|
+
#
|
|
41
|
+
# == Registry Requirements
|
|
42
|
+
#
|
|
43
|
+
# Images must be properly tagged for registry compatibility:
|
|
44
|
+
# - Include registry hostname for private registries
|
|
45
|
+
# - Include username/organization for Docker Hub
|
|
46
|
+
# - Examples: `username/myapp`, `registry.company.com/team/app`
|
|
47
|
+
# - Local image names (without `/`) cannot be pushed
|
|
48
|
+
#
|
|
49
|
+
# == Example Usage
|
|
50
|
+
#
|
|
51
|
+
# # Push to Docker Hub
|
|
52
|
+
# PushImage.call(
|
|
53
|
+
# server_context: context,
|
|
54
|
+
# name: "myusername/myapp",
|
|
55
|
+
# tag: "v1.0"
|
|
56
|
+
# )
|
|
57
|
+
#
|
|
58
|
+
# # Push to private registry
|
|
59
|
+
# PushImage.call(
|
|
60
|
+
# server_context: context,
|
|
61
|
+
# name: "registry.company.com/team/app",
|
|
62
|
+
# tag: "latest"
|
|
63
|
+
# )
|
|
64
|
+
#
|
|
65
|
+
# # Push with full repo specification
|
|
66
|
+
# PushImage.call(
|
|
67
|
+
# server_context: context,
|
|
68
|
+
# name: "myapp",
|
|
69
|
+
# repo_tag: "myregistry.com/myuser/myapp:v2.0"
|
|
70
|
+
# )
|
|
71
|
+
#
|
|
72
|
+
# @see PullImage
|
|
73
|
+
# @see TagImage
|
|
74
|
+
# @see BuildImage
|
|
75
|
+
# @since 0.1.0
|
|
76
|
+
class PushImage < RubyLLM::Tool
|
|
77
|
+
description 'Push a Docker image'
|
|
78
|
+
|
|
79
|
+
param :name, type: :string, desc: 'Image name or ID to push'
|
|
80
|
+
param :tag, type: :string, desc: 'Tag to push (optional, pushes all tags if not specified)',
|
|
81
|
+
required: false
|
|
82
|
+
param :repo_tag, type: :string, desc: 'Full repo:tag to push (e.g., "registry/repo:tag") (optional)',
|
|
83
|
+
required: false
|
|
84
|
+
|
|
85
|
+
# Push a Docker image to a registry.
|
|
86
|
+
#
|
|
87
|
+
# This method uploads the specified image to a Docker registry using
|
|
88
|
+
# the Docker CLI for native credential handling. The image must be
|
|
89
|
+
# properly tagged for registry compatibility.
|
|
90
|
+
#
|
|
91
|
+
# @param name [String] image name or ID to push
|
|
92
|
+
# @param server_context [Object] RubyLLM context (unused but required)
|
|
93
|
+
# @param tag [String, nil] specific tag to push
|
|
94
|
+
# @param repo_tag [String, nil] complete repository:tag specification
|
|
95
|
+
#
|
|
96
|
+
# @return [RubyLLM::Tool::Response] push operation results
|
|
97
|
+
#
|
|
98
|
+
# @raise [StandardError] for push failures or authentication issues
|
|
99
|
+
#
|
|
100
|
+
# @example Push tagged image to Docker Hub
|
|
101
|
+
# response = PushImage.call(
|
|
102
|
+
# server_context: context,
|
|
103
|
+
# name: "myuser/webapp",
|
|
104
|
+
# tag: "v1.2.3"
|
|
105
|
+
# )
|
|
106
|
+
#
|
|
107
|
+
# @example Push to private registry
|
|
108
|
+
# response = tool.execute(
|
|
109
|
+
# name: "internal-registry.com/team/service",
|
|
110
|
+
# tag: "latest"
|
|
111
|
+
# )
|
|
112
|
+
#
|
|
113
|
+
# @see Docker::Image.get
|
|
114
|
+
def execute(name:, tag: nil, repo_tag: nil)
|
|
115
|
+
# Construct the full image identifier
|
|
116
|
+
image_identifier = tag ? "#{name}:#{tag}" : name
|
|
117
|
+
|
|
118
|
+
# Validate that the image name includes a registry/username
|
|
119
|
+
# Images without a registry prefix will fail to push to Docker Hub
|
|
120
|
+
unless name.include?('/') || repo_tag&.include?('/')
|
|
121
|
+
error_msg = 'Error: Image name must include registry/username ' \
|
|
122
|
+
"(e.g., 'username/#{name}'). Local images cannot be " \
|
|
123
|
+
'pushed without a registry prefix.'
|
|
124
|
+
return error_msg
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Verify the image exists
|
|
128
|
+
begin
|
|
129
|
+
::Docker::Image.get(image_identifier)
|
|
130
|
+
rescue ::Docker::Error::NotFoundError
|
|
131
|
+
return "Image #{image_identifier} not found"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Use the Docker CLI to push the image
|
|
135
|
+
# This way we leverage Docker's native credential handling
|
|
136
|
+
push_target = repo_tag || image_identifier
|
|
137
|
+
_, stderr, status = Open3.capture3('docker', 'push', push_target)
|
|
138
|
+
|
|
139
|
+
if status.success?
|
|
140
|
+
"Image #{push_target} pushed successfully"
|
|
141
|
+
else
|
|
142
|
+
# Extract the error message from stderr
|
|
143
|
+
error_msg = stderr.strip
|
|
144
|
+
error_msg = 'Failed to push image' if error_msg.empty?
|
|
145
|
+
|
|
146
|
+
"Error pushing image: #{error_msg}"
|
|
147
|
+
end
|
|
148
|
+
rescue StandardError => e
|
|
149
|
+
"Error pushing image: #{e.message}"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# RubyLLM tool for recreating Docker containers with the same configuration.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides a convenient way to recreate containers while preserving
|
|
8
|
+
# their original configuration. It stops and removes the existing container,
|
|
9
|
+
# then creates a new one with identical settings. This is useful for applying
|
|
10
|
+
# image updates, clearing container state, or resolving container issues.
|
|
11
|
+
#
|
|
12
|
+
# == Features
|
|
13
|
+
#
|
|
14
|
+
# - Preserves complete container configuration
|
|
15
|
+
# - Maintains original name and settings
|
|
16
|
+
# - Handles running containers gracefully
|
|
17
|
+
# - Restores running state after recreation
|
|
18
|
+
# - Configurable stop timeout
|
|
19
|
+
# - Comprehensive error handling
|
|
20
|
+
#
|
|
21
|
+
# == Process Overview
|
|
22
|
+
#
|
|
23
|
+
# 1. Retrieve existing container configuration
|
|
24
|
+
# 2. Stop container gracefully (if running)
|
|
25
|
+
# 3. Remove the old container
|
|
26
|
+
# 4. Create new container with identical config
|
|
27
|
+
# 5. Start new container (if original was running)
|
|
28
|
+
#
|
|
29
|
+
# == ⚠️ Data Loss Warning ⚠️
|
|
30
|
+
#
|
|
31
|
+
# **DESTRUCTIVE OPERATION - DATA LOSS POSSIBLE**
|
|
32
|
+
#
|
|
33
|
+
# This operation can cause permanent data loss:
|
|
34
|
+
# - Container filesystem changes are lost
|
|
35
|
+
# - Temporary data and logs are deleted
|
|
36
|
+
# - Container state is reset completely
|
|
37
|
+
# - Network connections are interrupted
|
|
38
|
+
# - Anonymous volumes may be recreated empty
|
|
39
|
+
#
|
|
40
|
+
# == Security Considerations
|
|
41
|
+
#
|
|
42
|
+
# - Original configuration is preserved exactly
|
|
43
|
+
# - Sensitive environment variables are maintained
|
|
44
|
+
# - Port mappings and volume mounts are restored
|
|
45
|
+
# - Network access patterns remain the same
|
|
46
|
+
#
|
|
47
|
+
# Ensure original configuration is still secure:
|
|
48
|
+
# - Review exposed ports and volumes
|
|
49
|
+
# - Validate environment variables
|
|
50
|
+
# - Check image security updates
|
|
51
|
+
# - Verify network policies
|
|
52
|
+
#
|
|
53
|
+
# == Example Usage
|
|
54
|
+
#
|
|
55
|
+
# # Recreate with default timeout
|
|
56
|
+
# RecreateContainer.call(
|
|
57
|
+
# server_context: context,
|
|
58
|
+
# id: "web-server"
|
|
59
|
+
# )
|
|
60
|
+
#
|
|
61
|
+
# # Recreate with longer stop timeout
|
|
62
|
+
# RecreateContainer.call(
|
|
63
|
+
# server_context: context,
|
|
64
|
+
# id: "database",
|
|
65
|
+
# timeout: 30
|
|
66
|
+
# )
|
|
67
|
+
#
|
|
68
|
+
# @see CreateContainer
|
|
69
|
+
# @see StopContainer
|
|
70
|
+
# @see RemoveContainer
|
|
71
|
+
# @see Docker::Container.create
|
|
72
|
+
# @since 0.1.0
|
|
73
|
+
class RecreateContainer < RubyLLM::Tool
|
|
74
|
+
description 'Recreate a Docker container (stops, removes, and recreates with same configuration)'
|
|
75
|
+
|
|
76
|
+
param :id, type: :string, desc: 'Container ID or name to recreate'
|
|
77
|
+
param :timeout, type: :integer,
|
|
78
|
+
desc: 'Seconds to wait before killing the container when stopping (default: 10)',
|
|
79
|
+
required: false
|
|
80
|
+
|
|
81
|
+
# Recreate a Docker container with identical configuration.
|
|
82
|
+
#
|
|
83
|
+
# This method performs a complete container recreation cycle while
|
|
84
|
+
# preserving all configuration settings. The new container will have
|
|
85
|
+
# the same name, environment, port mappings, volumes, and other
|
|
86
|
+
# settings as the original.
|
|
87
|
+
#
|
|
88
|
+
# @param id [String] container ID (full or short) or container name
|
|
89
|
+
# @param server_context [Object] RubyLLM context (unused but required)
|
|
90
|
+
# @param timeout [Integer] seconds to wait before force killing during stop (default: 10)
|
|
91
|
+
#
|
|
92
|
+
# @return [RubyLLM::Tool::Response] recreation results with new container ID
|
|
93
|
+
#
|
|
94
|
+
# @raise [Docker::Error::NotFoundError] if container doesn't exist
|
|
95
|
+
# @raise [StandardError] for recreation failures
|
|
96
|
+
#
|
|
97
|
+
# @example Recreate application container
|
|
98
|
+
# response = RecreateContainer.call(
|
|
99
|
+
# server_context: context,
|
|
100
|
+
# id: "my-app"
|
|
101
|
+
# )
|
|
102
|
+
#
|
|
103
|
+
# @example Recreate database with extended timeout
|
|
104
|
+
# response = tool.execute(
|
|
105
|
+
# id: "postgres-main",
|
|
106
|
+
# timeout: 60 # Allow time for DB shutdown
|
|
107
|
+
# )
|
|
108
|
+
#
|
|
109
|
+
# @see Docker::Container.get
|
|
110
|
+
# @see Docker::Container.create
|
|
111
|
+
def execute(id:, timeout: 10)
|
|
112
|
+
# Get the existing container
|
|
113
|
+
old_container = ::Docker::Container.get(id)
|
|
114
|
+
config = old_container.json
|
|
115
|
+
|
|
116
|
+
# Extract configuration we need to preserve
|
|
117
|
+
image = config['Config']['Image']
|
|
118
|
+
name = config['Name']&.delete_prefix('/')
|
|
119
|
+
cmd = config['Config']['Cmd']
|
|
120
|
+
env = config['Config']['Env']
|
|
121
|
+
exposed_ports = config['Config']['ExposedPorts']
|
|
122
|
+
host_config = config['HostConfig']
|
|
123
|
+
|
|
124
|
+
# Stop and remove the old container
|
|
125
|
+
old_container.stop('timeout' => timeout) if config['State']['Running']
|
|
126
|
+
old_container.delete
|
|
127
|
+
|
|
128
|
+
# Create new container with same config
|
|
129
|
+
new_config = {
|
|
130
|
+
'Image' => image,
|
|
131
|
+
'Cmd' => cmd,
|
|
132
|
+
'Env' => env,
|
|
133
|
+
'ExposedPorts' => exposed_ports,
|
|
134
|
+
'HostConfig' => host_config
|
|
135
|
+
}
|
|
136
|
+
new_config['name'] = name if name
|
|
137
|
+
|
|
138
|
+
new_container = ::Docker::Container.create(new_config)
|
|
139
|
+
|
|
140
|
+
# Start if the old one was running
|
|
141
|
+
new_container.start if config['State']['Running']
|
|
142
|
+
|
|
143
|
+
"Container #{id} recreated successfully. New ID: #{new_container.id}"
|
|
144
|
+
rescue ::Docker::Error::NotFoundError
|
|
145
|
+
"Container #{id} not found"
|
|
146
|
+
rescue StandardError => e
|
|
147
|
+
"Error recreating container: #{e.message}"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# RubyLLM tool for removing Docker containers.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides the ability to permanently delete Docker containers
|
|
8
|
+
# from the system. It supports both graceful removal of stopped containers
|
|
9
|
+
# and forced removal of running containers. Optionally, it can also remove
|
|
10
|
+
# associated anonymous volumes.
|
|
11
|
+
#
|
|
12
|
+
# == Features
|
|
13
|
+
#
|
|
14
|
+
# - Remove stopped containers safely
|
|
15
|
+
# - Force removal of running containers
|
|
16
|
+
# - Optional removal of associated volumes
|
|
17
|
+
# - Comprehensive error handling
|
|
18
|
+
# - Works with containers by ID or name
|
|
19
|
+
#
|
|
20
|
+
# == Data Loss Warning
|
|
21
|
+
#
|
|
22
|
+
# ⚠️ **DESTRUCTIVE OPERATION** ⚠️
|
|
23
|
+
#
|
|
24
|
+
# This operation permanently deletes containers and potentially data:
|
|
25
|
+
# - Container filesystem changes are lost forever
|
|
26
|
+
# - Running processes are killed immediately (with force)
|
|
27
|
+
# - Associated volumes may be removed if specified
|
|
28
|
+
# - Container logs and metadata are deleted
|
|
29
|
+
# - Operation cannot be undone
|
|
30
|
+
#
|
|
31
|
+
# == Security Considerations
|
|
32
|
+
#
|
|
33
|
+
# - Forced removal can cause data corruption
|
|
34
|
+
# - Volume removal may affect other containers
|
|
35
|
+
# - Running container removal terminates services abruptly
|
|
36
|
+
# - Sensitive data in container memory is not securely wiped
|
|
37
|
+
#
|
|
38
|
+
# Best practices:
|
|
39
|
+
# - Stop containers gracefully before removal
|
|
40
|
+
# - Backup important data before removing
|
|
41
|
+
# - Verify volume dependencies before volume removal
|
|
42
|
+
# - Use force removal only when necessary
|
|
43
|
+
#
|
|
44
|
+
# == Example Usage
|
|
45
|
+
#
|
|
46
|
+
# # Remove stopped container
|
|
47
|
+
# RemoveContainer.call(
|
|
48
|
+
# server_context: context,
|
|
49
|
+
# id: "old-container"
|
|
50
|
+
# )
|
|
51
|
+
#
|
|
52
|
+
# # Force remove running container with volumes
|
|
53
|
+
# RemoveContainer.call(
|
|
54
|
+
# server_context: context,
|
|
55
|
+
# id: "problematic-container",
|
|
56
|
+
# force: true,
|
|
57
|
+
# volumes: true
|
|
58
|
+
# )
|
|
59
|
+
#
|
|
60
|
+
# @see StopContainer
|
|
61
|
+
# @see CreateContainer
|
|
62
|
+
# @see Docker::Container#delete
|
|
63
|
+
# @since 0.1.0
|
|
64
|
+
class RemoveContainer < RubyLLM::Tool
|
|
65
|
+
description 'Remove a Docker container'
|
|
66
|
+
|
|
67
|
+
param :id, desc: 'Container ID or name'
|
|
68
|
+
param :force, type: :boolean, desc: 'Force removal of running container (default: false)', required: false
|
|
69
|
+
param :volumes, type: :boolean, desc: 'Remove associated volumes (default: false)', required: false
|
|
70
|
+
|
|
71
|
+
# Remove a Docker container permanently.
|
|
72
|
+
#
|
|
73
|
+
# This method deletes a container from the Docker system. By default,
|
|
74
|
+
# it only removes stopped containers. The force option allows removal
|
|
75
|
+
# of running containers, and the volumes option removes associated
|
|
76
|
+
# anonymous volumes.
|
|
77
|
+
#
|
|
78
|
+
# @param id [String] container ID (full or short) or container name
|
|
79
|
+
# @param server_context [Object] RubyLLM context (unused but required)
|
|
80
|
+
# @param force [Boolean] whether to force remove running containers (default: false)
|
|
81
|
+
# @param volumes [Boolean] whether to remove associated volumes (default: false)
|
|
82
|
+
#
|
|
83
|
+
# @return [RubyLLM::Tool::Response] removal operation results
|
|
84
|
+
#
|
|
85
|
+
# @raise [Docker::Error::NotFoundError] if container doesn't exist
|
|
86
|
+
# @raise [StandardError] for other removal failures
|
|
87
|
+
#
|
|
88
|
+
# @example Remove stopped container
|
|
89
|
+
# response = RemoveContainer.call(
|
|
90
|
+
# server_context: context,
|
|
91
|
+
# id: "finished-job"
|
|
92
|
+
# )
|
|
93
|
+
#
|
|
94
|
+
# @example Force remove running container
|
|
95
|
+
# response = RemoveContainer.call(
|
|
96
|
+
# server_context: context,
|
|
97
|
+
# id: "stuck-container",
|
|
98
|
+
# force: true
|
|
99
|
+
# )
|
|
100
|
+
#
|
|
101
|
+
# @example Remove container and its volumes
|
|
102
|
+
# response = tool.execute(
|
|
103
|
+
# id: "temp-container",
|
|
104
|
+
# volumes: true
|
|
105
|
+
# )
|
|
106
|
+
#
|
|
107
|
+
# @see Docker::Container#delete
|
|
108
|
+
def execute(id:, force: false, volumes: false)
|
|
109
|
+
container = ::Docker::Container.get(id)
|
|
110
|
+
container.delete(force: force, v: volumes)
|
|
111
|
+
|
|
112
|
+
"Container #{id} removed successfully"
|
|
113
|
+
rescue ::Docker::Error::NotFoundError
|
|
114
|
+
"Container #{id} not found"
|
|
115
|
+
rescue StandardError => e
|
|
116
|
+
"Error removing container: #{e.message}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|