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