github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/api/handlers/compat/containers.go (about) 1 package compat 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "sort" 8 "strconv" 9 "strings" 10 "syscall" 11 "time" 12 13 "github.com/hanks177/podman/v4/libpod" 14 "github.com/hanks177/podman/v4/libpod/define" 15 "github.com/hanks177/podman/v4/pkg/api/handlers" 16 "github.com/hanks177/podman/v4/pkg/api/handlers/utils" 17 api "github.com/hanks177/podman/v4/pkg/api/types" 18 "github.com/hanks177/podman/v4/pkg/domain/entities" 19 "github.com/hanks177/podman/v4/pkg/domain/filters" 20 "github.com/hanks177/podman/v4/pkg/domain/infra/abi" 21 "github.com/hanks177/podman/v4/pkg/ps" 22 "github.com/hanks177/podman/v4/pkg/signal" 23 "github.com/hanks177/podman/v4/pkg/util" 24 "github.com/docker/docker/api/types" 25 "github.com/docker/docker/api/types/container" 26 "github.com/docker/docker/api/types/network" 27 "github.com/docker/go-connections/nat" 28 "github.com/docker/go-units" 29 "github.com/gorilla/schema" 30 "github.com/pkg/errors" 31 "github.com/sirupsen/logrus" 32 ) 33 34 func RemoveContainer(w http.ResponseWriter, r *http.Request) { 35 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 36 query := struct { 37 Force bool `schema:"force"` 38 Ignore bool `schema:"ignore"` 39 Depend bool `schema:"depend"` 40 Link bool `schema:"link"` 41 Timeout *uint `schema:"timeout"` 42 DockerVolumes bool `schema:"v"` 43 LibpodVolumes bool `schema:"volumes"` 44 }{ 45 // override any golang type defaults 46 } 47 48 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 49 utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 50 return 51 } 52 53 options := entities.RmOptions{ 54 Force: query.Force, 55 Ignore: query.Ignore, 56 } 57 if utils.IsLibpodRequest(r) { 58 options.Volumes = query.LibpodVolumes 59 options.Timeout = query.Timeout 60 options.Depend = query.Depend 61 } else { 62 if query.Link { 63 utils.Error(w, http.StatusBadRequest, utils.ErrLinkNotSupport) 64 return 65 } 66 options.Volumes = query.DockerVolumes 67 } 68 69 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 70 // Now use the ABI implementation to prevent us from having duplicate 71 // code. 72 containerEngine := abi.ContainerEngine{Libpod: runtime} 73 name := utils.GetName(r) 74 reports, err := containerEngine.ContainerRm(r.Context(), []string{name}, options) 75 if err != nil { 76 if errors.Cause(err) == define.ErrNoSuchCtr { 77 utils.ContainerNotFound(w, name, err) 78 return 79 } 80 81 utils.InternalServerError(w, err) 82 return 83 } 84 if len(reports) > 0 && reports[0].Err != nil { 85 err = reports[0].Err 86 if errors.Cause(err) == define.ErrNoSuchCtr { 87 utils.ContainerNotFound(w, name, err) 88 return 89 } 90 utils.InternalServerError(w, err) 91 return 92 } 93 if utils.IsLibpodRequest(r) { 94 utils.WriteResponse(w, http.StatusOK, reports) 95 return 96 } 97 utils.WriteResponse(w, http.StatusNoContent, nil) 98 } 99 100 func ListContainers(w http.ResponseWriter, r *http.Request) { 101 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 102 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 103 query := struct { 104 All bool `schema:"all"` 105 Limit int `schema:"limit"` 106 Size bool `schema:"size"` 107 }{ 108 // override any golang type defaults 109 } 110 111 filterMap, err := util.PrepareFilters(r) 112 if err != nil { 113 utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to decode filter parameters for %s", r.URL.String())) 114 return 115 } 116 117 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 118 utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 119 return 120 } 121 122 filterFuncs := make([]libpod.ContainerFilter, 0, len(*filterMap)) 123 all := query.All || query.Limit > 0 124 if len((*filterMap)) > 0 { 125 for k, v := range *filterMap { 126 generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) 127 if err != nil { 128 utils.InternalServerError(w, err) 129 return 130 } 131 filterFuncs = append(filterFuncs, generatedFunc) 132 } 133 } 134 135 // Docker thinks that if status is given as an input, then we should override 136 // the all setting and always deal with all containers. 137 if len((*filterMap)["status"]) > 0 { 138 all = true 139 } 140 if !all { 141 runningOnly, err := filters.GenerateContainerFilterFuncs("status", []string{define.ContainerStateRunning.String()}, runtime) 142 if err != nil { 143 utils.InternalServerError(w, err) 144 return 145 } 146 filterFuncs = append(filterFuncs, runningOnly) 147 } 148 149 containers, err := runtime.GetContainers(filterFuncs...) 150 if err != nil { 151 utils.InternalServerError(w, err) 152 return 153 } 154 if _, found := r.URL.Query()["limit"]; found && query.Limit > 0 { 155 // Sort the libpod containers 156 sort.Sort(ps.SortCreateTime{SortContainers: containers}) 157 // we should perform the lopping before we start getting 158 // the expensive information on containers 159 if len(containers) > query.Limit { 160 containers = containers[:query.Limit] 161 } 162 } 163 list := make([]*handlers.Container, 0, len(containers)) 164 for _, ctnr := range containers { 165 api, err := LibpodToContainer(ctnr, query.Size) 166 if err != nil { 167 if errors.Cause(err) == define.ErrNoSuchCtr { 168 // container was removed between the initial fetch of the list and conversion 169 logrus.Debugf("Container %s removed between initial fetch and conversion, ignoring in output", ctnr.ID()) 170 continue 171 } 172 utils.InternalServerError(w, err) 173 return 174 } 175 list = append(list, api) 176 } 177 utils.WriteResponse(w, http.StatusOK, list) 178 } 179 180 func GetContainer(w http.ResponseWriter, r *http.Request) { 181 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 182 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 183 query := struct { 184 Size bool `schema:"size"` 185 }{ 186 // override any golang type defaults 187 } 188 189 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 190 utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 191 return 192 } 193 194 name := utils.GetName(r) 195 ctnr, err := runtime.LookupContainer(name) 196 if err != nil { 197 utils.ContainerNotFound(w, name, err) 198 return 199 } 200 api, err := LibpodToContainerJSON(ctnr, query.Size) 201 if err != nil { 202 utils.InternalServerError(w, err) 203 return 204 } 205 utils.WriteResponse(w, http.StatusOK, api) 206 } 207 208 func KillContainer(w http.ResponseWriter, r *http.Request) { 209 // /{version}/containers/(name)/kill 210 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 211 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 212 query := struct { 213 Signal string `schema:"signal"` 214 }{ 215 Signal: "KILL", 216 } 217 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 218 utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 219 return 220 } 221 222 // Now use the ABI implementation to prevent us from having duplicate 223 // code. 224 containerEngine := abi.ContainerEngine{Libpod: runtime} 225 name := utils.GetName(r) 226 options := entities.KillOptions{ 227 Signal: query.Signal, 228 } 229 report, err := containerEngine.ContainerKill(r.Context(), []string{name}, options) 230 if err != nil { 231 if errors.Cause(err) == define.ErrCtrStateInvalid || 232 errors.Cause(err) == define.ErrCtrStopped { 233 utils.Error(w, http.StatusConflict, err) 234 return 235 } 236 if errors.Cause(err) == define.ErrNoSuchCtr { 237 utils.ContainerNotFound(w, name, err) 238 return 239 } 240 241 utils.InternalServerError(w, err) 242 return 243 } 244 245 if len(report) > 0 && report[0].Err != nil { 246 utils.InternalServerError(w, report[0].Err) 247 return 248 } 249 // Docker waits for the container to stop if the signal is 0 or 250 // SIGKILL. 251 if !utils.IsLibpodRequest(r) { 252 sig, err := signal.ParseSignalNameOrNumber(query.Signal) 253 if err != nil { 254 utils.InternalServerError(w, err) 255 return 256 } 257 if sig == 0 || sig == syscall.SIGKILL { 258 opts := entities.WaitOptions{ 259 Condition: []define.ContainerStatus{define.ContainerStateExited, define.ContainerStateStopped}, 260 Interval: time.Millisecond * 250, 261 } 262 if _, err := containerEngine.ContainerWait(r.Context(), []string{name}, opts); err != nil { 263 utils.Error(w, http.StatusInternalServerError, err) 264 return 265 } 266 } 267 } 268 // Success 269 utils.WriteResponse(w, http.StatusNoContent, nil) 270 } 271 272 func WaitContainer(w http.ResponseWriter, r *http.Request) { 273 // /{version}/containers/(name)/wait 274 utils.WaitContainerDocker(w, r) 275 } 276 277 func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error) { 278 imageID, imageName := l.Image() 279 280 var ( 281 err error 282 sizeRootFs int64 283 sizeRW int64 284 state define.ContainerStatus 285 status string 286 ) 287 288 if state, err = l.State(); err != nil { 289 return nil, err 290 } 291 stateStr := state.String() 292 if stateStr == "configured" { 293 stateStr = "created" 294 } 295 296 switch state { 297 case define.ContainerStateConfigured, define.ContainerStateCreated: 298 status = "Created" 299 case define.ContainerStateStopped, define.ContainerStateExited: 300 exitCode, _, err := l.ExitCode() 301 if err != nil { 302 return nil, err 303 } 304 finishedTime, err := l.FinishedTime() 305 if err != nil { 306 return nil, err 307 } 308 status = fmt.Sprintf("Exited (%d) %s ago", exitCode, units.HumanDuration(time.Since(finishedTime))) 309 case define.ContainerStateRunning, define.ContainerStatePaused: 310 startedTime, err := l.StartedTime() 311 if err != nil { 312 return nil, err 313 } 314 status = fmt.Sprintf("Up %s", units.HumanDuration(time.Since(startedTime))) 315 if state == define.ContainerStatePaused { 316 status += " (Paused)" 317 } 318 case define.ContainerStateRemoving: 319 status = "Removal In Progress" 320 case define.ContainerStateStopping: 321 status = "Stopping" 322 default: 323 status = "Unknown" 324 } 325 326 if sz { 327 if sizeRW, err = l.RWSize(); err != nil { 328 return nil, err 329 } 330 if sizeRootFs, err = l.RootFsSize(); err != nil { 331 return nil, err 332 } 333 } 334 335 portMappings, err := l.PortMappings() 336 if err != nil { 337 return nil, err 338 } 339 340 ports := make([]types.Port, len(portMappings)) 341 for idx, portMapping := range portMappings { 342 ports[idx] = types.Port{ 343 IP: portMapping.HostIP, 344 PrivatePort: portMapping.ContainerPort, 345 PublicPort: portMapping.HostPort, 346 Type: portMapping.Protocol, 347 } 348 } 349 inspect, err := l.Inspect(false) 350 if err != nil { 351 return nil, err 352 } 353 354 n, err := json.Marshal(inspect.NetworkSettings) 355 if err != nil { 356 return nil, err 357 } 358 networkSettings := types.SummaryNetworkSettings{} 359 if err := json.Unmarshal(n, &networkSettings); err != nil { 360 return nil, err 361 } 362 363 m, err := json.Marshal(inspect.Mounts) 364 if err != nil { 365 return nil, err 366 } 367 mounts := []types.MountPoint{} 368 if err := json.Unmarshal(m, &mounts); err != nil { 369 return nil, err 370 } 371 372 return &handlers.Container{ 373 Container: types.Container{ 374 ID: l.ID(), 375 Names: []string{fmt.Sprintf("/%s", l.Name())}, 376 Image: imageName, 377 ImageID: "sha256:" + imageID, 378 Command: strings.Join(l.Command(), " "), 379 Created: l.CreatedTime().Unix(), 380 Ports: ports, 381 SizeRw: sizeRW, 382 SizeRootFs: sizeRootFs, 383 Labels: l.Labels(), 384 State: stateStr, 385 Status: status, 386 HostConfig: struct { 387 NetworkMode string `json:",omitempty"` 388 }{ 389 "host", 390 }, 391 NetworkSettings: &networkSettings, 392 Mounts: mounts, 393 }, 394 ContainerCreateConfig: types.ContainerCreateConfig{}, 395 }, nil 396 } 397 398 func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) { 399 _, imageName := l.Image() 400 inspect, err := l.Inspect(sz) 401 if err != nil { 402 return nil, err 403 } 404 // Docker uses UTC 405 if inspect != nil && inspect.State != nil { 406 inspect.State.StartedAt = inspect.State.StartedAt.UTC() 407 inspect.State.FinishedAt = inspect.State.FinishedAt.UTC() 408 } 409 i, err := json.Marshal(inspect.State) 410 if err != nil { 411 return nil, err 412 } 413 state := types.ContainerState{} 414 if err := json.Unmarshal(i, &state); err != nil { 415 return nil, err 416 } 417 418 // docker considers paused to be running 419 if state.Paused { 420 state.Running = true 421 } 422 423 // docker calls the configured state "created" 424 if state.Status == define.ContainerStateConfigured.String() { 425 state.Status = define.ContainerStateCreated.String() 426 } 427 428 if l.HasHealthCheck() && state.Status != "created" { 429 state.Health = &types.Health{ 430 Status: inspect.State.Health.Status, 431 FailingStreak: inspect.State.Health.FailingStreak, 432 } 433 434 log := inspect.State.Health.Log 435 436 for _, item := range log { 437 res := &types.HealthcheckResult{} 438 s, _ := time.Parse(time.RFC3339Nano, item.Start) 439 e, _ := time.Parse(time.RFC3339Nano, item.End) 440 res.Start = s 441 res.End = e 442 res.ExitCode = item.ExitCode 443 res.Output = item.Output 444 state.Health.Log = append(state.Health.Log, res) 445 } 446 } 447 448 formatCapabilities(inspect.HostConfig.CapDrop) 449 formatCapabilities(inspect.HostConfig.CapAdd) 450 451 h, err := json.Marshal(inspect.HostConfig) 452 if err != nil { 453 return nil, err 454 } 455 hc := container.HostConfig{} 456 if err := json.Unmarshal(h, &hc); err != nil { 457 return nil, err 458 } 459 460 // k8s-file == json-file 461 if hc.LogConfig.Type == define.KubernetesLogging { 462 hc.LogConfig.Type = define.JSONLogging 463 } 464 g, err := json.Marshal(inspect.GraphDriver) 465 if err != nil { 466 return nil, err 467 } 468 graphDriver := types.GraphDriverData{} 469 if err := json.Unmarshal(g, &graphDriver); err != nil { 470 return nil, err 471 } 472 473 cb := types.ContainerJSONBase{ 474 ID: l.ID(), 475 Created: l.CreatedTime().UTC().Format(time.RFC3339Nano), // Docker uses UTC 476 Path: inspect.Path, 477 Args: inspect.Args, 478 State: &state, 479 Image: imageName, 480 ResolvConfPath: inspect.ResolvConfPath, 481 HostnamePath: inspect.HostnamePath, 482 HostsPath: inspect.HostsPath, 483 LogPath: l.LogPath(), 484 Node: nil, 485 Name: fmt.Sprintf("/%s", l.Name()), 486 RestartCount: int(inspect.RestartCount), 487 Driver: inspect.Driver, 488 Platform: "linux", 489 MountLabel: inspect.MountLabel, 490 ProcessLabel: inspect.ProcessLabel, 491 AppArmorProfile: inspect.AppArmorProfile, 492 ExecIDs: inspect.ExecIDs, 493 HostConfig: &hc, 494 GraphDriver: graphDriver, 495 SizeRw: inspect.SizeRw, 496 SizeRootFs: &inspect.SizeRootFs, 497 } 498 499 // set Path and Args 500 processArgs := l.Config().Spec.Process.Args 501 if len(processArgs) > 0 { 502 cb.Path = processArgs[0] 503 } 504 if len(processArgs) > 1 { 505 cb.Args = processArgs[1:] 506 } 507 stopTimeout := int(l.StopTimeout()) 508 509 exposedPorts := make(nat.PortSet) 510 for ep := range inspect.HostConfig.PortBindings { 511 splitp := strings.SplitN(ep, "/", 2) 512 if len(splitp) != 2 { 513 return nil, errors.Errorf("PORT/PROTOCOL Format required for %q", ep) 514 } 515 exposedPort, err := nat.NewPort(splitp[1], splitp[0]) 516 if err != nil { 517 return nil, err 518 } 519 exposedPorts[exposedPort] = struct{}{} 520 } 521 522 var healthcheck *container.HealthConfig 523 if inspect.Config.Healthcheck != nil { 524 healthcheck = &container.HealthConfig{ 525 Test: inspect.Config.Healthcheck.Test, 526 Interval: inspect.Config.Healthcheck.Interval, 527 Timeout: inspect.Config.Healthcheck.Timeout, 528 StartPeriod: inspect.Config.Healthcheck.StartPeriod, 529 Retries: inspect.Config.Healthcheck.Retries, 530 } 531 } 532 533 config := container.Config{ 534 Hostname: l.Hostname(), 535 Domainname: inspect.Config.DomainName, 536 User: l.User(), 537 AttachStdin: inspect.Config.AttachStdin, 538 AttachStdout: inspect.Config.AttachStdout, 539 AttachStderr: inspect.Config.AttachStderr, 540 ExposedPorts: exposedPorts, 541 Tty: inspect.Config.Tty, 542 OpenStdin: inspect.Config.OpenStdin, 543 StdinOnce: inspect.Config.StdinOnce, 544 Env: inspect.Config.Env, 545 Cmd: l.Command(), 546 Healthcheck: healthcheck, 547 ArgsEscaped: false, 548 Image: imageName, 549 Volumes: nil, 550 WorkingDir: l.WorkingDir(), 551 Entrypoint: l.Entrypoint(), 552 NetworkDisabled: false, 553 MacAddress: "", 554 OnBuild: nil, 555 Labels: l.Labels(), 556 StopSignal: strconv.Itoa(int(l.StopSignal())), 557 StopTimeout: &stopTimeout, 558 Shell: nil, 559 } 560 561 m, err := json.Marshal(inspect.Mounts) 562 if err != nil { 563 return nil, err 564 } 565 mounts := []types.MountPoint{} 566 if err := json.Unmarshal(m, &mounts); err != nil { 567 return nil, err 568 } 569 570 p, err := json.Marshal(inspect.NetworkSettings.Ports) 571 if err != nil { 572 return nil, err 573 } 574 ports := nat.PortMap{} 575 if err := json.Unmarshal(p, &ports); err != nil { 576 return nil, err 577 } 578 579 n, err := json.Marshal(inspect.NetworkSettings) 580 if err != nil { 581 return nil, err 582 } 583 584 networkSettings := types.NetworkSettings{} 585 if err := json.Unmarshal(n, &networkSettings); err != nil { 586 return nil, err 587 } 588 // do not report null instead use an empty map 589 if networkSettings.Networks == nil { 590 networkSettings.Networks = map[string]*network.EndpointSettings{} 591 } 592 593 c := types.ContainerJSON{ 594 ContainerJSONBase: &cb, 595 Mounts: mounts, 596 Config: &config, 597 NetworkSettings: &networkSettings, 598 } 599 return &c, nil 600 } 601 602 func formatCapabilities(slice []string) { 603 for i := range slice { 604 slice[i] = strings.TrimPrefix(slice[i], "CAP_") 605 } 606 } 607 608 func RenameContainer(w http.ResponseWriter, r *http.Request) { 609 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 610 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 611 612 name := utils.GetName(r) 613 query := struct { 614 Name string `schema:"name"` 615 }{} 616 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 617 utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 618 return 619 } 620 621 ctr, err := runtime.LookupContainer(name) 622 if err != nil { 623 utils.ContainerNotFound(w, name, err) 624 return 625 } 626 627 if _, err := runtime.RenameContainer(r.Context(), ctr, query.Name); err != nil { 628 if errors.Cause(err) == define.ErrPodExists || errors.Cause(err) == define.ErrCtrExists { 629 utils.Error(w, http.StatusConflict, err) 630 return 631 } 632 utils.InternalServerError(w, err) 633 return 634 } 635 636 utils.WriteResponse(w, http.StatusNoContent, nil) 637 }