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.
@@ -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