docker-swarm 0.2.0 → 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 +12 -17
- data/lib/docker_swarm/configuration.rb +7 -1
- data/lib/docker_swarm/connection.rb +13 -3
- data/lib/docker_swarm/middleware/request_encoder.rb +19 -3
- data/lib/docker_swarm/middleware/response_json_parser.rb +18 -6
- data/lib/docker_swarm/models/service.rb +0 -9
- data/lib/docker_swarm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b46f33638fb88f8c9fca4a85747f67e266525933e4b22d38bb47e2ce1556a552
|
|
4
|
+
data.tar.gz: b080b742324d91eb1570af605b05539da67284fb76631faae04583f2577edad3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17f03f903b19273e822feb4a389ff9b862d034607f032c3b6112838b1ddf5f38927dabad4001214f6ad9ce6a06f8661939b38fb2fa3dda8a0b1975246da199fa
|
|
7
|
+
data.tar.gz: 44edbe59ffa431b2cf78735ca138c2fd4c41d0d57dda543509d9de8a417343c525d1a3493d549fa4e2ebfc63948491a2994917df493683c25f8c33e2f795eadf
|
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://badge.fury.io/rb/docker-swarm)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
`docker-swarm` es un ORM ligero y cliente API robusto para interactuar con Docker Swarm desde Ruby. Diseñado para sentirse familiar a los desarrolladores de Rails, utiliza `ActiveModel` para ofrecer una interfaz limpia y potente.
|
|
6
|
+
`docker-swarm` es un ORM ligero y cliente API robusto para interactuar con Docker Swarm desde Ruby. Diseñado para sentirse familiar a los desarrolladores de Rails, utiliza `ActiveModel` para ofrecer una interfaz limpia y potente con estándares de observabilidad de Wispro.
|
|
7
7
|
|
|
8
8
|
## 🚀 Inicio Rápido
|
|
9
9
|
|
|
@@ -13,42 +13,37 @@ require 'docker_swarm'
|
|
|
13
13
|
# Configurar (opcional, usa defaults)
|
|
14
14
|
DockerSwarm.configure do |config|
|
|
15
15
|
config.socket_path = "unix:///var/run/docker.sock"
|
|
16
|
+
config.log_level = Logger::INFO
|
|
17
|
+
config.read_timeout = 30
|
|
18
|
+
config.max_retries = 3
|
|
16
19
|
end
|
|
17
20
|
|
|
18
|
-
# Listar servicios
|
|
21
|
+
# Listar servicios (con soporte para Indifferent Access en arrays)
|
|
19
22
|
services = DockerSwarm::Service.all
|
|
20
|
-
services.each { |s| puts "#{s.ID}: #{s.Spec[
|
|
23
|
+
services.each { |s| puts "#{s.ID}: #{s.Spec[:Name]}" }
|
|
21
24
|
|
|
22
25
|
# Crear un nuevo servicio
|
|
23
26
|
service = DockerSwarm::Service.create(
|
|
27
|
+
Name: "my-webapp",
|
|
24
28
|
Spec: {
|
|
25
|
-
Name: "my-webapp",
|
|
26
29
|
TaskTemplate: {
|
|
27
30
|
ContainerSpec: { Image: "nginx:latest" }
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
)
|
|
31
34
|
|
|
32
|
-
# Obtener logs
|
|
35
|
+
# Obtener logs (stdout y stderr habilitados por defecto)
|
|
33
36
|
puts service.logs
|
|
34
37
|
```
|
|
35
38
|
|
|
36
|
-
## 📖 Documentación Completa
|
|
37
|
-
|
|
38
|
-
Para profundizar en el uso de la gema, consulta las siguientes guías:
|
|
39
|
-
|
|
40
|
-
1. **[Guía de Configuración](docs/configuration.md)**: Cómo configurar el socket, logger y opciones globales.
|
|
41
|
-
2. **[Uso del ORM (Modelos)](docs/models.md)**: Todo sobre el ciclo de vida de los recursos (`Service`, `Node`, `Task`, etc.).
|
|
42
|
-
3. **[Cliente de API (Bajo Nivel)](docs/api.md)**: Cómo realizar peticiones personalizadas directamente a la API de Docker.
|
|
43
|
-
4. **[Manejo de Errores](docs/errors.md)**: Jerarquía de excepciones y mapeo de errores de Docker.
|
|
44
|
-
5. **[Pruebas y Mocking](docs/testing.md)**: Guía para testear tu aplicación sin depender de un socket de Docker real.
|
|
45
|
-
|
|
46
39
|
## 🛠 Características Clave
|
|
47
40
|
|
|
41
|
+
- **Observabilidad Wispro**: Logging estructurado (KV) con `component`, `event`, `source` y `duration_ms` usando reloj monotónico.
|
|
42
|
+
- **Seguridad**: Enmascaramiento automático de secretos (`password`, `token`, etc.) en los logs.
|
|
43
|
+
- **Deep Indifferent Access**: Acceso a atributos mediante símbolos o strings, incluso en resultados de listados (`.all`).
|
|
44
|
+
- **Resiliencia**: Gestión inteligente de timeouts (`read`, `write`, `connect`) y reintentos automáticos para errores de red.
|
|
48
45
|
- **Mapeo PascalCase**: Mantiene la fidelidad con los atributos de Docker (e.g., `s.ID`, `s.Spec`) evitando transformaciones costosas.
|
|
49
46
|
- **ActiveModel Ready**: Soporta validaciones, serialización JSON y comportamientos estándar de modelos Ruby.
|
|
50
|
-
- **Surgical Updates**: Actualizaciones precisas enviando solo el índice de versión y el payload necesario.
|
|
51
|
-
- **Excon Stack**: Basado en `Excon` con middlewares para encoding de peticiones, parseo de respuestas y gestión de errores.
|
|
52
47
|
|
|
53
48
|
## 🤝 Contribuir
|
|
54
49
|
|
|
@@ -4,12 +4,18 @@ require "logger"
|
|
|
4
4
|
|
|
5
5
|
module DockerSwarm
|
|
6
6
|
class Configuration
|
|
7
|
-
attr_accessor :socket_path, :logger, :log_level
|
|
7
|
+
attr_accessor :socket_path, :logger, :log_level,
|
|
8
|
+
:read_timeout, :write_timeout, :connect_timeout,
|
|
9
|
+
:max_retries
|
|
8
10
|
|
|
9
11
|
def initialize
|
|
10
12
|
@socket_path = "unix:///var/run/docker.sock"
|
|
11
13
|
@logger = Logger.new($stdout)
|
|
12
14
|
@log_level = Logger::INFO
|
|
15
|
+
@read_timeout = 60
|
|
16
|
+
@write_timeout = 60
|
|
17
|
+
@connect_timeout = 10
|
|
18
|
+
@max_retries = 3
|
|
13
19
|
end
|
|
14
20
|
end
|
|
15
21
|
end
|
|
@@ -16,12 +16,22 @@ module DockerSwarm
|
|
|
16
16
|
level: :debug,
|
|
17
17
|
data: { method: options[:method], path: options[:path] })
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# Combinar opciones por defecto de la gema con las de la petición
|
|
20
|
+
request_options = {
|
|
21
|
+
idempotent: true,
|
|
22
|
+
retry_errors: [Excon::Error::Socket, Excon::Error::Timeout],
|
|
23
|
+
read_timeout: DockerSwarm.configuration.read_timeout,
|
|
24
|
+
write_timeout: DockerSwarm.configuration.write_timeout,
|
|
25
|
+
connect_timeout: DockerSwarm.configuration.connect_timeout,
|
|
26
|
+
retries: DockerSwarm.configuration.max_retries
|
|
27
|
+
}.merge(options)
|
|
28
|
+
|
|
29
|
+
response = client.request(request_options)
|
|
20
30
|
|
|
21
31
|
log_event("request_success",
|
|
22
32
|
data: {
|
|
23
|
-
method:
|
|
24
|
-
path:
|
|
33
|
+
method: request_options[:method],
|
|
34
|
+
path: request_options[:path],
|
|
25
35
|
status: response.status,
|
|
26
36
|
duration_ms: calculate_duration(start_time)
|
|
27
37
|
})
|
|
@@ -4,12 +4,28 @@ module DockerSwarm
|
|
|
4
4
|
module Middleware
|
|
5
5
|
class RequestEncoder < Excon::Middleware::Base
|
|
6
6
|
def request_call(env)
|
|
7
|
-
if env[:body] && env[:body].is_a?(
|
|
8
|
-
env[:
|
|
9
|
-
env[:
|
|
7
|
+
if env[:body] && !env[:body].is_a?(String)
|
|
8
|
+
content_type = (env[:headers]["Content-Type"] || env[:headers][:content_type]).to_s
|
|
9
|
+
env[:body] = serialize_body(env[:body], content_type)
|
|
10
|
+
|
|
11
|
+
if content_type.blank? || content_type.include?("application/json")
|
|
12
|
+
env[:headers]["Content-Type"] ||= "application/json"
|
|
13
|
+
end
|
|
10
14
|
end
|
|
11
15
|
@stack.request_call(env)
|
|
12
16
|
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def serialize_body(body, content_type)
|
|
21
|
+
if content_type.include?("application/x-www-form-urlencoded")
|
|
22
|
+
URI.encode_www_form(body)
|
|
23
|
+
elsif content_type.include?("multipart/form-data")
|
|
24
|
+
body
|
|
25
|
+
else
|
|
26
|
+
body.to_json
|
|
27
|
+
end
|
|
28
|
+
end
|
|
13
29
|
end
|
|
14
30
|
end
|
|
15
31
|
end
|
|
@@ -9,17 +9,29 @@ module DockerSwarm
|
|
|
9
9
|
headers = env[:response][:headers] || {}
|
|
10
10
|
|
|
11
11
|
if body && !body.empty? && headers["Content-Type"]&.include?("application/json")
|
|
12
|
-
|
|
13
|
-
parsed = JSON.parse(body)
|
|
14
|
-
env[:response][:body] = parsed.is_a?(Hash) ? parsed.with_indifferent_access : parsed
|
|
15
|
-
rescue JSON::ParserError
|
|
16
|
-
# Keep original body if parsing fails
|
|
17
|
-
end
|
|
12
|
+
env[:response][:body] = parse_json(body)
|
|
18
13
|
end
|
|
19
14
|
end
|
|
20
15
|
|
|
21
16
|
@stack.response_call(env)
|
|
22
17
|
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def parse_json(body)
|
|
22
|
+
result = body.is_a?(String) ? JSON.parse(body) : body
|
|
23
|
+
|
|
24
|
+
case result
|
|
25
|
+
when Hash
|
|
26
|
+
result.with_indifferent_access
|
|
27
|
+
when Array
|
|
28
|
+
result.map { |item| item.is_a?(Hash) ? item.with_indifferent_access : item }
|
|
29
|
+
else
|
|
30
|
+
result
|
|
31
|
+
end
|
|
32
|
+
rescue JSON::ParserError
|
|
33
|
+
body
|
|
34
|
+
end
|
|
23
35
|
end
|
|
24
36
|
end
|
|
25
37
|
end
|
|
@@ -8,20 +8,11 @@ module DockerSwarm
|
|
|
8
8
|
include Concerns::Updatable
|
|
9
9
|
include Concerns::Deletable
|
|
10
10
|
|
|
11
|
-
validate :validate_name_presence
|
|
12
|
-
|
|
13
11
|
# Fetches logs for the service
|
|
14
12
|
# @param query_params [Hash] Query parameters for the logs endpoint (stdout, stderr, follow, etc.)
|
|
15
13
|
# @return [String] The raw log stream
|
|
16
14
|
def logs(query_params = { stdout: 1, stderr: 1 })
|
|
17
15
|
Api.request(action: self.class.routes[:logs], arguments: { id: self.ID }, query_params: query_params)
|
|
18
16
|
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def validate_name_presence
|
|
23
|
-
name = attributes["Name"] || (respond_to?(:Name) ? Name : nil)
|
|
24
|
-
errors.add(:Name, "can't be blank") if name.blank?
|
|
25
|
-
end
|
|
26
17
|
end
|
|
27
18
|
end
|
data/lib/docker_swarm/version.rb
CHANGED