github.com/rish1988/moby@v25.0.2+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/containerd/log" 10 "github.com/docker/docker/api/server/httputils" 11 basictypes "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/backend" 13 "github.com/docker/docker/api/types/filters" 14 "github.com/docker/docker/api/types/registry" 15 types "github.com/docker/docker/api/types/swarm" 16 "github.com/docker/docker/api/types/versions" 17 "github.com/docker/docker/errdefs" 18 "github.com/pkg/errors" 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 log.G(ctx).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 log.G(ctx).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 log.G(ctx).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 log.G(ctx).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 log.G(ctx).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 log.G(ctx).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 log.G(ctx).WithContext(ctx).WithFields(log.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 // TODO(thaJeztah): remove logentries check and migration code in release v26.0.0. 213 if service.TaskTemplate.LogDriver != nil && service.TaskTemplate.LogDriver.Name == "logentries" { 214 return errdefs.InvalidParameter(errors.New("the logentries logging driver has been deprecated and removed")) 215 } 216 217 // Get returns "" if the header does not exist 218 encodedAuth := r.Header.Get(registry.AuthHeader) 219 queryRegistry := false 220 if v := httputils.VersionFromContext(ctx); v != "" { 221 if versions.LessThan(v, "1.30") { 222 queryRegistry = true 223 } 224 adjustForAPIVersion(v, &service) 225 } 226 227 version := httputils.VersionFromContext(ctx) 228 if versions.LessThan(version, "1.44") { 229 if service.TaskTemplate.ContainerSpec != nil && service.TaskTemplate.ContainerSpec.Healthcheck != nil { 230 // StartInterval was added in API 1.44 231 service.TaskTemplate.ContainerSpec.Healthcheck.StartInterval = 0 232 } 233 } 234 235 resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry) 236 if err != nil { 237 log.G(ctx).WithFields(log.Fields{ 238 "error": err, 239 "service-name": service.Name, 240 }).Debug("Error creating service") 241 return err 242 } 243 244 return httputils.WriteJSON(w, http.StatusCreated, resp) 245 } 246 247 func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 248 var service types.ServiceSpec 249 if err := httputils.ReadJSON(r, &service); err != nil { 250 return err 251 } 252 // TODO(thaJeztah): remove logentries check and migration code in release v26.0.0. 253 if service.TaskTemplate.LogDriver != nil && service.TaskTemplate.LogDriver.Name == "logentries" { 254 return errdefs.InvalidParameter(errors.New("the logentries logging driver has been deprecated and removed")) 255 } 256 257 rawVersion := r.URL.Query().Get("version") 258 version, err := strconv.ParseUint(rawVersion, 10, 64) 259 if err != nil { 260 err := fmt.Errorf("invalid service version '%s': %v", rawVersion, err) 261 return errdefs.InvalidParameter(err) 262 } 263 264 var flags basictypes.ServiceUpdateOptions 265 266 // Get returns "" if the header does not exist 267 flags.EncodedRegistryAuth = r.Header.Get(registry.AuthHeader) 268 flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom") 269 flags.Rollback = r.URL.Query().Get("rollback") 270 queryRegistry := false 271 if v := httputils.VersionFromContext(ctx); v != "" { 272 if versions.LessThan(v, "1.30") { 273 queryRegistry = true 274 } 275 adjustForAPIVersion(v, &service) 276 } 277 278 resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry) 279 if err != nil { 280 log.G(ctx).WithContext(ctx).WithFields(log.Fields{ 281 "error": err, 282 "service-id": vars["id"], 283 }).Debug("Error updating service") 284 return err 285 } 286 return httputils.WriteJSON(w, http.StatusOK, resp) 287 } 288 289 func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 290 if err := sr.backend.RemoveService(vars["id"]); err != nil { 291 log.G(ctx).WithContext(ctx).WithFields(log.Fields{ 292 "error": err, 293 "service-id": vars["id"], 294 }).Debug("Error removing service") 295 return err 296 } 297 return nil 298 } 299 300 func (sr *swarmRouter) getTaskLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 301 if err := httputils.ParseForm(r); err != nil { 302 return err 303 } 304 305 // make a selector to pass to the helper function 306 selector := &backend.LogSelector{ 307 Tasks: []string{vars["id"]}, 308 } 309 return sr.swarmLogs(ctx, w, r, selector) 310 } 311 312 func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 313 if err := httputils.ParseForm(r); err != nil { 314 return err 315 } 316 317 // make a selector to pass to the helper function 318 selector := &backend.LogSelector{ 319 Services: []string{vars["id"]}, 320 } 321 return sr.swarmLogs(ctx, w, r, selector) 322 } 323 324 func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 325 if err := httputils.ParseForm(r); err != nil { 326 return err 327 } 328 filter, err := filters.FromJSON(r.Form.Get("filters")) 329 if err != nil { 330 return err 331 } 332 333 nodes, err := sr.backend.GetNodes(basictypes.NodeListOptions{Filters: filter}) 334 if err != nil { 335 log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting nodes") 336 return err 337 } 338 339 return httputils.WriteJSON(w, http.StatusOK, nodes) 340 } 341 342 func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 343 node, err := sr.backend.GetNode(vars["id"]) 344 if err != nil { 345 log.G(ctx).WithContext(ctx).WithFields(log.Fields{ 346 "error": err, 347 "node-id": vars["id"], 348 }).Debug("Error getting node") 349 return err 350 } 351 352 return httputils.WriteJSON(w, http.StatusOK, node) 353 } 354 355 func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 356 var node types.NodeSpec 357 if err := httputils.ReadJSON(r, &node); err != nil { 358 return err 359 } 360 361 rawVersion := r.URL.Query().Get("version") 362 version, err := strconv.ParseUint(rawVersion, 10, 64) 363 if err != nil { 364 err := fmt.Errorf("invalid node version '%s': %v", rawVersion, err) 365 return errdefs.InvalidParameter(err) 366 } 367 368 if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil { 369 log.G(ctx).WithContext(ctx).WithFields(log.Fields{ 370 "error": err, 371 "node-id": vars["id"], 372 }).Debug("Error updating node") 373 return err 374 } 375 return nil 376 } 377 378 func (sr *swarmRouter) removeNode(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 383 force := httputils.BoolValue(r, "force") 384 385 if err := sr.backend.RemoveNode(vars["id"], force); err != nil { 386 log.G(ctx).WithContext(ctx).WithFields(log.Fields{ 387 "error": err, 388 "node-id": vars["id"], 389 }).Debug("Error removing node") 390 return err 391 } 392 return nil 393 } 394 395 func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 396 if err := httputils.ParseForm(r); err != nil { 397 return err 398 } 399 filter, err := filters.FromJSON(r.Form.Get("filters")) 400 if err != nil { 401 return err 402 } 403 404 tasks, err := sr.backend.GetTasks(basictypes.TaskListOptions{Filters: filter}) 405 if err != nil { 406 log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting tasks") 407 return err 408 } 409 410 return httputils.WriteJSON(w, http.StatusOK, tasks) 411 } 412 413 func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 414 task, err := sr.backend.GetTask(vars["id"]) 415 if err != nil { 416 log.G(ctx).WithContext(ctx).WithFields(log.Fields{ 417 "error": err, 418 "task-id": vars["id"], 419 }).Debug("Error getting task") 420 return err 421 } 422 423 return httputils.WriteJSON(w, http.StatusOK, task) 424 } 425 426 func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 427 if err := httputils.ParseForm(r); err != nil { 428 return err 429 } 430 filters, err := filters.FromJSON(r.Form.Get("filters")) 431 if err != nil { 432 return err 433 } 434 435 secrets, err := sr.backend.GetSecrets(basictypes.SecretListOptions{Filters: filters}) 436 if err != nil { 437 return err 438 } 439 440 return httputils.WriteJSON(w, http.StatusOK, secrets) 441 } 442 443 func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 444 var secret types.SecretSpec 445 if err := httputils.ReadJSON(r, &secret); err != nil { 446 return err 447 } 448 version := httputils.VersionFromContext(ctx) 449 if secret.Templating != nil && versions.LessThan(version, "1.37") { 450 return errdefs.InvalidParameter(errors.Errorf("secret templating is not supported on the specified API version: %s", version)) 451 } 452 453 id, err := sr.backend.CreateSecret(secret) 454 if err != nil { 455 return err 456 } 457 458 return httputils.WriteJSON(w, http.StatusCreated, &basictypes.SecretCreateResponse{ 459 ID: id, 460 }) 461 } 462 463 func (sr *swarmRouter) removeSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 464 if err := sr.backend.RemoveSecret(vars["id"]); err != nil { 465 return err 466 } 467 w.WriteHeader(http.StatusNoContent) 468 469 return nil 470 } 471 472 func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 473 secret, err := sr.backend.GetSecret(vars["id"]) 474 if err != nil { 475 return err 476 } 477 478 return httputils.WriteJSON(w, http.StatusOK, secret) 479 } 480 481 func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 482 var secret types.SecretSpec 483 if err := httputils.ReadJSON(r, &secret); err != nil { 484 return err 485 } 486 487 rawVersion := r.URL.Query().Get("version") 488 version, err := strconv.ParseUint(rawVersion, 10, 64) 489 if err != nil { 490 return errdefs.InvalidParameter(fmt.Errorf("invalid secret version")) 491 } 492 493 id := vars["id"] 494 return sr.backend.UpdateSecret(id, version, secret) 495 } 496 497 func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 498 if err := httputils.ParseForm(r); err != nil { 499 return err 500 } 501 filters, err := filters.FromJSON(r.Form.Get("filters")) 502 if err != nil { 503 return err 504 } 505 506 configs, err := sr.backend.GetConfigs(basictypes.ConfigListOptions{Filters: filters}) 507 if err != nil { 508 return err 509 } 510 511 return httputils.WriteJSON(w, http.StatusOK, configs) 512 } 513 514 func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 515 var config types.ConfigSpec 516 if err := httputils.ReadJSON(r, &config); err != nil { 517 return err 518 } 519 520 version := httputils.VersionFromContext(ctx) 521 if config.Templating != nil && versions.LessThan(version, "1.37") { 522 return errdefs.InvalidParameter(errors.Errorf("config templating is not supported on the specified API version: %s", version)) 523 } 524 525 id, err := sr.backend.CreateConfig(config) 526 if err != nil { 527 return err 528 } 529 530 return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ConfigCreateResponse{ 531 ID: id, 532 }) 533 } 534 535 func (sr *swarmRouter) removeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 536 if err := sr.backend.RemoveConfig(vars["id"]); err != nil { 537 return err 538 } 539 w.WriteHeader(http.StatusNoContent) 540 541 return nil 542 } 543 544 func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 545 config, err := sr.backend.GetConfig(vars["id"]) 546 if err != nil { 547 return err 548 } 549 550 return httputils.WriteJSON(w, http.StatusOK, config) 551 } 552 553 func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 554 var config types.ConfigSpec 555 if err := httputils.ReadJSON(r, &config); err != nil { 556 return err 557 } 558 559 rawVersion := r.URL.Query().Get("version") 560 version, err := strconv.ParseUint(rawVersion, 10, 64) 561 if err != nil { 562 return errdefs.InvalidParameter(fmt.Errorf("invalid config version")) 563 } 564 565 id := vars["id"] 566 return sr.backend.UpdateConfig(id, version, config) 567 }