docker-swarm 0.6.0 → 0.7.1

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.
@@ -1,257 +0,0 @@
1
- # API Detallada
2
-
3
- Referencia completa de modelos, concerns, middleware y métodos públicos de DockerSwarm.
4
-
5
- ## Modelos — Tabla de capacidades
6
-
7
- | Modelo | Creatable | Updatable | Deletable | Loggable | Extra |
8
- |--------|-----------|-----------|-----------|----------|-------|
9
- | Service | x | x | x | x | `#restart`, ciclo de vida completo |
10
- | Node | | x | x | | Miembros del cluster, no se crean |
11
- | Task | | | | x | Read-only, generados por services |
12
- | Container | | | x | x | `#start`, `#stop` |
13
- | Network | x | x | x | | CRUD completo |
14
- | Volume | x | | x | | `root_key = "Volumes"` |
15
- | Config | x | | x | | Configuración del cluster |
16
- | Secret | x | | x | | Datos sensibles |
17
- | Image | x | | x | | Pull/removal |
18
- | Swarm | | | | | `.show` (estático) |
19
- | System | | | | | `.info`, `.version`, `.up`, `.df` |
20
-
21
- ## Base — Métodos de clase
22
-
23
- ```ruby
24
- # Listar todos (con filtros opcionales)
25
- # @param filters [Hash] Filtros Docker (label:, name:, id:, role:, etc.)
26
- # @return [Array<Model>]
27
- Model.all(filters = {})
28
-
29
- # Alias de .all
30
- Model.where(filters)
31
-
32
- # Buscar por ID (retorna nil si 404)
33
- # @param id [String]
34
- # @return [Model, nil]
35
- Model.find(id)
36
-
37
- # Nombre del recurso pluralizado (ej: "services", "nodes")
38
- Model.resource_name
39
-
40
- # Endpoints del modelo desde Api::ENDPOINTS
41
- Model.routes
42
- ```
43
-
44
- ### Filtros soportados
45
-
46
- ```ruby
47
- # Filtros Docker se serializan como JSON en query param `filters`
48
- DockerSwarm::Service.all(label: ["app=web"], name: ["my-service"])
49
- DockerSwarm::Container.all(status: ["running"])
50
- DockerSwarm::Node.all(role: ["manager"])
51
-
52
- # Parámetros globales (no van en filters)
53
- DockerSwarm::Image.all(all: true) # incluir intermedias
54
- DockerSwarm::Container.all(limit: 10) # limitar resultados
55
- ```
56
-
57
- ## Base — Métodos de instancia
58
-
59
- ```ruby
60
- # ID del recurso
61
- # @return [String]
62
- model.id
63
-
64
- # Hash de atributos (excluye internos de ActiveModel)
65
- # @return [Hash]
66
- model.attributes
67
-
68
- # Recarga desde Docker API
69
- # @return [self]
70
- model.reload
71
-
72
- # Prepara payload para Docker (excluye ID, Version, CreatedAt, extrae Spec)
73
- # @return [Hash]
74
- model.payload_for_docker
75
-
76
- # Persistido? (tiene ID)
77
- # @return [Boolean]
78
- model.persisted?
79
-
80
- # Inspección legible: #<DockerSwarm::Service ID: abc, Name: web, Image: nginx>
81
- model.inspect
82
-
83
- # Serialización
84
- model.as_json
85
- model.serializable_hash
86
- ```
87
-
88
- ## Concern: Creatable
89
-
90
- Incluido en: Service, Network, Volume, Config, Secret, Image.
91
-
92
- ```ruby
93
- # Crear y persistir
94
- # @param attributes [Hash] Atributos PascalCase
95
- # @return [Model] instancia (con ID si exitoso)
96
- Model.create(attributes)
97
-
98
- # Persistir instancia nueva (o delegar a update si persisted?)
99
- # @return [Boolean] false si validación falla
100
- model.save
101
- ```
102
-
103
- Flujo interno de `save`: `valid?` → `Api.request(:create, payload_for_docker)` → asigna ID de response → `reload`.
104
-
105
- ## Concern: Updatable
106
-
107
- Incluido en: Service, Node, Network.
108
-
109
- ```ruby
110
- # Actualizar recurso persistido
111
- # @param new_attributes [Hash] Atributos a mergear
112
- # @return [Boolean] false si validación falla
113
- model.update(new_attributes = {})
114
- ```
115
-
116
- Flujo interno: `assign_attributes` (deep_merge en Spec) → `valid?` → `Api.request(:update, id:, version: Version["Index"], payload:)`.
117
-
118
- **Importante:** El query param `version` se extrae automáticamente de `self.Version["Index"]`. Sin esto, Docker rechaza el update con 500.
119
-
120
- ## Concern: Deletable
121
-
122
- Incluido en: Service, Node, Container, Network, Volume, Config, Secret, Image.
123
-
124
- ```ruby
125
- # Eliminar por instancia
126
- # @return [true, nil] nil si ya no existía (404 graceful)
127
- model.destroy
128
-
129
- # Eliminar por ID (class method)
130
- # @return [true, nil]
131
- Model.destroy(id)
132
- ```
133
-
134
- ## Concern: Loggable
135
-
136
- Incluido en: Service, Task, Container.
137
-
138
- ```ruby
139
- # Obtener logs del recurso
140
- # @param query_params [Hash] stdout:, stderr:, follow:, tail:, since:, timestamps:
141
- # @return [String] Raw log stream
142
- model.logs(query_params = { stdout: 1, stderr: 1 })
143
- ```
144
-
145
- ## Service — Métodos específicos
146
-
147
- ```ruby
148
- service = DockerSwarm::Service.find("service_id")
149
-
150
- # Reiniciar servicio (fuerza recreación de tasks)
151
- # Equivalente a `docker service update --force`
152
- # @return [Boolean]
153
- service.restart
154
- ```
155
-
156
- ## Container — Métodos específicos
157
-
158
- ```ruby
159
- container = DockerSwarm::Container.find("container_id")
160
-
161
- # Iniciar contenedor detenido
162
- # @return [Boolean]
163
- container.start
164
-
165
- # Detener contenedor en ejecución
166
- # @return [Boolean]
167
- container.stop
168
- ```
169
-
170
- ## System — Métodos estáticos
171
-
172
- ```ruby
173
- # Ping al daemon
174
- # @return [String] "OK"
175
- DockerSwarm::System.up
176
-
177
- # Información del daemon
178
- # @return [Hash] (Containers, Images, Driver, MemoryLimit, etc.)
179
- DockerSwarm::System.info
180
-
181
- # Versión de Docker
182
- # @return [Hash] (Version, ApiVersion, Os, Arch, etc.)
183
- DockerSwarm::System.version
184
-
185
- # Uso de disco
186
- # @return [Hash] (LayersSize, Images, Containers, Volumes)
187
- DockerSwarm::System.df
188
- ```
189
-
190
- ## Swarm — Método estático
191
-
192
- ```ruby
193
- # Información del cluster Swarm
194
- # @return [Hash] (ID, Version, Spec, JoinTokens, etc.)
195
- DockerSwarm::Swarm.show
196
- ```
197
-
198
- ## Volume — Particularidad
199
-
200
- Volume sobreescribe `root_key` porque Docker envuelve la respuesta en `{"Volumes": [...]}`:
201
-
202
- ```ruby
203
- class Volume < Base
204
- def self.root_key = "Volumes"
205
- end
206
- ```
207
-
208
- ## Api — Bajo nivel
209
-
210
- ```ruby
211
- # Request directo al Docker API
212
- # @param action [Hash] {method:, path:} desde ENDPOINTS
213
- # @param arguments [Hash] Interpolación en path (id:)
214
- # @param query_params [Hash] Query string
215
- # @param payload [Hash, nil] Body del request
216
- DockerSwarm::Api.request(action:, arguments: {}, query_params: {}, payload: nil)
217
- ```
218
-
219
- ### Endpoints registrados
220
-
221
- Todos definidos en `Api::ENDPOINTS` como Hash frozen:
222
-
223
- | Recurso | Acciones |
224
- |---------|----------|
225
- | swarm | show |
226
- | system | info, version, up, df |
227
- | nodes | index, show, update, destroy |
228
- | tasks | index, show, logs |
229
- | services | index, show, create, update, destroy, logs |
230
- | configs | index, show, create, destroy |
231
- | secrets | index, show, create, destroy |
232
- | networks | index, show, create, update, destroy |
233
- | volumes | index, show, create, destroy |
234
- | containers | index, show, create, start, stop, destroy, logs |
235
- | images | index, show, create, destroy |
236
-
237
- ## Middleware Stack
238
-
239
- Orden de ejecución en Excon:
240
-
241
- 1. **Excon defaults** (Retry, Instrumentor, etc.)
242
- 2. **Excon::Middleware::RedirectFollower**
243
- 3. **RequestEncoder** — Serializa body: JSON (default), form-urlencoded, multipart. Detecta por Content-Type header.
244
- 4. **ResponseJSONParser** — Parsea JSON si Content-Type incluye `application/json`. Aplica `with_indifferent_access` recursivo (Hash y Array de Hashes).
245
- 5. **ErrorHandler** — Status 4xx/5xx → excepción tipada. Loguea `business_error` antes de raise.
246
-
247
- ## Configuración
248
-
249
- | Opción | Tipo | Default | Descripción |
250
- |--------|------|---------|-------------|
251
- | `socket_path` | String | `unix:///var/run/docker.sock` | Socket Unix o URL TCP |
252
- | `logger` | Logger | `Logger.new($stdout)` | Logger para KV output |
253
- | `log_level` | Integer | `Logger::INFO` | Nivel de log (se aplica al logger) |
254
- | `read_timeout` | Float | `60.0` | Timeout lectura (segundos) |
255
- | `write_timeout` | Float | `60.0` | Timeout escritura (segundos) |
256
- | `connect_timeout` | Float | `10.0` | Timeout conexión (segundos) |
257
- | `max_retries` | Integer | `3` | Reintentos en Socket/Timeout errors |
@@ -1,157 +0,0 @@
1
- # Catálogo de Errores
2
-
3
- Referencia completa de excepciones de DockerSwarm. Todas heredan de `DockerSwarm::Error`.
4
-
5
- ## Jerarquía
6
-
7
- ```
8
- DockerSwarm::Error (base)
9
- ├── BadRequest (400)
10
- ├── Unauthorized (401)
11
- ├── Forbidden (403)
12
- ├── NotFound (404)
13
- ├── NotAcceptable (406)
14
- ├── RequestTimeout (408)
15
- ├── Conflict (409)
16
- ├── UnprocessableEntity (422)
17
- ├── TooManyRequests (429)
18
- ├── InternalServerError (500)
19
- ├── BadGateway (502)
20
- ├── ServiceUnavailable (503)
21
- ├── GatewayTimeout (504)
22
- └── Communication (socket/red)
23
- ```
24
-
25
- ## Acceso
26
-
27
- Cada excepción tiene 3 formas de acceso equivalentes:
28
-
29
- ```ruby
30
- DockerSwarm::NotFound # alias directo (recomendado)
31
- DockerSwarm::Error::NotFound # acceso via clase Error
32
- DockerSwarm::Errors::NotFound # módulo Errors (const_missing dinámico)
33
- ```
34
-
35
- ## Catálogo completo
36
-
37
- ### BadRequest (400)
38
-
39
- **Causa:** Payload malformado o parámetros inválidos.
40
- **Reproducción:** Enviar JSON con campos incorrectos (ej: `Replicas: "abc"` en vez de integer).
41
- **Resolución:** Validar payload contra la documentación de Docker API. Verificar tipos de datos.
42
-
43
- ### Unauthorized (401)
44
-
45
- **Causa:** Credenciales inválidas o ausentes para Docker daemon con TLS.
46
- **Reproducción:** Conectar a daemon protegido sin certificados.
47
- **Resolución:** Configurar TLS client certificates en Excon o en la URL de conexión.
48
-
49
- ### Forbidden (403)
50
-
51
- **Causa:** Permisos insuficientes para la operación.
52
- **Reproducción:** Intentar operación de swarm en un nodo worker.
53
- **Resolución:** Verificar que el nodo es manager y que el usuario tiene permisos sobre el socket.
54
-
55
- ### NotFound (404)
56
-
57
- **Causa:** Recurso no existe o fue eliminado.
58
- **Reproducción:** `Service.find("id_inexistente")`.
59
- **Resolución:** `Base.find` retorna `nil` automáticamente. `Deletable#destroy` también es graceful (retorna `nil` en 404). No requiere rescue manual en estos casos.
60
-
61
- ### NotAcceptable (406)
62
-
63
- **Causa:** El servidor no puede producir una respuesta aceptable.
64
- **Reproducción:** Raro en Docker API.
65
- **Resolución:** Verificar headers Accept del request.
66
-
67
- ### RequestTimeout (408)
68
-
69
- **Causa:** Docker daemon tardó demasiado en responder.
70
- **Reproducción:** Operación en un cluster sobrecargado.
71
- **Resolución:** Incrementar `read_timeout` en configuración. Verificar estado del cluster.
72
-
73
- ### Conflict (409)
74
-
75
- **Causa:** Nombre duplicado, recurso en uso, o versión desactualizada.
76
- **Reproducción:** `Service.create(Name: "nombre_existente")` o update con `Version.Index` stale.
77
- **Resolución:** Para nombres: verificar existencia antes de crear. Para versiones: hacer `reload` y reintentar el update.
78
-
79
- ### UnprocessableEntity (422)
80
-
81
- **Causa:** Payload semánticamente inválido.
82
- **Reproducción:** Crear servicio con imagen inexistente y restricciones de scheduling imposibles.
83
- **Resolución:** Verificar que la imagen existe y que las constraints del servicio son alcanzables.
84
-
85
- ### TooManyRequests (429)
86
-
87
- **Causa:** Rate limiting del Docker daemon o registry.
88
- **Reproducción:** Burst de requests al API.
89
- **Resolución:** Implementar backoff. Los retries de Excon cubren errores de socket/timeout pero no 429.
90
-
91
- ### InternalServerError (500)
92
-
93
- **Causa:** Error interno del Docker daemon.
94
- **Reproducción:** Bug en Docker, operación sobre estado inconsistente, o update sin `version` query param.
95
- **Resolución:** Verificar logs del Docker daemon (`journalctl -u docker`). Si es por version faltante, usar el modelo (Updatable lo maneja).
96
-
97
- ### BadGateway (502)
98
-
99
- **Causa:** Proxy o load balancer entre cliente y daemon devuelve error.
100
- **Reproducción:** Docker daemon detrás de reverse proxy caído.
101
- **Resolución:** Verificar infraestructura de red y proxy.
102
-
103
- ### ServiceUnavailable (503)
104
-
105
- **Causa:** Docker daemon reiniciando o en mantenimiento.
106
- **Reproducción:** Request durante restart del servicio Docker.
107
- **Resolución:** Reintentar después de esperar. `max_retries` cubre errores de socket pero no 503.
108
-
109
- ### GatewayTimeout (504)
110
-
111
- **Causa:** Proxy entre cliente y daemon timeout.
112
- **Reproducción:** Operación larga detrás de proxy con timeout corto.
113
- **Resolución:** Incrementar timeout del proxy. Verificar que `read_timeout` de la gema es menor que el del proxy.
114
-
115
- ### Communication
116
-
117
- **Causa:** Error de socket o red — daemon caído, socket inexistente, permisos insuficientes.
118
- **Reproducción:** `DockerSwarm::System.up` con daemon apagado.
119
- **Resolución:** Verificar que Docker está corriendo (`systemctl status docker`), que el socket existe y que el usuario tiene permisos de lectura.
120
-
121
- ## Manejo recomendado
122
-
123
- ```ruby
124
- begin
125
- DockerSwarm::Service.create(Name: "web", TaskTemplate: { ... })
126
- rescue DockerSwarm::Conflict => e
127
- # Nombre duplicado — buscar existente
128
- existing = DockerSwarm::Service.all(name: ["web"]).first
129
- rescue DockerSwarm::Communication => e
130
- # Docker caído
131
- logger.error("Docker unreachable: #{e.message}")
132
- rescue DockerSwarm::Error => e
133
- # Cualquier otro error de Docker
134
- logger.error("Docker error: #{e.class} #{e.message}")
135
- end
136
- ```
137
-
138
- ## Logging automático
139
-
140
- El middleware ErrorHandler loguea automáticamente antes de raise:
141
-
142
- ```
143
- component=docker_swarm.middleware.error_handler event=business_error source=http status=409 message="name conflicts" method=post path=/services/create
144
- ```
145
-
146
- Formato KV con masking de datos sensibles via LogHelper.
147
-
148
- ## Causa original (Excon wrapping)
149
-
150
- Excon puede envolver excepciones del middleware en `Excon::Error::Socket`. Connection detecta esto y re-raise la excepción original:
151
-
152
- ```ruby
153
- # Connection#request internamente:
154
- actual_error = e.cause&.class&.name&.include?("DockerSwarm::Error") ? e.cause : e
155
- ```
156
-
157
- La excepción original mantiene la causa via `Exception#cause` de Ruby para trazabilidad completa.