github.com/rawahars/moby@v24.0.4+incompatible/api/server/router/swarm/cluster_routes.go (about) 1 package swarm // import "github.com/docker/docker/api/server/router/swarm" 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strconv" 8 9 "github.com/docker/docker/api/server/httputils" 10 basictypes "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/backend" 12 "github.com/docker/docker/api/types/filters" 13 "github.com/docker/docker/api/types/registry" 14 types "github.com/docker/docker/api/types/swarm" 15 "github.com/docker/docker/api/types/versions" 16 "github.com/docker/docker/errdefs" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 ) 20 21 func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 22 var req types.InitRequest 23 if err := httputils.ReadJSON(r, &req); err != nil { 24 return err 25 } 26 version := httputils.VersionFromContext(ctx) 27 28 // DefaultAddrPool and SubnetSize were added in API 1.39. Ignore on older API versions. 29 if versions.LessThan(version, "1.39") { 30 req.DefaultAddrPool = nil 31 req.SubnetSize = 0 32 } 33 // DataPathPort was added in API 1.40. Ignore this option on older API versions. 34 if versions.LessThan(version, "1.40") { 35 req.DataPathPort = 0 36 } 37 nodeID, err := sr.backend.Init(req) 38 if err != nil { 39 logrus.WithContext(ctx).WithError(err).Debug("Error initializing swarm") 40 return err 41 } 42 return httputils.WriteJSON(w, http.StatusOK, nodeID) 43 } 44 45 func (sr *swarmRouter) joinCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 46 var req types.JoinRequest 47 if err := httputils.ReadJSON(r, &req); err != nil { 48 return err 49 } 50 return sr.backend.Join(req) 51 } 52 53 func (sr *swarmRouter) leaveCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 54 if err := httputils.ParseForm(r); err != nil { 55 return err 56 } 57 58 force := httputils.BoolValue(r, "force") 59 return sr.backend.Leave(ctx, force) 60 } 61 62 func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 63 swarm, err := sr.backend.Inspect() 64 if err != nil { 65 logrus.WithContext(ctx).WithError(err).Debug("Error getting swarm") 66 return err 67 } 68 69 return httputils.WriteJSON(w, http.StatusOK, swarm) 70 } 71 72 func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 73 var swarm types.Spec 74 if err := httputils.ReadJSON(r, &swarm); err != nil { 75 return err 76 } 77 78 rawVersion := r.URL.Query().Get("version") 79 version, err := strconv.ParseUint(rawVersion, 10, 64) 80 if err != nil { 81 err := fmt.Errorf("invalid swarm version '%s': %v", rawVersion, err) 82 return errdefs.InvalidParameter(err) 83 } 84 85 var flags types.UpdateFlags 86 87 if value := r.URL.Query().Get("rotateWorkerToken"); value != "" { 88 rot, err := strconv.ParseBool(value) 89 if err != nil { 90 err := fmt.Errorf("invalid value for rotateWorkerToken: %s", value) 91 return errdefs.InvalidParameter(err) 92 } 93 94 flags.RotateWorkerToken = rot 95 } 96 97 if value := r.URL.Query().Get("rotateManagerToken"); value != "" { 98 rot, err := strconv.ParseBool(value) 99 if err != nil { 100 err := fmt.Errorf("invalid value for rotateManagerToken: %s", value) 101 return errdefs.InvalidParameter(err) 102 } 103 104 flags.RotateManagerToken = rot 105 } 106 107 if value := r.URL.Query().Get("rotateManagerUnlockKey"); value != "" { 108 rot, err := strconv.ParseBool(value) 109 if err != nil { 110 return errdefs.InvalidParameter(fmt.Errorf("invalid value for rotateManagerUnlockKey: %s", value)) 111 } 112 113 flags.RotateManagerUnlockKey = rot 114 } 115 116 if err := sr.backend.Update(version, swarm, flags); err != nil { 117 logrus.WithContext(ctx).WithError(err).Debug("Error configuring swarm") 118 return err 119 } 120 return nil 121 } 122 123 func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 124 var req types.UnlockRequest 125 if err := httputils.ReadJSON(r, &req); err != nil { 126 return err 127 } 128 129 if err := sr.backend.UnlockSwarm(req); err != nil { 130 logrus.WithContext(ctx).WithError(err).Debug("Error unlocking swarm") 131 return err 132 } 133 return nil 134 } 135 136 func (sr *swarmRouter) getUnlockKey(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 137 unlockKey, err := sr.backend.GetUnlockKey() 138 if err != nil { 139 logrus.WithContext(ctx).WithError(err).Debug("Error retrieving swarm unlock key") 140 return err 141 } 142 143 return httputils.WriteJSON(w, http.StatusOK, &basictypes.SwarmUnlockKeyResponse{ 144 UnlockKey: unlockKey, 145 }) 146 } 147 148 func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 149 if err := httputils.ParseForm(r); err != nil { 150 return err 151 } 152 filter, err := filters.FromJSON(r.Form.Get("filters")) 153 if err != nil { 154 return err 155 } 156 157 // the status query parameter is only support in API versions >= 1.41. If 158 // the client is using a lesser version, ignore the parameter. 159 cliVersion := httputils.VersionFromContext(ctx) 160 var status bool 161 if value := r.URL.Query().Get("status"); value != "" && !versions.LessThan(cliVersion, "1.41") { 162 var err error 163 status, err = strconv.ParseBool(value) 164 if err != nil { 165 return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for status: %s", value) 166 } 167 } 168 169 services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter, Status: status}) 170 if err != nil { 171 logrus.WithContext(ctx).WithError(err).Debug("Error getting services") 172 return err 173 } 174 175 return httputils.WriteJSON(w, http.StatusOK, services) 176 } 177 178 func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 179 var insertDefaults bool 180 181 if value := r.URL.Query().Get("insertDefaults"); value != "" { 182 var err error 183 insertDefaults, err = strconv.ParseBool(value) 184 if err != nil { 185 return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for insertDefaults: %s", value) 186 } 187 } 188 189 // you may note that there is no code here to handle the "status" query 190 // parameter, as in getServices. the Status field is not supported when 191 // retrieving an individual service because the Backend API changes 192 // required to accommodate it would be too disruptive, and because that 193 // field is so rarely needed as part of an individual service inspection. 194 195 service, err := sr.backend.GetService(vars["id"], insertDefaults) 196 if err != nil { 197 logrus.WithContext(ctx).WithFields(logrus.Fields{ 198 "error": err, 199 "service-id": vars["id"], 200 }).Debug("Error getting service") 201 return err 202 } 203 204 return httputils.WriteJSON(w, http.StatusOK, service) 205 } 206 207 func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 208 var service types.ServiceSpec 209 if err := httputils.ReadJSON(r, &service); err != nil { 210 return err 211 } 212 213 // Get returns "" if the header does not exist 214 encodedAuth := r.Header.Get(registry.AuthHeader) 215 queryRegistry := false 216 if v := httputils.VersionFromContext(ctx); v != "" { 217 if versions.LessThan(v, "1.30") { 218 queryRegistry = true 219 } 220 adjustForAPIVersion(v, &service) 221 } 222 resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry) 223 if err != nil { 224 logrus.WithContext(ctx).WithFields(logrus.Fields{ 225 "error": err, 226 "service-name": service.Name, 227 }).Debug("Error creating service") 228 return err 229 } 230 231 return httputils.WriteJSON(w, http.StatusCreated, resp) 232 } 233 234 func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 235 var service types.ServiceSpec 236 if err := httputils.ReadJSON(r, &service); err != nil { 237 return err 238 } 239 240 rawVersion := r.URL.Query().Get("version") 241 version, err := strconv.ParseUint(rawVersion, 10, 64) 242 if err != nil { 243 err := fmt.Errorf("invalid service version '%s': %v", rawVersion, err) 244 return errdefs.InvalidParameter(err) 245 } 246 247 var flags basictypes.ServiceUpdateOptions 248 249 // Get returns "" if the header does not exist 250 flags.EncodedRegistryAuth = r.Header.Get(registry.AuthHeader) 251 flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom") 252 flags.Rollback = r.URL.Query().Get("rollback") 253 queryRegistry := false 254 if v := httputils.VersionFromContext(ctx); v != "" { 255 if versions.LessThan(v, "1.30") { 256 queryRegistry = true 257 } 258 adjustForAPIVersion(v, &service) 259 } 260 261 resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry) 262 if err != nil { 263 logrus.WithContext(ctx).WithFields(logrus.Fields{ 264 "error": err, 265 "service-id": vars["id"], 266 }).Debug("Error updating service") 267 return err 268 } 269 return httputils.WriteJSON(w, http.StatusOK, resp) 270 } 271 272 func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 273 if err := sr.backend.RemoveService(vars["id"]); err != nil { 274 logrus.WithContext(ctx).WithFields(logrus.Fields{ 275 "error": err, 276 "service-id": vars["id"], 277 }).Debug("Error removing service") 278 return err 279 } 280 return nil 281 } 282 283 func (sr *swarmRouter) getTaskLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 284 if err := httputils.ParseForm(r); err != nil { 285 return err 286 } 287 288 // make a selector to pass to the helper function 289 selector := &backend.LogSelector{ 290 Tasks: []string{vars["id"]}, 291 } 292 return sr.swarmLogs(ctx, w, r, selector) 293 } 294 295 func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 296 if err := httputils.ParseForm(r); err != nil { 297 return err 298 } 299 300 // make a selector to pass to the helper function 301 selector := &backend.LogSelector{ 302 Services: []string{vars["id"]}, 303 } 304 return sr.swarmLogs(ctx, w, r, selector) 305 } 306 307 func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 308 if err := httputils.ParseForm(r); err != nil { 309 return err 310 } 311 filter, err := filters.FromJSON(r.Form.Get("filters")) 312 if err != nil { 313 return err 314 } 315 316 nodes, err := sr.backend.GetNodes(basictypes.NodeListOptions{Filters: filter}) 317 if err != nil { 318 logrus.WithContext(ctx).WithError(err).Debug("Error getting nodes") 319 return err 320 } 321 322 return httputils.WriteJSON(w, http.StatusOK, nodes) 323 } 324 325 func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 326 node, err := sr.backend.GetNode(vars["id"]) 327 if err != nil { 328 logrus.WithContext(ctx).WithFields(logrus.Fields{ 329 "error": err, 330 "node-id": vars["id"], 331 }).Debug("Error getting node") 332 return err 333 } 334 335 return httputils.WriteJSON(w, http.StatusOK, node) 336 } 337 338 func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 339 var node types.NodeSpec 340 if err := httputils.ReadJSON(r, &node); err != nil { 341 return err 342 } 343 344 rawVersion := r.URL.Query().Get("version") 345 version, err := strconv.ParseUint(rawVersion, 10, 64) 346 if err != nil { 347 err := fmt.Errorf("invalid node version '%s': %v", rawVersion, err) 348 return errdefs.InvalidParameter(err) 349 } 350 351 if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil { 352 logrus.WithContext(ctx).WithFields(logrus.Fields{ 353 "error": err, 354 "node-id": vars["id"], 355 }).Debug("Error updating node") 356 return err 357 } 358 return nil 359 } 360 361 func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 362 if err := httputils.ParseForm(r); err != nil { 363 return err 364 } 365 366 force := httputils.BoolValue(r, "force") 367 368 if err := sr.backend.RemoveNode(vars["id"], force); err != nil { 369 logrus.WithContext(ctx).WithFields(logrus.Fields{ 370 "error": err, 371 "node-id": vars["id"], 372 }).Debug("Error removing node") 373 return err 374 } 375 return nil 376 } 377 378 func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 379 if err := httputils.ParseForm(r); err != nil { 380 return err 381 } 382 filter, err := filters.FromJSON(r.Form.Get("filters")) 383 if err != nil { 384 return err 385 } 386 387 tasks, err := sr.backend.GetTasks(basictypes.TaskListOptions{Filters: filter}) 388 if err != nil { 389 logrus.WithContext(ctx).WithError(err).Debug("Error getting tasks") 390 return err 391 } 392 393 return httputils.WriteJSON(w, http.StatusOK, tasks) 394 } 395 396 func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 397 task, err := sr.backend.GetTask(vars["id"]) 398 if err != nil { 399 logrus.WithContext(ctx).WithFields(logrus.Fields{ 400 "error": err, 401 "task-id": vars["id"], 402 }).Debug("Error getting task") 403 return err 404 } 405 406 return httputils.WriteJSON(w, http.StatusOK, task) 407 } 408 409 func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 410 if err := httputils.ParseForm(r); err != nil { 411 return err 412 } 413 filters, err := filters.FromJSON(r.Form.Get("filters")) 414 if err != nil { 415 return err 416 } 417 418 secrets, err := sr.backend.GetSecrets(basictypes.SecretListOptions{Filters: filters}) 419 if err != nil { 420 return err 421 } 422 423 return httputils.WriteJSON(w, http.StatusOK, secrets) 424 } 425 426 func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 427 var secret types.SecretSpec 428 if err := httputils.ReadJSON(r, &secret); err != nil { 429 return err 430 } 431 version := httputils.VersionFromContext(ctx) 432 if secret.Templating != nil && versions.LessThan(version, "1.37") { 433 return errdefs.InvalidParameter(errors.Errorf("secret templating is not supported on the specified API version: %s", version)) 434 } 435 436 id, err := sr.backend.CreateSecret(secret) 437 if err != nil { 438 return err 439 } 440 441 return httputils.WriteJSON(w, http.StatusCreated, &basictypes.SecretCreateResponse{ 442 ID: id, 443 }) 444 } 445 446 func (sr *swarmRouter) removeSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 447 if err := sr.backend.RemoveSecret(vars["id"]); err != nil { 448 return err 449 } 450 w.WriteHeader(http.StatusNoContent) 451 452 return nil 453 } 454 455 func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 456 secret, err := sr.backend.GetSecret(vars["id"]) 457 if err != nil { 458 return err 459 } 460 461 return httputils.WriteJSON(w, http.StatusOK, secret) 462 } 463 464 func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 465 var secret types.SecretSpec 466 if err := httputils.ReadJSON(r, &secret); err != nil { 467 return err 468 } 469 470 rawVersion := r.URL.Query().Get("version") 471 version, err := strconv.ParseUint(rawVersion, 10, 64) 472 if err != nil { 473 return errdefs.InvalidParameter(fmt.Errorf("invalid secret version")) 474 } 475 476 id := vars["id"] 477 return sr.backend.UpdateSecret(id, version, secret) 478 } 479 480 func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 481 if err := httputils.ParseForm(r); err != nil { 482 return err 483 } 484 filters, err := filters.FromJSON(r.Form.Get("filters")) 485 if err != nil { 486 return err 487 } 488 489 configs, err := sr.backend.GetConfigs(basictypes.ConfigListOptions{Filters: filters}) 490 if err != nil { 491 return err 492 } 493 494 return httputils.WriteJSON(w, http.StatusOK, configs) 495 } 496 497 func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 498 var config types.ConfigSpec 499 if err := httputils.ReadJSON(r, &config); err != nil { 500 return err 501 } 502 503 version := httputils.VersionFromContext(ctx) 504 if config.Templating != nil && versions.LessThan(version, "1.37") { 505 return errdefs.InvalidParameter(errors.Errorf("config templating is not supported on the specified API version: %s", version)) 506 } 507 508 id, err := sr.backend.CreateConfig(config) 509 if err != nil { 510 return err 511 } 512 513 return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ConfigCreateResponse{ 514 ID: id, 515 }) 516 } 517 518 func (sr *swarmRouter) removeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 519 if err := sr.backend.RemoveConfig(vars["id"]); err != nil { 520 return err 521 } 522 w.WriteHeader(http.StatusNoContent) 523 524 return nil 525 } 526 527 func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 528 config, err := sr.backend.GetConfig(vars["id"]) 529 if err != nil { 530 return err 531 } 532 533 return httputils.WriteJSON(w, http.StatusOK, config) 534 } 535 536 func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 537 var config types.ConfigSpec 538 if err := httputils.ReadJSON(r, &config); err != nil { 539 return err 540 } 541 542 rawVersion := r.URL.Query().Get("version") 543 version, err := strconv.ParseUint(rawVersion, 10, 64) 544 if err != nil { 545 return errdefs.InvalidParameter(fmt.Errorf("invalid config version")) 546 } 547 548 id := vars["id"] 549 return sr.backend.UpdateConfig(id, version, config) 550 }