github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/compat/containers.go (about) 1 package compat 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "strings" 9 "syscall" 10 "time" 11 12 "github.com/containers/podman/v2/libpod" 13 "github.com/containers/podman/v2/libpod/define" 14 "github.com/containers/podman/v2/pkg/api/handlers" 15 "github.com/containers/podman/v2/pkg/api/handlers/utils" 16 "github.com/containers/podman/v2/pkg/signal" 17 "github.com/docker/docker/api/types" 18 "github.com/docker/docker/api/types/container" 19 "github.com/docker/go-connections/nat" 20 "github.com/gorilla/schema" 21 "github.com/pkg/errors" 22 "github.com/sirupsen/logrus" 23 ) 24 25 func RemoveContainer(w http.ResponseWriter, r *http.Request) { 26 decoder := r.Context().Value("decoder").(*schema.Decoder) 27 query := struct { 28 Force bool `schema:"force"` 29 Vols bool `schema:"v"` 30 Link bool `schema:"link"` 31 }{ 32 // override any golang type defaults 33 } 34 35 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 36 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 37 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 38 return 39 } 40 41 if query.Link && !utils.IsLibpodRequest(r) { 42 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 43 utils.ErrLinkNotSupport) 44 return 45 } 46 47 runtime := r.Context().Value("runtime").(*libpod.Runtime) 48 name := utils.GetName(r) 49 con, err := runtime.LookupContainer(name) 50 if err != nil && errors.Cause(err) == define.ErrNoSuchCtr { 51 // Failed to get container. If force is specified, get the container's ID 52 // and evict it 53 if !query.Force { 54 utils.ContainerNotFound(w, name, err) 55 return 56 } 57 58 if _, err := runtime.EvictContainer(r.Context(), name, query.Vols); err != nil { 59 if errors.Cause(err) == define.ErrNoSuchCtr { 60 logrus.Debugf("Ignoring error (--allow-missing): %q", err) 61 w.WriteHeader(http.StatusNoContent) 62 return 63 } 64 logrus.Warn(errors.Wrapf(err, "failed to evict container: %q", name)) 65 utils.InternalServerError(w, err) 66 return 67 } 68 w.WriteHeader(http.StatusNoContent) 69 return 70 } 71 72 if err := runtime.RemoveContainer(r.Context(), con, query.Force, query.Vols); err != nil { 73 utils.InternalServerError(w, err) 74 return 75 } 76 utils.WriteResponse(w, http.StatusNoContent, "") 77 } 78 79 func ListContainers(w http.ResponseWriter, r *http.Request) { 80 var ( 81 containers []*libpod.Container 82 err error 83 ) 84 runtime := r.Context().Value("runtime").(*libpod.Runtime) 85 decoder := r.Context().Value("decoder").(*schema.Decoder) 86 query := struct { 87 All bool `schema:"all"` 88 Limit int `schema:"limit"` 89 Size bool `schema:"size"` 90 Filters map[string][]string `schema:"filters"` 91 }{ 92 // override any golang type defaults 93 } 94 95 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 96 utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 97 return 98 } 99 if query.All { 100 containers, err = runtime.GetAllContainers() 101 } else { 102 containers, err = runtime.GetRunningContainers() 103 } 104 if err != nil { 105 utils.InternalServerError(w, err) 106 return 107 } 108 if _, found := r.URL.Query()["limit"]; found && query.Limit > 0 { 109 last := query.Limit 110 if len(containers) > last { 111 containers = containers[len(containers)-last:] 112 } 113 } 114 // TODO filters still need to be applied 115 var list = make([]*handlers.Container, len(containers)) 116 for i, ctnr := range containers { 117 api, err := LibpodToContainer(ctnr, query.Size) 118 if err != nil { 119 utils.InternalServerError(w, err) 120 return 121 } 122 list[i] = api 123 } 124 utils.WriteResponse(w, http.StatusOK, list) 125 } 126 127 func GetContainer(w http.ResponseWriter, r *http.Request) { 128 runtime := r.Context().Value("runtime").(*libpod.Runtime) 129 decoder := r.Context().Value("decoder").(*schema.Decoder) 130 query := struct { 131 Size bool `schema:"size"` 132 }{ 133 // override any golang type defaults 134 } 135 136 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 137 utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 138 return 139 } 140 141 name := utils.GetName(r) 142 ctnr, err := runtime.LookupContainer(name) 143 if err != nil { 144 utils.ContainerNotFound(w, name, err) 145 return 146 } 147 api, err := LibpodToContainerJSON(ctnr, query.Size) 148 if err != nil { 149 utils.InternalServerError(w, err) 150 return 151 } 152 utils.WriteResponse(w, http.StatusOK, api) 153 } 154 155 func KillContainer(w http.ResponseWriter, r *http.Request) { 156 // /{version}/containers/(name)/kill 157 runtime := r.Context().Value("runtime").(*libpod.Runtime) 158 decoder := r.Context().Value("decoder").(*schema.Decoder) 159 query := struct { 160 Signal string `schema:"signal"` 161 }{ 162 Signal: "KILL", 163 } 164 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 165 utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 166 return 167 } 168 169 sig, err := signal.ParseSignalNameOrNumber(query.Signal) 170 if err != nil { 171 utils.InternalServerError(w, err) 172 return 173 } 174 name := utils.GetName(r) 175 con, err := runtime.LookupContainer(name) 176 if err != nil { 177 utils.ContainerNotFound(w, name, err) 178 return 179 } 180 181 state, err := con.State() 182 if err != nil { 183 utils.InternalServerError(w, err) 184 return 185 } 186 187 // If the Container is stopped already, send a 409 188 if state == define.ContainerStateStopped || state == define.ContainerStateExited { 189 utils.Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, errors.New(fmt.Sprintf("Cannot kill Container %s, it is not running", name))) 190 return 191 } 192 193 signal := uint(sig) 194 195 err = con.Kill(signal) 196 if err != nil { 197 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name)) 198 return 199 } 200 201 // Docker waits for the container to stop if the signal is 0 or 202 // SIGKILL. 203 if !utils.IsLibpodRequest(r) && (signal == 0 || syscall.Signal(signal) == syscall.SIGKILL) { 204 if _, err = con.Wait(); err != nil { 205 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to wait for Container %s", con.ID())) 206 return 207 } 208 } 209 // Success 210 utils.WriteResponse(w, http.StatusNoContent, "") 211 } 212 213 func WaitContainer(w http.ResponseWriter, r *http.Request) { 214 var msg string 215 // /{version}/containers/(name)/wait 216 exitCode, err := utils.WaitContainer(w, r) 217 if err != nil { 218 return 219 } 220 utils.WriteResponse(w, http.StatusOK, handlers.ContainerWaitOKBody{ 221 StatusCode: int(exitCode), 222 Error: struct { 223 Message string 224 }{ 225 Message: msg, 226 }, 227 }) 228 } 229 230 func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error) { 231 imageID, imageName := l.Image() 232 233 var ( 234 err error 235 sizeRootFs int64 236 sizeRW int64 237 state define.ContainerStatus 238 ) 239 240 if state, err = l.State(); err != nil { 241 return nil, err 242 } 243 stateStr := state.String() 244 if stateStr == "configured" { 245 stateStr = "created" 246 } 247 248 if sz { 249 if sizeRW, err = l.RWSize(); err != nil { 250 return nil, err 251 } 252 if sizeRootFs, err = l.RootFsSize(); err != nil { 253 return nil, err 254 } 255 } 256 257 return &handlers.Container{Container: types.Container{ 258 ID: l.ID(), 259 Names: []string{fmt.Sprintf("/%s", l.Name())}, 260 Image: imageName, 261 ImageID: imageID, 262 Command: strings.Join(l.Command(), " "), 263 Created: l.CreatedTime().Unix(), 264 Ports: nil, 265 SizeRw: sizeRW, 266 SizeRootFs: sizeRootFs, 267 Labels: l.Labels(), 268 State: stateStr, 269 Status: "", 270 HostConfig: struct { 271 NetworkMode string `json:",omitempty"` 272 }{ 273 "host"}, 274 NetworkSettings: nil, 275 Mounts: nil, 276 }, 277 ContainerCreateConfig: types.ContainerCreateConfig{}, 278 }, nil 279 } 280 281 func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) { 282 _, imageName := l.Image() 283 inspect, err := l.Inspect(sz) 284 if err != nil { 285 return nil, err 286 } 287 i, err := json.Marshal(inspect.State) 288 if err != nil { 289 return nil, err 290 } 291 state := types.ContainerState{} 292 if err := json.Unmarshal(i, &state); err != nil { 293 return nil, err 294 } 295 296 // docker considers paused to be running 297 if state.Paused { 298 state.Running = true 299 } 300 301 formatCapabilities(inspect.HostConfig.CapDrop) 302 formatCapabilities(inspect.HostConfig.CapAdd) 303 304 h, err := json.Marshal(inspect.HostConfig) 305 if err != nil { 306 return nil, err 307 } 308 hc := container.HostConfig{} 309 if err := json.Unmarshal(h, &hc); err != nil { 310 return nil, err 311 } 312 g, err := json.Marshal(inspect.GraphDriver) 313 if err != nil { 314 return nil, err 315 } 316 graphDriver := types.GraphDriverData{} 317 if err := json.Unmarshal(g, &graphDriver); err != nil { 318 return nil, err 319 } 320 321 cb := types.ContainerJSONBase{ 322 ID: l.ID(), 323 Created: l.CreatedTime().Format(time.RFC3339Nano), 324 Path: inspect.Path, 325 Args: inspect.Args, 326 State: &state, 327 Image: imageName, 328 ResolvConfPath: inspect.ResolvConfPath, 329 HostnamePath: inspect.HostnamePath, 330 HostsPath: inspect.HostsPath, 331 LogPath: l.LogPath(), 332 Node: nil, 333 Name: fmt.Sprintf("/%s", l.Name()), 334 RestartCount: int(inspect.RestartCount), 335 Driver: inspect.Driver, 336 Platform: "linux", 337 MountLabel: inspect.MountLabel, 338 ProcessLabel: inspect.ProcessLabel, 339 AppArmorProfile: inspect.AppArmorProfile, 340 ExecIDs: inspect.ExecIDs, 341 HostConfig: &hc, 342 GraphDriver: graphDriver, 343 SizeRw: inspect.SizeRw, 344 SizeRootFs: &inspect.SizeRootFs, 345 } 346 347 // set Path and Args 348 processArgs := l.Config().Spec.Process.Args 349 if len(processArgs) > 0 { 350 cb.Path = processArgs[0] 351 } 352 if len(processArgs) > 1 { 353 cb.Args = processArgs[1:] 354 } 355 stopTimeout := int(l.StopTimeout()) 356 357 exposedPorts := make(nat.PortSet) 358 for ep := range inspect.HostConfig.PortBindings { 359 splitp := strings.SplitN(ep, "/", 2) 360 if len(splitp) != 2 { 361 return nil, errors.Errorf("PORT/PROTOCOL Format required for %q", ep) 362 } 363 exposedPort, err := nat.NewPort(splitp[1], splitp[0]) 364 if err != nil { 365 return nil, err 366 } 367 exposedPorts[exposedPort] = struct{}{} 368 } 369 370 config := container.Config{ 371 Hostname: l.Hostname(), 372 Domainname: inspect.Config.DomainName, 373 User: l.User(), 374 AttachStdin: inspect.Config.AttachStdin, 375 AttachStdout: inspect.Config.AttachStdout, 376 AttachStderr: inspect.Config.AttachStderr, 377 ExposedPorts: exposedPorts, 378 Tty: inspect.Config.Tty, 379 OpenStdin: inspect.Config.OpenStdin, 380 StdinOnce: inspect.Config.StdinOnce, 381 Env: inspect.Config.Env, 382 Cmd: l.Command(), 383 Healthcheck: nil, 384 ArgsEscaped: false, 385 Image: imageName, 386 Volumes: nil, 387 WorkingDir: l.WorkingDir(), 388 Entrypoint: l.Entrypoint(), 389 NetworkDisabled: false, 390 MacAddress: "", 391 OnBuild: nil, 392 Labels: l.Labels(), 393 StopSignal: strconv.Itoa(int(l.StopSignal())), 394 StopTimeout: &stopTimeout, 395 Shell: nil, 396 } 397 398 m, err := json.Marshal(inspect.Mounts) 399 if err != nil { 400 return nil, err 401 } 402 mounts := []types.MountPoint{} 403 if err := json.Unmarshal(m, &mounts); err != nil { 404 return nil, err 405 } 406 407 p, err := json.Marshal(inspect.NetworkSettings.Ports) 408 if err != nil { 409 return nil, err 410 } 411 ports := nat.PortMap{} 412 if err := json.Unmarshal(p, &ports); err != nil { 413 return nil, err 414 } 415 416 n, err := json.Marshal(inspect.NetworkSettings) 417 if err != nil { 418 return nil, err 419 } 420 421 networkSettings := types.NetworkSettings{} 422 if err := json.Unmarshal(n, &networkSettings); err != nil { 423 return nil, err 424 } 425 426 c := types.ContainerJSON{ 427 ContainerJSONBase: &cb, 428 Mounts: mounts, 429 Config: &config, 430 NetworkSettings: &networkSettings, 431 } 432 return &c, nil 433 } 434 435 func formatCapabilities(slice []string) { 436 for i := range slice { 437 slice[i] = strings.TrimPrefix(slice[i], "CAP_") 438 } 439 }