swarmkit-client 0.1.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 +7 -0
- data/LICENSE +201 -0
- data/NOTICE +485 -0
- data/README.md +257 -0
- data/bin/swarmkit-client +6 -0
- data/docs/api.md +3607 -0
- data/lib/swarmkit-client/api.rb +51 -0
- data/lib/swarmkit-client/cli.rb +159 -0
- data/lib/swarmkit-client/schema.pb +0 -0
- data/lib/swarmkit-client.rb +50 -0
- data/lib/version.rb +5 -0
- data/schema/sources.json +58 -0
- metadata +80 -0
data/README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# swarmkit-client
|
|
2
|
+
|
|
3
|
+
A compact Ruby client for SwarmKit's internal gRPC API: **65 RPCs, 11 services**,
|
|
4
|
+
full protobuf types, TLS, deadlines and streaming. This connects directly to
|
|
5
|
+
SwarmKit; it is separate from Docker Engine's HTTP API.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Ruby 3.2 or newer. From this checkout:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bundle install
|
|
13
|
+
bundle exec rake build
|
|
14
|
+
gem install pkg/swarmkit-client-0.1.0.gem
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or use `gem 'swarmkit-client', path: '../swarmkit-client'` in another project's Gemfile.
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
Installing the gem adds `swarmkit-client`. In this checkout, use
|
|
22
|
+
`bundle exec ruby bin/swarmkit-client` before installation.
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
swarmkit-client --help
|
|
26
|
+
swarmkit-client methods # All 65 RPCs; offline
|
|
27
|
+
swarmkit-client methods control # RPCs for one service
|
|
28
|
+
swarmkit-client describe control get_service # Request/response fields, nested types and enums; offline
|
|
29
|
+
|
|
30
|
+
swarmkit-client health check '{"service":"ControlAPI"}'
|
|
31
|
+
swarmkit-client control list_services '{"filters":{"names":["grib_grib_loader"]}}'
|
|
32
|
+
swarmkit-client control get_service '{"service_id":"service-id"}'
|
|
33
|
+
swarmkit-client --timeout 30 watch watch '{"entries":[{"kind":"service","action":7}]}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Syntax: `swarmkit-client [options] SERVICE METHOD [JSON|@FILE|-]`. Service and
|
|
37
|
+
method names match the Ruby client. Omitted JSON means `{}`; use `@request.json`
|
|
38
|
+
to read a file or `-` to read stdin. Client-streaming RPCs accept a JSON array.
|
|
39
|
+
Input follows protobuf JSON, accepting snake_case or lowerCamelCase field names;
|
|
40
|
+
unknown fields are rejected before connecting.
|
|
41
|
+
|
|
42
|
+
Connection flags are `--target`, `--ca`, `--cert`, `--key`, `--server-name` and
|
|
43
|
+
`--timeout`; defaults match the [Ruby client](#connection-options). For a forwarded socket:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
swarmkit-client --target unix:/tmp/swarmkit.sock --ca ./swarm-root-ca.crt \
|
|
47
|
+
control get_service @request.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Each response is one JSON line on stdout, flushed immediately for streams.
|
|
51
|
+
Output uses protobuf JSON with snake_case names, string-encoded 64-bit integers
|
|
52
|
+
and omitted default scalar fields. Errors go to stderr without echoing request
|
|
53
|
+
values or server error details. Exit codes: `0` success (including a closed output
|
|
54
|
+
pipe), `1` RPC/I/O failure, `2` invalid input, `130` interruption. Reaching a stream
|
|
55
|
+
deadline returns `1`, with gRPC status `4`; already received lines remain valid.
|
|
56
|
+
Streams are cancelled and the channel closed on completion, error or Ctrl-C.
|
|
57
|
+
|
|
58
|
+
The CLI emits diagnostic JSON with credential-bearing fields redacted, including
|
|
59
|
+
registry auth, passwords, tokens, private/unlock keys, opaque data, environment,
|
|
60
|
+
command arguments, labels and options. These become `"[REDACTED]"`. Use the Ruby
|
|
61
|
+
API for complete objects; CLI output is unsuitable for read/modify/write updates.
|
|
62
|
+
Explicit mutation commands run directly, for example
|
|
63
|
+
`swarmkit-client control update_service @update.json`; the request must include
|
|
64
|
+
the complete spec and current version as shown [below](#update-a-service).
|
|
65
|
+
|
|
66
|
+
## Read services
|
|
67
|
+
|
|
68
|
+
Run on a Swarm manager with permission to access its control socket and root CA:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
require 'swarmkit-client'
|
|
72
|
+
|
|
73
|
+
api = Swarmkit::API
|
|
74
|
+
Swarmkit::Client.open do |client|
|
|
75
|
+
request = api::ListServicesRequest.new(filters: {names: ['grib_grib_loader']})
|
|
76
|
+
client.control.list_services(request).services.each do |service|
|
|
77
|
+
puts "#{service.id}: #{service.spec.task.container&.image}"
|
|
78
|
+
end
|
|
79
|
+
status = client.health.check(api::HealthCheckRequest.new(service: 'ControlAPI'))
|
|
80
|
+
puts status.status # :SERVING
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`Client.open` closes the shared channel even if the block raises. For a persistent
|
|
85
|
+
connection, use `Client.new` and call `close` when finished. All service stubs
|
|
86
|
+
share that connection; close it only after its callers and streams finish.
|
|
87
|
+
|
|
88
|
+
Internal responses can contain stored registry credentials, configuration data
|
|
89
|
+
and tokens. Select the fields you need; avoid logging complete protobuf objects.
|
|
90
|
+
|
|
91
|
+
## Connection options
|
|
92
|
+
|
|
93
|
+
| Keyword | Default | Meaning |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `target` | `unix:/var/run/docker/swarm/control.sock` | Native gRPC target, including TCP `host:port` |
|
|
96
|
+
| `ca` | `/var/lib/docker/swarm/certificates/swarm-root-ca.crt` | Path to trusted CA PEM |
|
|
97
|
+
| `cert`, `key` | `nil` | Paths to client certificate and private key PEM; provide both for mutual TLS |
|
|
98
|
+
| `server_name` | `swarm-manager` | Expected certificate SAN and authority; `nil` uses the target hostname |
|
|
99
|
+
| `timeout` | `10` | Positive finite default deadline duration in seconds, including streams |
|
|
100
|
+
| `channel_args` | `{}` | Native gRPC channel settings; default receive limit is 16 MiB |
|
|
101
|
+
|
|
102
|
+
The inspected Docker 29.2.0 manager uses **TLS even on its Unix socket**.
|
|
103
|
+
`server_name` selects the identity to verify against the trusted CA; verification
|
|
104
|
+
stays enabled. Through an SSH Unix-socket forward, supply the forwarded socket
|
|
105
|
+
as `target` and a local copy of the public root CA as `ca`.
|
|
106
|
+
|
|
107
|
+
Remote TCP example, with paths to authorized Swarm certificates:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
client = Swarmkit::Client.new(
|
|
111
|
+
target: 'manager.example:2377', ca: '/secure/swarm-root-ca.crt',
|
|
112
|
+
cert: '/secure/swarm-node.crt', key: '/secure/swarm-node.key'
|
|
113
|
+
)
|
|
114
|
+
begin
|
|
115
|
+
# Use client.control, client.watch, etc.
|
|
116
|
+
ensure
|
|
117
|
+
client.close
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The remote manager enforces certificate roles, organization and revocation.
|
|
122
|
+
Socket access uses a different authorization path. `raft` and `raft_membership`
|
|
123
|
+
are registered only on the network server in the inspected build. Having a
|
|
124
|
+
client method does not make that RPC available or authorized on every endpoint.
|
|
125
|
+
|
|
126
|
+
## API and types
|
|
127
|
+
|
|
128
|
+
The [complete reference](docs/api.md) lists **every method, request parameter,
|
|
129
|
+
response property, nested type, field number, default, oneof and enum value**.
|
|
130
|
+
|
|
131
|
+
| Client accessor | RPCs | Purpose |
|
|
132
|
+
|---|---:|---|
|
|
133
|
+
| `control` | 43 | Nodes, tasks, services, networks, clusters, secrets, configs, volumes and custom objects |
|
|
134
|
+
| `dispatcher` | 6 | Worker sessions, assignments and status updates |
|
|
135
|
+
| `ca` | 2 | Cluster certificates and root CA |
|
|
136
|
+
| `node_ca` | 2 | Node certificate issuance and status |
|
|
137
|
+
| `raft` | 3 | Raft transport and member address lookup |
|
|
138
|
+
| `raft_membership` | 2 | Raft membership changes |
|
|
139
|
+
| `logs` | 1 | Log subscriptions |
|
|
140
|
+
| `log_broker` | 2 | Log subscription delivery and publishing |
|
|
141
|
+
| `watch` | 1 | Store event subscriptions |
|
|
142
|
+
| `health` | 1 | Named service health |
|
|
143
|
+
| `resource_allocator` | 2 | Network attachment allocation |
|
|
144
|
+
|
|
145
|
+
Stubs follow native [Ruby gRPC](https://grpc.io/docs/languages/ruby/basics/):
|
|
146
|
+
snake_case RPC names, typed request objects and typed responses. Construct
|
|
147
|
+
nested messages from hashes, use symbols for named enums and integers for enum
|
|
148
|
+
bitmasks such as Watch action `7`. Protobuf field names are preserved; Go's custom
|
|
149
|
+
names from `gogoproto` options do not rename the Ruby API.
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
request = Swarmkit::API::GetServiceRequest.new(service_id: 'service-id')
|
|
153
|
+
response = client.control.get_service(request, deadline: Time.now + 5)
|
|
154
|
+
service = response.service
|
|
155
|
+
|
|
156
|
+
# Inspect field definitions without reading or dumping object values.
|
|
157
|
+
Swarmkit::API::GetServiceRequest.descriptor.each do |field|
|
|
158
|
+
puts "#{field.name}: #{field.type}"
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
All SwarmKit types are under `Swarmkit::API`, including nested constants such as
|
|
163
|
+
`ContainerSpec::PullOptions`. Imported protobuf/raft types can be obtained from
|
|
164
|
+
`Swarmkit::API::POOL.lookup('raftpb.Message').msgclass`; nested hashes normally
|
|
165
|
+
make this unnecessary. The private descriptor pool prevents collisions with
|
|
166
|
+
other gems that load SwarmKit definitions.
|
|
167
|
+
|
|
168
|
+
Native gRPC options (`deadline`, `metadata`, `return_op`, `parent`, `credentials`)
|
|
169
|
+
pass through unchanged. Failures raise native `GRPC::BadStatus` subclasses such
|
|
170
|
+
as `GRPC::NotFound`, `GRPC::PermissionDenied` and `GRPC::DeadlineExceeded`.
|
|
171
|
+
The client adds no application-level retries. A mutation may have committed
|
|
172
|
+
before a deadline error; read the current state before deciding to retry.
|
|
173
|
+
|
|
174
|
+
## Update a service
|
|
175
|
+
|
|
176
|
+
This writes to the manager when called. Read the complete current spec and use
|
|
177
|
+
its **`meta.version`** for optimistic concurrency:
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
Swarmkit::Client.open do |client|
|
|
181
|
+
api = Swarmkit::API
|
|
182
|
+
service = client.control.get_service(api::GetServiceRequest.new(service_id: 'service-id')).service
|
|
183
|
+
service.spec.task.container.image = 'registry.example/app:v2'
|
|
184
|
+
client.control.update_service(api::UpdateServiceRequest.new(
|
|
185
|
+
service_id: service.id, service_version: service.meta.version, spec: service.spec
|
|
186
|
+
))
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The bundled descriptor contains the full upstream schema, including registry
|
|
191
|
+
auth, maps and oneofs. It is not the reduced read projection used by the earlier
|
|
192
|
+
incident diagnostic script. Do not build updates from that reduced projection.
|
|
193
|
+
|
|
194
|
+
## Watch and cancel
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
Swarmkit::Client.open do |client|
|
|
198
|
+
request = Swarmkit::API::WatchRequest.new(entries: [{kind: 'service', action: 7}])
|
|
199
|
+
operation = client.watch.watch(request, deadline: Time.now + 30, return_op: true)
|
|
200
|
+
begin
|
|
201
|
+
operation.execute.each do |message|
|
|
202
|
+
next if message.events.empty? # Initial readiness response.
|
|
203
|
+
|
|
204
|
+
puts "Store version: #{message.version.index}"
|
|
205
|
+
message.events.each { |event| puts event.action }
|
|
206
|
+
end
|
|
207
|
+
rescue GRPC::DeadlineExceeded
|
|
208
|
+
# The requested observation period ended.
|
|
209
|
+
ensure
|
|
210
|
+
operation.cancel
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Save event `version` to resume with `resume_from: {index: version}`. Compacted
|
|
216
|
+
history requires a fresh snapshot. `include_old_object` applies to live updates,
|
|
217
|
+
not replay. Always cancel an operation when stopping consumption early.
|
|
218
|
+
Client-streaming RPCs such as `log_broker.publish_logs` accept an Enumerable of
|
|
219
|
+
typed request messages. Their use belongs to the corresponding SwarmKit protocol.
|
|
220
|
+
|
|
221
|
+
## Development and release
|
|
222
|
+
|
|
223
|
+
The layout, RSpec approach, JUnit reports, Rake tasks and publish-on-main workflow
|
|
224
|
+
follow [artyomb/dry-stack](https://github.com/artyomb/dry-stack). The client and CLI
|
|
225
|
+
share descriptor-based bindings without hand-written wrappers for individual
|
|
226
|
+
RPCs. The CLI uses the standard library's OptionParser and JSON.
|
|
227
|
+
|
|
228
|
+
```sh
|
|
229
|
+
bundle exec rake # RuboCop + RSpec; results/rspec.xml
|
|
230
|
+
bundle exec rake spec # Local TLS fixtures; no production access
|
|
231
|
+
bundle exec rake build # Checks, then pkg/swarmkit-client-VERSION.gem
|
|
232
|
+
bundle exec rake schema # Fetch checksum-pinned sources and compile
|
|
233
|
+
bundle exec rake docs # Regenerate the complete field reference
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Specs cover TLS Unix sockets, mutual TLS TCP, certificate verification, complete
|
|
237
|
+
read/update messages, status errors, deadlines, both stream directions and
|
|
238
|
+
cancellation. Update tests use an in-process fixture, not a production manager.
|
|
239
|
+
CLI specs also cover discovery, schema output, input sources, redaction,
|
|
240
|
+
streaming, exit codes and executable startup.
|
|
241
|
+
|
|
242
|
+
GitHub Actions checks Ruby 3.2 and 3.4 on pull requests and pushes to `main`.
|
|
243
|
+
After successful checks, a push to `main` runs `rake push`, using repository secret
|
|
244
|
+
`RUBYGEMS_API_KEY` through `GEM_HOST_API_KEY`. Configure that secret and ensure the
|
|
245
|
+
gem name is available before enabling publication. Bump `lib/version.rb` for
|
|
246
|
+
each published version; RubyGems rejects an existing version. `rake push` also
|
|
247
|
+
publishes locally when explicitly invoked.
|
|
248
|
+
|
|
249
|
+
Schema: [moby/swarmkit `17b8d222e7dd`](https://github.com/moby/swarmkit/tree/17b8d222e7dd),
|
|
250
|
+
the revision pinned by the inspected Docker 29.2.0 build. This is an internal,
|
|
251
|
+
version-sensitive API. `schema/sources.json` pins source URLs and SHA-256 values;
|
|
252
|
+
`grpc-tools` 1.85.0 supplies protoc 35.1. Source comments are stripped from the
|
|
253
|
+
runtime descriptor. To change revisions, update the manifest URLs/checksums,
|
|
254
|
+
rebuild schema and docs, then run the specs against the intended Docker version.
|
|
255
|
+
No compiler or network access is needed to load the installed gem.
|
|
256
|
+
|
|
257
|
+
Apache-2.0; bundled schema attributions and dependency licenses are in [NOTICE](NOTICE).
|