ruby_llm-docker 0.0.1 โ 0.3.0
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 +269 -0
- data/examples/list_containers.rb +36 -0
- data/examples/test_chat.rb +81 -0
- data/lib/ruby_llm/docker/build_image.rb +110 -0
- data/lib/ruby_llm/docker/copy_to_container.rb +151 -0
- data/lib/ruby_llm/docker/create_container.rb +124 -0
- data/lib/ruby_llm/docker/create_network.rb +115 -0
- data/lib/ruby_llm/docker/create_volume.rb +107 -0
- data/lib/ruby_llm/docker/exec_container.rb +168 -0
- data/lib/ruby_llm/docker/fetch_container_logs.rb +110 -0
- data/lib/ruby_llm/docker/list_containers.rb +65 -0
- data/lib/ruby_llm/docker/list_images.rb +58 -0
- data/lib/ruby_llm/docker/list_networks.rb +59 -0
- data/lib/ruby_llm/docker/list_volumes.rb +59 -0
- data/lib/ruby_llm/docker/pull_image.rb +103 -0
- data/lib/ruby_llm/docker/push_image.rb +124 -0
- data/lib/ruby_llm/docker/recreate_container.rb +119 -0
- data/lib/ruby_llm/docker/remove_container.rb +92 -0
- data/lib/ruby_llm/docker/remove_image.rb +100 -0
- data/lib/ruby_llm/docker/remove_network.rb +84 -0
- data/lib/ruby_llm/docker/remove_volume.rb +94 -0
- data/lib/ruby_llm/docker/run_container.rb +124 -0
- data/lib/ruby_llm/docker/start_container.rb +74 -0
- data/lib/ruby_llm/docker/stop_container.rb +83 -0
- data/lib/ruby_llm/docker/tag_image.rb +111 -0
- data/lib/ruby_llm/docker/version.rb +11 -2
- data/lib/ruby_llm/docker.rb +66 -3
- data/sig/ruby_llm/docker.rbs +1 -1
- metadata +104 -5
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Test script for RubyLLM Docker Tools
|
|
5
|
+
# This verifies that all Docker tools load correctly and the chat system works
|
|
6
|
+
# without requiring an OpenAI API key or active Docker daemon
|
|
7
|
+
|
|
8
|
+
require_relative '../lib/ruby_llm/docker'
|
|
9
|
+
|
|
10
|
+
puts '๐งช Testing Docker Chat functionality...'
|
|
11
|
+
|
|
12
|
+
# Test 1: Check that all tools are available
|
|
13
|
+
puts "\n1. Testing tool loading:"
|
|
14
|
+
tools = RubyLLM::Docker.all_tools
|
|
15
|
+
puts "โ
Found #{tools.size} Docker tools"
|
|
16
|
+
expected_tools = 22
|
|
17
|
+
if tools.size == expected_tools
|
|
18
|
+
puts "โ
Expected number of tools loaded (#{expected_tools})"
|
|
19
|
+
else
|
|
20
|
+
puts "โ Expected #{expected_tools} tools, but found #{tools.size}"
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Test 2: Check that all tools are valid RubyLLM::Tool classes
|
|
25
|
+
puts "\n2. Testing tool validity:"
|
|
26
|
+
invalid_tools = tools.reject { |tool| tool < RubyLLM::Tool }
|
|
27
|
+
if invalid_tools.empty?
|
|
28
|
+
puts 'โ
All tools inherit from RubyLLM::Tool'
|
|
29
|
+
else
|
|
30
|
+
puts "โ Invalid tools found: #{invalid_tools.map(&:name)}"
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Test 3: Test helper method (without actually creating a chat)
|
|
35
|
+
puts "\n3. Testing helper methods:"
|
|
36
|
+
begin
|
|
37
|
+
# Create a mock chat object to test the method exists
|
|
38
|
+
mock_chat = Object.new
|
|
39
|
+
def mock_chat.with_tool(_tool_class)
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
result = RubyLLM::Docker.add_all_tools_to_chat(mock_chat)
|
|
44
|
+
if result == mock_chat
|
|
45
|
+
puts 'โ
add_all_tools_to_chat method works correctly'
|
|
46
|
+
else
|
|
47
|
+
puts 'โ add_all_tools_to_chat method failed'
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
50
|
+
rescue StandardError => e
|
|
51
|
+
puts "โ Helper method test failed: #{e.message}"
|
|
52
|
+
exit 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Test 4: Check that docker_chat.rb file exists and is executable
|
|
56
|
+
puts "\n4. Testing chat script availability:"
|
|
57
|
+
chat_script = File.join(__dir__, 'docker_chat.rb')
|
|
58
|
+
if File.exist?(chat_script)
|
|
59
|
+
puts 'โ
docker_chat.rb exists'
|
|
60
|
+
|
|
61
|
+
if File.executable?(chat_script)
|
|
62
|
+
puts 'โ
docker_chat.rb is executable'
|
|
63
|
+
else
|
|
64
|
+
puts 'โ ๏ธ docker_chat.rb is not executable (run: chmod +x examples/docker_chat.rb)'
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
puts 'โ docker_chat.rb not found'
|
|
68
|
+
exit 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
puts "\n๐ All tests passed!"
|
|
72
|
+
puts "\n๐ Next steps:"
|
|
73
|
+
puts " 1. Set your OpenAI API key: export OPENAI_API_KEY='your-key-here'"
|
|
74
|
+
puts ' 2. Run the chat: ruby examples/docker_chat.rb'
|
|
75
|
+
puts ' 3. Or run with: ./examples/docker_chat.rb'
|
|
76
|
+
puts "\n๐ก Example chat commands to try:"
|
|
77
|
+
puts " โข 'How many containers are running?'"
|
|
78
|
+
puts " โข 'Show me all Docker images'"
|
|
79
|
+
puts " โข 'List Docker networks'"
|
|
80
|
+
puts " โข '/help' - to see all available tools"
|
|
81
|
+
puts " โข '/exit' - to quit the chat"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# MCP tool for building Docker images.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides the ability to build Docker images from Dockerfile
|
|
8
|
+
# content. It creates custom images by executing Dockerfile instructions
|
|
9
|
+
# and supports comprehensive build configuration including tagging and
|
|
10
|
+
# build arguments.
|
|
11
|
+
#
|
|
12
|
+
# == Features
|
|
13
|
+
#
|
|
14
|
+
# - Build images from Dockerfile content strings
|
|
15
|
+
# - Support for custom image tagging
|
|
16
|
+
# - Comprehensive build output and error reporting
|
|
17
|
+
# - Handles all standard Dockerfile instructions
|
|
18
|
+
# - Build context management
|
|
19
|
+
# - Progress tracking and logging
|
|
20
|
+
#
|
|
21
|
+
# == Security Considerations
|
|
22
|
+
#
|
|
23
|
+
# Image building involves significant security risks:
|
|
24
|
+
# - **Code Execution**: Dockerfile RUN commands execute arbitrary code
|
|
25
|
+
# - **Network Access**: Build process can access networks and repositories
|
|
26
|
+
# - **File System Access**: Can read local files and directories
|
|
27
|
+
# - **Credential Exposure**: May expose build-time secrets and credentials
|
|
28
|
+
# - **Supply Chain Risk**: Downloaded packages may contain malware
|
|
29
|
+
# - **Resource Consumption**: Builds can consume significant CPU, memory, and storage
|
|
30
|
+
#
|
|
31
|
+
# **Security Recommendations**:
|
|
32
|
+
# - Review all Dockerfile content before building
|
|
33
|
+
# - Use trusted base images only
|
|
34
|
+
# - Avoid embedding secrets in image layers
|
|
35
|
+
# - Implement build isolation and sandboxing
|
|
36
|
+
# - Monitor build resource consumption
|
|
37
|
+
# - Scan built images for vulnerabilities
|
|
38
|
+
# - Use multi-stage builds to minimize attack surface
|
|
39
|
+
#
|
|
40
|
+
# == Parameters
|
|
41
|
+
#
|
|
42
|
+
# - **dockerfile**: Dockerfile content as a string (required)
|
|
43
|
+
# - **tag**: Tag for the built image (optional, e.g., "myimage:latest")
|
|
44
|
+
#
|
|
45
|
+
# == Example Usage
|
|
46
|
+
#
|
|
47
|
+
# # Build simple image
|
|
48
|
+
# dockerfile_content = <<~DOCKERFILE
|
|
49
|
+
# FROM alpine:latest
|
|
50
|
+
# RUN apk add --no-cache curl
|
|
51
|
+
# CMD ["curl", "--version"]
|
|
52
|
+
# DOCKERFILE
|
|
53
|
+
#
|
|
54
|
+
# response = BuildImage.call(
|
|
55
|
+
# server_context: context,
|
|
56
|
+
# dockerfile: dockerfile_content,
|
|
57
|
+
# tag: "my-curl:latest"
|
|
58
|
+
# )
|
|
59
|
+
#
|
|
60
|
+
# # Build web server image
|
|
61
|
+
# dockerfile_content = <<~DOCKERFILE
|
|
62
|
+
# FROM nginx:alpine
|
|
63
|
+
# COPY nginx.conf /etc/nginx/nginx.conf
|
|
64
|
+
# EXPOSE 80
|
|
65
|
+
# CMD ["nginx", "-g", "daemon off;"]
|
|
66
|
+
# DOCKERFILE
|
|
67
|
+
#
|
|
68
|
+
# response = BuildImage.call(
|
|
69
|
+
# server_context: context,
|
|
70
|
+
# dockerfile: dockerfile_content,
|
|
71
|
+
# tag: "custom-nginx:v1.0"
|
|
72
|
+
# )
|
|
73
|
+
#
|
|
74
|
+
# @see ::Docker::Image.build_from_dir
|
|
75
|
+
# @since 0.1.0
|
|
76
|
+
BUILD_IMAGE_DEFINITION = ToolForge.define(:build_image) do
|
|
77
|
+
description 'Build a Docker image'
|
|
78
|
+
|
|
79
|
+
param :dockerfile,
|
|
80
|
+
type: :string,
|
|
81
|
+
description: 'Dockerfile content as a string'
|
|
82
|
+
|
|
83
|
+
param :tag,
|
|
84
|
+
type: :string,
|
|
85
|
+
description: 'Tag for the built image (e.g., "myimage:latest")',
|
|
86
|
+
required: false
|
|
87
|
+
|
|
88
|
+
execute do |dockerfile:, tag: nil|
|
|
89
|
+
# Build the image
|
|
90
|
+
image = ::Docker::Image.build(dockerfile)
|
|
91
|
+
|
|
92
|
+
# If a tag was specified, tag the image
|
|
93
|
+
if tag
|
|
94
|
+
# Split tag into repo and tag parts
|
|
95
|
+
repo, image_tag = tag.split(':', 2)
|
|
96
|
+
image_tag ||= 'latest'
|
|
97
|
+
image.tag('repo' => repo, 'tag' => image_tag, 'force' => true)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
response_text = "Image built successfully. ID: #{image.id}"
|
|
101
|
+
response_text += ", Tag: #{tag}" if tag
|
|
102
|
+
response_text
|
|
103
|
+
rescue StandardError => e
|
|
104
|
+
"Error building image: #{e.message}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
BuildImage = BUILD_IMAGE_DEFINITION.to_ruby_llm_tool
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# MCP tool for copying files and directories to Docker containers.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides the ability to copy files and directories from the
|
|
8
|
+
# local host filesystem into running Docker containers. It supports both
|
|
9
|
+
# individual files and entire directory trees, with optional ownership
|
|
10
|
+
# modification within the container.
|
|
11
|
+
#
|
|
12
|
+
# == Features
|
|
13
|
+
#
|
|
14
|
+
# - Copy files and directories from host to container
|
|
15
|
+
# - Supports recursive directory copying
|
|
16
|
+
# - Preserves file permissions and metadata
|
|
17
|
+
# - Optional ownership modification after copy
|
|
18
|
+
# - Works with running containers
|
|
19
|
+
# - Comprehensive error handling and validation
|
|
20
|
+
#
|
|
21
|
+
# == Security Considerations
|
|
22
|
+
#
|
|
23
|
+
# File copying operations have significant security implications:
|
|
24
|
+
# - **File System Access**: Reads local host file system content
|
|
25
|
+
# - **Container Modification**: Alters container file system state
|
|
26
|
+
# - **Data Injection**: Can introduce malicious files into containers
|
|
27
|
+
# - **Permission Escalation**: May affect container security context
|
|
28
|
+
# - **Resource Consumption**: Large copies can consume storage and I/O
|
|
29
|
+
# - **Path Traversal**: Improper paths could access unintended locations
|
|
30
|
+
#
|
|
31
|
+
# **Security Recommendations**:
|
|
32
|
+
# - Validate and sanitize all file paths
|
|
33
|
+
# - Implement access controls for source file locations
|
|
34
|
+
# - Monitor file copy operations and sizes
|
|
35
|
+
# - Use read-only mounts where possible
|
|
36
|
+
# - Apply resource limits to prevent abuse
|
|
37
|
+
#
|
|
38
|
+
# == Parameters
|
|
39
|
+
#
|
|
40
|
+
# - **id**: Container ID or name (required)
|
|
41
|
+
# - **source_path**: Path to file/directory on local filesystem (required)
|
|
42
|
+
# - **destination_path**: Path inside container for copied content (required)
|
|
43
|
+
# - **owner**: Owner for copied files (optional, e.g., "1000:1000" or "username:group")
|
|
44
|
+
#
|
|
45
|
+
# == Example Usage
|
|
46
|
+
#
|
|
47
|
+
# # Copy single file
|
|
48
|
+
# response = CopyToContainer.call(
|
|
49
|
+
# server_context: context,
|
|
50
|
+
# id: "web-server",
|
|
51
|
+
# source_path: "/local/config.conf",
|
|
52
|
+
# destination_path: "/etc/nginx/nginx.conf"
|
|
53
|
+
# )
|
|
54
|
+
#
|
|
55
|
+
# # Copy directory with ownership change
|
|
56
|
+
# response = CopyToContainer.call(
|
|
57
|
+
# server_context: context,
|
|
58
|
+
# id: "app-container",
|
|
59
|
+
# source_path: "/local/app-data",
|
|
60
|
+
# destination_path: "/var/lib/app",
|
|
61
|
+
# owner: "app:app"
|
|
62
|
+
# )
|
|
63
|
+
#
|
|
64
|
+
# @see ::Docker::Container#archive_in_stream
|
|
65
|
+
# @since 0.1.0
|
|
66
|
+
COPY_TO_CONTAINER_DEFINITION = ToolForge.define(:copy_to_container) do
|
|
67
|
+
description 'Copy a file or directory from the local filesystem into a running Docker container. ' \
|
|
68
|
+
'The source path is on the local machine, and the destination path is inside the container.'
|
|
69
|
+
|
|
70
|
+
param :id,
|
|
71
|
+
type: :string,
|
|
72
|
+
description: 'Container ID or name'
|
|
73
|
+
|
|
74
|
+
param :source_path,
|
|
75
|
+
type: :string,
|
|
76
|
+
description: 'Path to the file or directory on the local filesystem to copy'
|
|
77
|
+
|
|
78
|
+
param :destination_path,
|
|
79
|
+
type: :string,
|
|
80
|
+
description: 'Path inside the container where the file/directory should be copied'
|
|
81
|
+
|
|
82
|
+
param :owner,
|
|
83
|
+
type: :string,
|
|
84
|
+
description: 'Owner for the copied files (optional, e.g., "1000:1000" or "username:group")',
|
|
85
|
+
required: false
|
|
86
|
+
|
|
87
|
+
# Helper method for adding files/directories to tar
|
|
88
|
+
class_helper :add_to_tar do |tar, path, archive_path|
|
|
89
|
+
if File.directory?(path)
|
|
90
|
+
# Add directory entry
|
|
91
|
+
tar.mkdir(archive_path, File.stat(path).mode)
|
|
92
|
+
|
|
93
|
+
# Add directory contents
|
|
94
|
+
Dir.entries(path).each do |entry|
|
|
95
|
+
next if ['.', '..'].include?(entry)
|
|
96
|
+
|
|
97
|
+
full_path = File.join(path, entry)
|
|
98
|
+
archive_entry_path = File.join(archive_path, entry)
|
|
99
|
+
add_to_tar(tar, full_path, archive_entry_path)
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
# Add file
|
|
103
|
+
File.open(path, 'rb') do |file|
|
|
104
|
+
tar.add_file_simple(archive_path, File.stat(path).mode, file.size) do |tar_file|
|
|
105
|
+
IO.copy_stream(file, tar_file)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
execute do |id:, source_path:, destination_path:, owner: nil|
|
|
112
|
+
container = ::Docker::Container.get(id)
|
|
113
|
+
|
|
114
|
+
# Verify source path exists
|
|
115
|
+
next "Source path not found: #{source_path}" unless File.exist?(source_path)
|
|
116
|
+
|
|
117
|
+
# Create a tar archive of the source
|
|
118
|
+
tar_io = StringIO.new
|
|
119
|
+
tar_io.set_encoding('ASCII-8BIT')
|
|
120
|
+
|
|
121
|
+
Gem::Package::TarWriter.new(tar_io) do |tar|
|
|
122
|
+
add_to_tar(tar, source_path, File.basename(source_path))
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
tar_io.rewind
|
|
126
|
+
|
|
127
|
+
# Copy to container
|
|
128
|
+
container.archive_in_stream(destination_path) do
|
|
129
|
+
tar_io.read
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Optionally change ownership
|
|
133
|
+
if owner
|
|
134
|
+
chown_path = File.join(destination_path, File.basename(source_path))
|
|
135
|
+
container.exec(['chown', '-R', owner, chown_path])
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
file_type = File.directory?(source_path) ? 'directory' : 'file'
|
|
139
|
+
response_text = "Successfully copied #{file_type} from #{source_path} to #{id}:#{destination_path}"
|
|
140
|
+
response_text += "\nOwnership changed to #{owner}" if owner
|
|
141
|
+
response_text
|
|
142
|
+
rescue ::Docker::Error::NotFoundError
|
|
143
|
+
"Container #{id} not found"
|
|
144
|
+
rescue StandardError => e
|
|
145
|
+
"Error copying to container: #{e.message}"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
CopyToContainer = COPY_TO_CONTAINER_DEFINITION.to_ruby_llm_tool
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# MCP tool for creating Docker containers.
|
|
6
|
+
#
|
|
7
|
+
# This tool creates a new Docker container from a specified image without
|
|
8
|
+
# starting it. The container is created in a "created" state and can be
|
|
9
|
+
# started later using the start_container tool. This two-step process
|
|
10
|
+
# allows for container configuration before execution.
|
|
11
|
+
#
|
|
12
|
+
# == Features
|
|
13
|
+
#
|
|
14
|
+
# - Creates containers from any available Docker image
|
|
15
|
+
# - Supports custom container naming
|
|
16
|
+
# - Configures command execution and environment variables
|
|
17
|
+
# - Sets up port exposure and network configuration
|
|
18
|
+
# - Applies advanced host configurations
|
|
19
|
+
# - Handles container labeling and metadata
|
|
20
|
+
#
|
|
21
|
+
# == Security Considerations
|
|
22
|
+
#
|
|
23
|
+
# Container creation is a powerful operation that can:
|
|
24
|
+
# - **Resource Allocation**: Consume system resources and storage
|
|
25
|
+
# - **Network Access**: Create network endpoints and bindings
|
|
26
|
+
# - **File System Access**: Mount host directories and volumes
|
|
27
|
+
# - **Security Context**: Run with elevated privileges if configured
|
|
28
|
+
#
|
|
29
|
+
# Implement proper access controls and resource limits.
|
|
30
|
+
#
|
|
31
|
+
# == Parameters
|
|
32
|
+
#
|
|
33
|
+
# - **image**: Docker image to use (required)
|
|
34
|
+
# - **name**: Custom container name (optional)
|
|
35
|
+
# - **cmd**: Command to execute as space-separated string (optional)
|
|
36
|
+
# - **env**: Environment variables as comma-separated KEY=VALUE pairs (optional)
|
|
37
|
+
# - **exposed_ports**: Port exposure configuration as JSON object (optional)
|
|
38
|
+
# - **host_config**: Advanced host configuration as JSON object (optional)
|
|
39
|
+
#
|
|
40
|
+
# == Example Usage
|
|
41
|
+
#
|
|
42
|
+
# # Simple container creation
|
|
43
|
+
# response = CreateContainer.call(
|
|
44
|
+
# server_context: context,
|
|
45
|
+
# image: "nginx:latest",
|
|
46
|
+
# name: "web-server"
|
|
47
|
+
# )
|
|
48
|
+
#
|
|
49
|
+
# # Advanced container with configuration
|
|
50
|
+
# response = CreateContainer.call(
|
|
51
|
+
# server_context: context,
|
|
52
|
+
# image: "postgres:13",
|
|
53
|
+
# name: "database",
|
|
54
|
+
# env: "POSTGRES_PASSWORD=secret,POSTGRES_DB=myapp",
|
|
55
|
+
# exposed_ports: {"5432/tcp" => {}},
|
|
56
|
+
# host_config: {
|
|
57
|
+
# "PortBindings" => {"5432/tcp" => [{"HostPort" => "5432"}]},
|
|
58
|
+
# "Binds" => ["/host/data:/var/lib/postgresql/data:rw"]
|
|
59
|
+
# }
|
|
60
|
+
# )
|
|
61
|
+
#
|
|
62
|
+
# @see ::Docker::Container.create
|
|
63
|
+
# @since 0.1.0
|
|
64
|
+
CREATE_CONTAINER_DEFINITION = ToolForge.define(:create_container) do
|
|
65
|
+
description 'Create a Docker container'
|
|
66
|
+
|
|
67
|
+
param :image,
|
|
68
|
+
type: :string,
|
|
69
|
+
description: 'Image name to use (e.g., "ubuntu:22.04")'
|
|
70
|
+
|
|
71
|
+
param :name,
|
|
72
|
+
type: :string,
|
|
73
|
+
description: 'Container name (optional)',
|
|
74
|
+
required: false
|
|
75
|
+
|
|
76
|
+
param :cmd,
|
|
77
|
+
type: :string,
|
|
78
|
+
description: 'Command to run as space-separated string (optional, e.g., "npm start" or "python app.py")',
|
|
79
|
+
required: false
|
|
80
|
+
|
|
81
|
+
param :env,
|
|
82
|
+
type: :string,
|
|
83
|
+
description: 'Environment variables as comma-separated KEY=VALUE pairs (optional)',
|
|
84
|
+
required: false
|
|
85
|
+
|
|
86
|
+
param :exposed_ports,
|
|
87
|
+
type: :object,
|
|
88
|
+
description: 'Exposed ports as {"port/protocol": {}} (optional)',
|
|
89
|
+
required: false
|
|
90
|
+
|
|
91
|
+
param :host_config,
|
|
92
|
+
type: :object,
|
|
93
|
+
description: 'Host configuration including port bindings, volumes, etc. (optional)',
|
|
94
|
+
required: false
|
|
95
|
+
|
|
96
|
+
execute do |image:, name: nil, cmd: nil, env: nil, exposed_ports: nil, host_config: nil|
|
|
97
|
+
config = { 'Image' => image }
|
|
98
|
+
config['name'] = name if name
|
|
99
|
+
|
|
100
|
+
# Parse cmd string into array if provided
|
|
101
|
+
config['Cmd'] = Shellwords.split(cmd) if cmd && !cmd.strip.empty?
|
|
102
|
+
|
|
103
|
+
# Parse env string into array if provided
|
|
104
|
+
config['Env'] = env.split(',').map(&:strip) if env && !env.strip.empty?
|
|
105
|
+
|
|
106
|
+
config['ExposedPorts'] = exposed_ports if exposed_ports
|
|
107
|
+
config['HostConfig'] = host_config if host_config
|
|
108
|
+
|
|
109
|
+
container = ::Docker::Container.create(config)
|
|
110
|
+
container_name = container.info['Names']&.first&.delete_prefix('/')
|
|
111
|
+
|
|
112
|
+
"Container created successfully. ID: #{container.id}, Name: #{container_name}"
|
|
113
|
+
rescue ::Docker::Error::NotFoundError
|
|
114
|
+
"Image #{image} not found"
|
|
115
|
+
rescue ::Docker::Error::ConflictError
|
|
116
|
+
"Container with name #{name} already exists"
|
|
117
|
+
rescue StandardError => e
|
|
118
|
+
"Error creating container: #{e.message}"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
CreateContainer = CREATE_CONTAINER_DEFINITION.to_ruby_llm_tool
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# MCP tool for creating Docker networks.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides the ability to create custom Docker networks
|
|
8
|
+
# for container communication and isolation. Networks enable secure
|
|
9
|
+
# and controlled communication between containers and external systems.
|
|
10
|
+
#
|
|
11
|
+
# == Features
|
|
12
|
+
#
|
|
13
|
+
# - Create custom Docker networks
|
|
14
|
+
# - Support for multiple network drivers (bridge, overlay, host, etc.)
|
|
15
|
+
# - Duplicate name checking and validation
|
|
16
|
+
# - Network configuration and options
|
|
17
|
+
# - Comprehensive error handling
|
|
18
|
+
# - Network isolation and security controls
|
|
19
|
+
#
|
|
20
|
+
# == Security Considerations
|
|
21
|
+
#
|
|
22
|
+
# Network creation involves important security considerations:
|
|
23
|
+
# - **Network Isolation**: Improper networks can compromise container isolation
|
|
24
|
+
# - **Traffic Control**: Networks affect inter-container communication
|
|
25
|
+
# - **External Access**: Bridge networks may expose containers externally
|
|
26
|
+
# - **Resource Usage**: Networks consume system resources
|
|
27
|
+
# - **DNS Resolution**: Custom networks affect service discovery
|
|
28
|
+
# - **Firewall Bypass**: Networks can bypass host firewall rules
|
|
29
|
+
#
|
|
30
|
+
# **Security Recommendations**:
|
|
31
|
+
# - Use appropriate network drivers for use case
|
|
32
|
+
# - Implement network segmentation strategies
|
|
33
|
+
# - Monitor network traffic and usage
|
|
34
|
+
# - Avoid exposing internal networks externally
|
|
35
|
+
# - Use network policies for access control
|
|
36
|
+
# - Regular audit of network configurations
|
|
37
|
+
#
|
|
38
|
+
# == Parameters
|
|
39
|
+
#
|
|
40
|
+
# - **name**: Name of the network (required)
|
|
41
|
+
# - **driver**: Driver to use (optional, default: "bridge")
|
|
42
|
+
# - **check_duplicate**: Check for networks with duplicate names (optional, default: true)
|
|
43
|
+
#
|
|
44
|
+
# == Network Drivers
|
|
45
|
+
#
|
|
46
|
+
# - **bridge**: Default driver for single-host networking
|
|
47
|
+
# - **overlay**: Multi-host networking for Docker Swarm
|
|
48
|
+
# - **host**: Uses host's network stack directly
|
|
49
|
+
# - **none**: Disables networking for containers
|
|
50
|
+
# - **macvlan**: Assigns MAC addresses to containers
|
|
51
|
+
#
|
|
52
|
+
# == Example Usage
|
|
53
|
+
#
|
|
54
|
+
# # Create basic bridge network
|
|
55
|
+
# response = CreateNetwork.call(
|
|
56
|
+
# server_context: context,
|
|
57
|
+
# name: "app-network"
|
|
58
|
+
# )
|
|
59
|
+
#
|
|
60
|
+
# # Create overlay network for swarm
|
|
61
|
+
# response = CreateNetwork.call(
|
|
62
|
+
# server_context: context,
|
|
63
|
+
# name: "swarm-network",
|
|
64
|
+
# driver: "overlay"
|
|
65
|
+
# )
|
|
66
|
+
#
|
|
67
|
+
# # Create network allowing duplicates
|
|
68
|
+
# response = CreateNetwork.call(
|
|
69
|
+
# server_context: context,
|
|
70
|
+
# name: "test-network",
|
|
71
|
+
# driver: "bridge",
|
|
72
|
+
# check_duplicate: false
|
|
73
|
+
# )
|
|
74
|
+
#
|
|
75
|
+
# @see ::Docker::Network.create
|
|
76
|
+
# @since 0.1.0
|
|
77
|
+
CREATE_NETWORK_DEFINITION = ToolForge.define(:create_network) do
|
|
78
|
+
description 'Create a Docker network'
|
|
79
|
+
|
|
80
|
+
param :name,
|
|
81
|
+
type: :string,
|
|
82
|
+
description: 'Name of the network'
|
|
83
|
+
|
|
84
|
+
param :driver,
|
|
85
|
+
type: :string,
|
|
86
|
+
description: 'Driver to use (default: bridge)',
|
|
87
|
+
required: false,
|
|
88
|
+
default: 'bridge'
|
|
89
|
+
|
|
90
|
+
param :check_duplicate,
|
|
91
|
+
type: :boolean,
|
|
92
|
+
description: 'Check for networks with duplicate names (default: true)',
|
|
93
|
+
required: false,
|
|
94
|
+
default: true
|
|
95
|
+
|
|
96
|
+
execute do |name:, driver: 'bridge', check_duplicate: true|
|
|
97
|
+
options = {
|
|
98
|
+
'Name' => name,
|
|
99
|
+
'Driver' => driver,
|
|
100
|
+
'CheckDuplicate' => check_duplicate
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
network = ::Docker::Network.create(name, options)
|
|
104
|
+
|
|
105
|
+
"Network #{name} created successfully. ID: #{network.id}"
|
|
106
|
+
rescue ::Docker::Error::ConflictError
|
|
107
|
+
"Network #{name} already exists"
|
|
108
|
+
rescue StandardError => e
|
|
109
|
+
"Error creating network: #{e.message}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
CreateNetwork = CREATE_NETWORK_DEFINITION.to_ruby_llm_tool
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Docker
|
|
5
|
+
# MCP tool for creating Docker volumes.
|
|
6
|
+
#
|
|
7
|
+
# This tool provides the ability to create Docker volumes for persistent
|
|
8
|
+
# data storage. Volumes are essential for maintaining data across container
|
|
9
|
+
# lifecycles and enabling data sharing between containers.
|
|
10
|
+
#
|
|
11
|
+
# == Features
|
|
12
|
+
#
|
|
13
|
+
# - Create named Docker volumes
|
|
14
|
+
# - Support for multiple volume drivers
|
|
15
|
+
# - Persistent data storage management
|
|
16
|
+
# - Volume driver configuration
|
|
17
|
+
# - Comprehensive error handling
|
|
18
|
+
# - Volume lifecycle management
|
|
19
|
+
#
|
|
20
|
+
# == Security Considerations
|
|
21
|
+
#
|
|
22
|
+
# Volume creation involves important security considerations:
|
|
23
|
+
# - **Data Persistence**: Volumes store data beyond container lifecycle
|
|
24
|
+
# - **Access Control**: Volume permissions affect data security
|
|
25
|
+
# - **Data Isolation**: Improper volumes can compromise data separation
|
|
26
|
+
# - **Storage Security**: Volume drivers may have security implications
|
|
27
|
+
# - **Resource Usage**: Volumes consume disk space and system resources
|
|
28
|
+
# - **Data Leakage**: Shared volumes can expose sensitive data
|
|
29
|
+
#
|
|
30
|
+
# **Security Recommendations**:
|
|
31
|
+
# - Use appropriate volume drivers for security requirements
|
|
32
|
+
# - Implement proper access controls and permissions
|
|
33
|
+
# - Monitor volume usage and capacity
|
|
34
|
+
# - Regular backup of critical volume data
|
|
35
|
+
# - Audit volume access patterns
|
|
36
|
+
# - Use encryption for sensitive data volumes
|
|
37
|
+
# - Implement volume lifecycle policies
|
|
38
|
+
#
|
|
39
|
+
# == Parameters
|
|
40
|
+
#
|
|
41
|
+
# - **name**: Name of the volume (required)
|
|
42
|
+
# - **driver**: Driver to use (optional, default: "local")
|
|
43
|
+
#
|
|
44
|
+
# == Volume Drivers
|
|
45
|
+
#
|
|
46
|
+
# - **local**: Default driver for local filesystem storage
|
|
47
|
+
# - **nfs**: Network File System driver for shared storage
|
|
48
|
+
# - **cifs**: Common Internet File System driver
|
|
49
|
+
# - **rexray**: REX-Ray driver for cloud storage integration
|
|
50
|
+
# - **convoy**: Convoy driver for snapshot management
|
|
51
|
+
#
|
|
52
|
+
# == Example Usage
|
|
53
|
+
#
|
|
54
|
+
# # Create basic local volume
|
|
55
|
+
# response = CreateVolume.call(
|
|
56
|
+
# server_context: context,
|
|
57
|
+
# name: "app-data"
|
|
58
|
+
# )
|
|
59
|
+
#
|
|
60
|
+
# # Create volume with specific driver
|
|
61
|
+
# response = CreateVolume.call(
|
|
62
|
+
# server_context: context,
|
|
63
|
+
# name: "shared-storage",
|
|
64
|
+
# driver: "nfs"
|
|
65
|
+
# )
|
|
66
|
+
#
|
|
67
|
+
# # Create database volume
|
|
68
|
+
# response = CreateVolume.call(
|
|
69
|
+
# server_context: context,
|
|
70
|
+
# name: "postgres-data",
|
|
71
|
+
# driver: "local"
|
|
72
|
+
# )
|
|
73
|
+
#
|
|
74
|
+
# @see ::Docker::Volume.create
|
|
75
|
+
# @since 0.1.0
|
|
76
|
+
CREATE_VOLUME_DEFINITION = ToolForge.define(:create_volume) do
|
|
77
|
+
description 'Create a Docker volume'
|
|
78
|
+
|
|
79
|
+
param :name,
|
|
80
|
+
type: :string,
|
|
81
|
+
description: 'Name of the volume'
|
|
82
|
+
|
|
83
|
+
param :driver,
|
|
84
|
+
type: :string,
|
|
85
|
+
description: 'Driver to use (default: local)',
|
|
86
|
+
required: false,
|
|
87
|
+
default: 'local'
|
|
88
|
+
|
|
89
|
+
execute do |name:, driver: 'local'|
|
|
90
|
+
options = {
|
|
91
|
+
'Name' => name,
|
|
92
|
+
'Driver' => driver
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
::Docker::Volume.create(name, options)
|
|
96
|
+
|
|
97
|
+
"Volume #{name} created successfully"
|
|
98
|
+
rescue ::Docker::Error::ConflictError
|
|
99
|
+
"Volume #{name} already exists"
|
|
100
|
+
rescue StandardError => e
|
|
101
|
+
"Error creating volume: #{e.message}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
CreateVolume = CREATE_VOLUME_DEFINITION.to_ruby_llm_tool
|
|
106
|
+
end
|
|
107
|
+
end
|