github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/libpod/pods.go (about) 1 package libpod 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 9 "github.com/containers/podman/v2/libpod" 10 "github.com/containers/podman/v2/libpod/define" 11 "github.com/containers/podman/v2/pkg/api/handlers" 12 "github.com/containers/podman/v2/pkg/api/handlers/utils" 13 "github.com/containers/podman/v2/pkg/domain/entities" 14 "github.com/containers/podman/v2/pkg/domain/infra/abi" 15 "github.com/containers/podman/v2/pkg/specgen" 16 "github.com/containers/podman/v2/pkg/specgen/generate" 17 "github.com/containers/podman/v2/pkg/util" 18 "github.com/gorilla/schema" 19 "github.com/pkg/errors" 20 "github.com/sirupsen/logrus" 21 ) 22 23 func PodCreate(w http.ResponseWriter, r *http.Request) { 24 var ( 25 runtime = r.Context().Value("runtime").(*libpod.Runtime) 26 err error 27 ) 28 var psg specgen.PodSpecGenerator 29 if err := json.NewDecoder(r.Body).Decode(&psg); err != nil { 30 utils.Error(w, "failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) 31 return 32 } 33 pod, err := generate.MakePod(&psg, runtime) 34 if err != nil { 35 httpCode := http.StatusInternalServerError 36 if errors.Cause(err) == define.ErrPodExists { 37 httpCode = http.StatusConflict 38 } 39 utils.Error(w, "Something went wrong.", httpCode, err) 40 return 41 } 42 utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()}) 43 } 44 45 func Pods(w http.ResponseWriter, r *http.Request) { 46 decoder := r.Context().Value("decoder").(*schema.Decoder) 47 query := struct { 48 Filters map[string][]string `schema:"filters"` 49 }{ 50 // override any golang type defaults 51 } 52 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 53 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 54 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 55 return 56 } 57 58 pods, err := utils.GetPods(w, r) 59 if err != nil { 60 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 61 return 62 } 63 utils.WriteResponse(w, http.StatusOK, pods) 64 } 65 66 func PodInspect(w http.ResponseWriter, r *http.Request) { 67 runtime := r.Context().Value("runtime").(*libpod.Runtime) 68 name := utils.GetName(r) 69 pod, err := runtime.LookupPod(name) 70 if err != nil { 71 utils.PodNotFound(w, name, err) 72 return 73 } 74 podData, err := pod.Inspect() 75 if err != nil { 76 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 77 return 78 } 79 80 report := entities.PodInspectReport{ 81 InspectPodData: podData, 82 } 83 utils.WriteResponse(w, http.StatusOK, report) 84 } 85 86 func PodStop(w http.ResponseWriter, r *http.Request) { 87 var ( 88 stopError error 89 runtime = r.Context().Value("runtime").(*libpod.Runtime) 90 decoder = r.Context().Value("decoder").(*schema.Decoder) 91 responses map[string]error 92 ) 93 query := struct { 94 Timeout int `schema:"t"` 95 }{ 96 // override any golang type defaults 97 } 98 99 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 100 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 101 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 102 return 103 } 104 name := utils.GetName(r) 105 pod, err := runtime.LookupPod(name) 106 if err != nil { 107 utils.PodNotFound(w, name, err) 108 return 109 } 110 111 status, err := pod.GetPodStatus() 112 if err != nil { 113 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 114 return 115 } 116 if status != define.PodStateRunning { 117 utils.WriteResponse(w, http.StatusNotModified, "") 118 return 119 } 120 121 if query.Timeout > 0 { 122 responses, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout) 123 } else { 124 responses, stopError = pod.Stop(r.Context(), false) 125 } 126 if stopError != nil && errors.Cause(stopError) != define.ErrPodPartialFail { 127 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 128 return 129 } 130 // Try to clean up the pod - but only warn on failure, it's nonfatal. 131 if cleanupCtrs, cleanupErr := pod.Cleanup(r.Context()); cleanupErr != nil { 132 logrus.Errorf("Error cleaning up pod %s: %v", pod.ID(), cleanupErr) 133 for id, err := range cleanupCtrs { 134 logrus.Errorf("Error cleaning up pod %s container %s: %v", pod.ID(), id, err) 135 } 136 } 137 var errs []error //nolint 138 for id, err := range responses { 139 errs = append(errs, errors.Wrapf(err, "error stopping container %s", id)) 140 } 141 report := entities.PodStopReport{ 142 Errs: errs, 143 Id: pod.ID(), 144 } 145 utils.WriteResponse(w, http.StatusOK, report) 146 } 147 148 func PodStart(w http.ResponseWriter, r *http.Request) { 149 var errs []error //nolint 150 runtime := r.Context().Value("runtime").(*libpod.Runtime) 151 name := utils.GetName(r) 152 pod, err := runtime.LookupPod(name) 153 if err != nil { 154 utils.PodNotFound(w, name, err) 155 return 156 } 157 status, err := pod.GetPodStatus() 158 if err != nil { 159 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 160 return 161 } 162 if status == define.PodStateRunning { 163 utils.WriteResponse(w, http.StatusNotModified, "") 164 return 165 } 166 responses, err := pod.Start(r.Context()) 167 if err != nil && errors.Cause(err) != define.ErrPodPartialFail { 168 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 169 return 170 } 171 for id, err := range responses { 172 errs = append(errs, errors.Wrapf(err, "error starting container %s", id)) 173 } 174 report := entities.PodStartReport{ 175 Errs: errs, 176 Id: pod.ID(), 177 } 178 utils.WriteResponse(w, http.StatusOK, report) 179 } 180 181 func PodDelete(w http.ResponseWriter, r *http.Request) { 182 var ( 183 runtime = r.Context().Value("runtime").(*libpod.Runtime) 184 decoder = r.Context().Value("decoder").(*schema.Decoder) 185 ) 186 query := struct { 187 Force bool `schema:"force"` 188 }{ 189 // override any golang type defaults 190 } 191 192 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 193 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 194 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 195 return 196 } 197 name := utils.GetName(r) 198 pod, err := runtime.LookupPod(name) 199 if err != nil { 200 utils.PodNotFound(w, name, err) 201 return 202 } 203 if err := runtime.RemovePod(r.Context(), pod, true, query.Force); err != nil { 204 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 205 return 206 } 207 report := entities.PodRmReport{ 208 Id: pod.ID(), 209 } 210 utils.WriteResponse(w, http.StatusOK, report) 211 } 212 213 func PodRestart(w http.ResponseWriter, r *http.Request) { 214 var errs []error //nolint 215 runtime := r.Context().Value("runtime").(*libpod.Runtime) 216 name := utils.GetName(r) 217 pod, err := runtime.LookupPod(name) 218 if err != nil { 219 utils.PodNotFound(w, name, err) 220 return 221 } 222 responses, err := pod.Restart(r.Context()) 223 if err != nil && errors.Cause(err) != define.ErrPodPartialFail { 224 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 225 return 226 } 227 for id, err := range responses { 228 errs = append(errs, errors.Wrapf(err, "error restarting container %s", id)) 229 } 230 report := entities.PodRestartReport{ 231 Errs: errs, 232 Id: pod.ID(), 233 } 234 utils.WriteResponse(w, http.StatusOK, report) 235 } 236 237 func PodPrune(w http.ResponseWriter, r *http.Request) { 238 reports, err := PodPruneHelper(w, r) 239 if err != nil { 240 utils.InternalServerError(w, err) 241 return 242 } 243 utils.WriteResponse(w, http.StatusOK, reports) 244 } 245 246 func PodPruneHelper(w http.ResponseWriter, r *http.Request) ([]*entities.PodPruneReport, error) { 247 var ( 248 runtime = r.Context().Value("runtime").(*libpod.Runtime) 249 ) 250 responses, err := runtime.PrunePods(r.Context()) 251 if err != nil { 252 return nil, err 253 } 254 reports := make([]*entities.PodPruneReport, 0, len(responses)) 255 for k, v := range responses { 256 reports = append(reports, &entities.PodPruneReport{ 257 Err: v, 258 Id: k, 259 }) 260 } 261 return reports, nil 262 } 263 264 func PodPause(w http.ResponseWriter, r *http.Request) { 265 var errs []error //nolint 266 runtime := r.Context().Value("runtime").(*libpod.Runtime) 267 name := utils.GetName(r) 268 pod, err := runtime.LookupPod(name) 269 if err != nil { 270 utils.PodNotFound(w, name, err) 271 return 272 } 273 responses, err := pod.Pause(r.Context()) 274 if err != nil && errors.Cause(err) != define.ErrPodPartialFail { 275 utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) 276 return 277 } 278 for id, v := range responses { 279 errs = append(errs, errors.Wrapf(v, "error pausing container %s", id)) 280 } 281 report := entities.PodPauseReport{ 282 Errs: errs, 283 Id: pod.ID(), 284 } 285 utils.WriteResponse(w, http.StatusOK, report) 286 } 287 288 func PodUnpause(w http.ResponseWriter, r *http.Request) { 289 var errs []error //nolint 290 runtime := r.Context().Value("runtime").(*libpod.Runtime) 291 name := utils.GetName(r) 292 pod, err := runtime.LookupPod(name) 293 if err != nil { 294 utils.PodNotFound(w, name, err) 295 return 296 } 297 responses, err := pod.Unpause(r.Context()) 298 if err != nil && errors.Cause(err) != define.ErrPodPartialFail { 299 utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err) 300 return 301 } 302 for id, v := range responses { 303 errs = append(errs, errors.Wrapf(v, "error unpausing container %s", id)) 304 } 305 report := entities.PodUnpauseReport{ 306 Errs: errs, 307 Id: pod.ID(), 308 } 309 utils.WriteResponse(w, http.StatusOK, &report) 310 } 311 312 func PodTop(w http.ResponseWriter, r *http.Request) { 313 runtime := r.Context().Value("runtime").(*libpod.Runtime) 314 decoder := r.Context().Value("decoder").(*schema.Decoder) 315 316 query := struct { 317 PsArgs string `schema:"ps_args"` 318 }{ 319 PsArgs: "", 320 } 321 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 322 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 323 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 324 return 325 } 326 327 name := utils.GetName(r) 328 pod, err := runtime.LookupPod(name) 329 if err != nil { 330 utils.PodNotFound(w, name, err) 331 return 332 } 333 334 args := []string{} 335 if query.PsArgs != "" { 336 args = append(args, query.PsArgs) 337 } 338 output, err := pod.GetPodPidInformation(args) 339 if err != nil { 340 utils.InternalServerError(w, err) 341 return 342 } 343 344 var body = handlers.PodTopOKBody{} 345 if len(output) > 0 { 346 body.Titles = strings.Split(output[0], "\t") 347 for _, line := range output[1:] { 348 body.Processes = append(body.Processes, strings.Split(line, "\t")) 349 } 350 } 351 utils.WriteJSON(w, http.StatusOK, body) 352 } 353 354 func PodKill(w http.ResponseWriter, r *http.Request) { 355 var ( 356 runtime = r.Context().Value("runtime").(*libpod.Runtime) 357 decoder = r.Context().Value("decoder").(*schema.Decoder) 358 signal = "SIGKILL" 359 errs []error //nolint 360 ) 361 query := struct { 362 Signal string `schema:"signal"` 363 }{ 364 // override any golang type defaults 365 } 366 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 367 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 368 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 369 return 370 } 371 if _, found := r.URL.Query()["signal"]; found { 372 signal = query.Signal 373 } 374 375 sig, err := util.ParseSignal(signal) 376 if err != nil { 377 utils.InternalServerError(w, errors.Wrapf(err, "unable to parse signal value")) 378 return 379 } 380 name := utils.GetName(r) 381 pod, err := runtime.LookupPod(name) 382 if err != nil { 383 utils.PodNotFound(w, name, err) 384 return 385 } 386 logrus.Debugf("Killing pod %s with signal %d", pod.ID(), sig) 387 podStates, err := pod.Status() 388 if err != nil { 389 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) 390 return 391 } 392 hasRunning := false 393 for _, s := range podStates { 394 if s == define.ContainerStateRunning { 395 hasRunning = true 396 break 397 } 398 } 399 if !hasRunning { 400 msg := fmt.Sprintf("Container %s is not running", pod.ID()) 401 utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID())) 402 return 403 } 404 405 responses, err := pod.Kill(r.Context(), uint(sig)) 406 if err != nil && errors.Cause(err) != define.ErrPodPartialFail { 407 utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err) 408 return 409 } 410 411 for _, v := range responses { 412 if v != nil { 413 errs = append(errs, v) 414 } 415 } 416 report := &entities.PodKillReport{ 417 Errs: errs, 418 Id: pod.ID(), 419 } 420 utils.WriteResponse(w, http.StatusOK, report) 421 } 422 423 func PodExists(w http.ResponseWriter, r *http.Request) { 424 runtime := r.Context().Value("runtime").(*libpod.Runtime) 425 name := utils.GetName(r) 426 _, err := runtime.LookupPod(name) 427 if err != nil { 428 utils.PodNotFound(w, name, err) 429 return 430 } 431 utils.WriteResponse(w, http.StatusNoContent, "") 432 } 433 434 func PodStats(w http.ResponseWriter, r *http.Request) { 435 runtime := r.Context().Value("runtime").(*libpod.Runtime) 436 decoder := r.Context().Value("decoder").(*schema.Decoder) 437 438 query := struct { 439 NamesOrIDs []string `schema:"namesOrIDs"` 440 All bool `schema:"all"` 441 }{ 442 // default would go here 443 } 444 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 445 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 446 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 447 return 448 } 449 450 // Validate input. 451 options := entities.PodStatsOptions{All: query.All} 452 if err := entities.ValidatePodStatsOptions(query.NamesOrIDs, &options); err != nil { 453 utils.InternalServerError(w, err) 454 } 455 456 // Collect the stats and send them over the wire. 457 containerEngine := abi.ContainerEngine{Libpod: runtime} 458 reports, err := containerEngine.PodStats(r.Context(), query.NamesOrIDs, options) 459 460 // Error checks as documented in swagger. 461 switch errors.Cause(err) { 462 case define.ErrNoSuchPod: 463 utils.Error(w, "one or more pods not found", http.StatusNotFound, err) 464 return 465 case nil: 466 // Nothing to do. 467 default: 468 utils.InternalServerError(w, err) 469 return 470 } 471 472 utils.WriteResponse(w, http.StatusOK, reports) 473 }