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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b43832405fbf723559703102c4d84a42c27c677d601625de43c81ceb2db804d0
4
- data.tar.gz: 57d9349d4ce67bc9e157c6d235e5cf4925b0af05df53ce0f29fffd2bd888fbb7
3
+ metadata.gz: b46f33638fb88f8c9fca4a85747f67e266525933e4b22d38bb47e2ce1556a552
4
+ data.tar.gz: b080b742324d91eb1570af605b05539da67284fb76631faae04583f2577edad3
5
5
  SHA512:
6
- metadata.gz: 72c9fa985935caac2386f9b782ac3728f4c5ffbd121db6f5b36a6e1291eafed9bc5500bd0841385caad69ad5430152b5d11d10261c4ad73fbda86bd9cc7053b3
7
- data.tar.gz: e841eaa3cad32138f809a40556a2c798fb97e7c92bf40975d2076adbd6650a699eb5635880f6c50d598de2c1017045d61771ff88f7a794f8482a71bdb32b7fe3
6
+ metadata.gz: 17f03f903b19273e822feb4a389ff9b862d034607f032c3b6112838b1ddf5f38927dabad4001214f6ad9ce6a06f8661939b38fb2fa3dda8a0b1975246da199fa
7
+ data.tar.gz: 44edbe59ffa431b2cf78735ca138c2fd4c41d0d57dda543509d9de8a417343c525d1a3493d549fa4e2ebfc63948491a2994917df493683c25f8c33e2f795eadf
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/docker-swarm.svg)](https://badge.fury.io/rb/docker-swarm)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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['Name']}" }
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
- response = client.request(options)
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: options[:method],
24
- path: options[: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?(Hash)
8
- env[:body] = env[:body].to_json
9
- env[:headers]["Content-Type"] ||= "application/json"
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
- begin
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerSwarm
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-swarm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel