github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/libpod/containers.go (about) 1 package libpod 2 3 import ( 4 "io/ioutil" 5 "net/http" 6 "os" 7 "strconv" 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/compat" 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/ps" 16 "github.com/gorilla/schema" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 ) 20 21 func ContainerExists(w http.ResponseWriter, r *http.Request) { 22 decoder := r.Context().Value("decoder").(*schema.Decoder) 23 runtime := r.Context().Value("runtime").(*libpod.Runtime) 24 // Now use the ABI implementation to prevent us from having duplicate 25 // code. 26 containerEngine := abi.ContainerEngine{Libpod: runtime} 27 28 name := utils.GetName(r) 29 query := struct { 30 External bool `schema:"external"` 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 options := entities.ContainerExistsOptions{ 42 External: query.External, 43 } 44 45 report, err := containerEngine.ContainerExists(r.Context(), name, options) 46 if err != nil { 47 if errors.Cause(err) == define.ErrNoSuchCtr { 48 utils.ContainerNotFound(w, name, err) 49 return 50 } 51 utils.InternalServerError(w, err) 52 return 53 54 } 55 if report.Value { 56 utils.WriteResponse(w, http.StatusNoContent, "") 57 } else { 58 utils.ContainerNotFound(w, name, define.ErrNoSuchCtr) 59 } 60 } 61 62 func ListContainers(w http.ResponseWriter, r *http.Request) { 63 decoder := r.Context().Value("decoder").(*schema.Decoder) 64 query := struct { 65 All bool `schema:"all"` 66 Filters map[string][]string `schema:"filters"` 67 Last int `schema:"last"` // alias for limit 68 Limit int `schema:"limit"` 69 Namespace bool `schema:"namespace"` 70 Size bool `schema:"size"` 71 Sync bool `schema:"sync"` 72 }{ 73 // override any golang type defaults 74 } 75 76 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 77 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 78 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 79 return 80 } 81 82 limit := query.Limit 83 // Support `last` as an alias for `limit`. While Podman uses --last in 84 // the CLI, the API is using `limit`. As we first used `last` in the 85 // API as well, we decided to go with aliasing to prevent any 86 // regression. See github.com/containers/podman/issues/6413. 87 if _, found := r.URL.Query()["last"]; found { 88 logrus.Info("List containers: received `last` parameter - overwriting `limit`") 89 limit = query.Last 90 } 91 92 runtime := r.Context().Value("runtime").(*libpod.Runtime) 93 opts := entities.ContainerListOptions{ 94 All: query.All, 95 Filters: query.Filters, 96 Last: limit, 97 Size: query.Size, 98 Sort: "", 99 Namespace: query.Namespace, 100 Pod: true, 101 Sync: query.Sync, 102 } 103 pss, err := ps.GetContainerLists(runtime, opts) 104 if err != nil { 105 utils.InternalServerError(w, err) 106 return 107 } 108 if len(pss) == 0 { 109 utils.WriteResponse(w, http.StatusOK, "[]") 110 return 111 } 112 utils.WriteResponse(w, http.StatusOK, pss) 113 } 114 115 func GetContainer(w http.ResponseWriter, r *http.Request) { 116 decoder := r.Context().Value("decoder").(*schema.Decoder) 117 query := struct { 118 Size bool `schema:"size"` 119 }{ 120 // override any golang type defaults 121 } 122 123 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 124 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 125 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 126 return 127 } 128 runtime := r.Context().Value("runtime").(*libpod.Runtime) 129 name := utils.GetName(r) 130 container, err := runtime.LookupContainer(name) 131 if err != nil { 132 utils.ContainerNotFound(w, name, err) 133 return 134 } 135 data, err := container.Inspect(query.Size) 136 if err != nil { 137 utils.InternalServerError(w, err) 138 return 139 } 140 utils.WriteResponse(w, http.StatusOK, data) 141 } 142 143 func WaitContainer(w http.ResponseWriter, r *http.Request) { 144 exitCode, err := utils.WaitContainer(w, r) 145 if err != nil { 146 return 147 } 148 utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode))) 149 } 150 151 func UnmountContainer(w http.ResponseWriter, r *http.Request) { 152 runtime := r.Context().Value("runtime").(*libpod.Runtime) 153 name := utils.GetName(r) 154 conn, err := runtime.LookupContainer(name) 155 if err != nil { 156 utils.ContainerNotFound(w, name, err) 157 return 158 } 159 // TODO In future it might be an improvement that libpod unmount return a 160 // "container not mounted" error so we can surface that to the endpoint user 161 if err := conn.Unmount(false); err != nil { 162 utils.InternalServerError(w, err) 163 } 164 utils.WriteResponse(w, http.StatusNoContent, "") 165 166 } 167 func MountContainer(w http.ResponseWriter, r *http.Request) { 168 runtime := r.Context().Value("runtime").(*libpod.Runtime) 169 name := utils.GetName(r) 170 conn, err := runtime.LookupContainer(name) 171 if err != nil { 172 utils.ContainerNotFound(w, name, err) 173 return 174 } 175 m, err := conn.Mount() 176 if err != nil { 177 utils.InternalServerError(w, err) 178 } 179 utils.WriteResponse(w, http.StatusOK, m) 180 } 181 182 func ShowMountedContainers(w http.ResponseWriter, r *http.Request) { 183 response := make(map[string]string) 184 runtime := r.Context().Value("runtime").(*libpod.Runtime) 185 conns, err := runtime.GetAllContainers() 186 if err != nil { 187 utils.InternalServerError(w, err) 188 } 189 for _, conn := range conns { 190 mounted, mountPoint, err := conn.Mounted() 191 if err != nil { 192 utils.InternalServerError(w, err) 193 } 194 if !mounted { 195 continue 196 } 197 response[conn.ID()] = mountPoint 198 } 199 utils.WriteResponse(w, http.StatusOK, response) 200 } 201 202 func Checkpoint(w http.ResponseWriter, r *http.Request) { 203 var targetFile string 204 decoder := r.Context().Value("decoder").(*schema.Decoder) 205 query := struct { 206 Keep bool `schema:"keep"` 207 LeaveRunning bool `schema:"leaveRunning"` 208 TCPEstablished bool `schema:"tcpEstablished"` 209 Export bool `schema:"export"` 210 IgnoreRootFS bool `schema:"ignoreRootFS"` 211 }{ 212 // override any golang type defaults 213 } 214 215 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 216 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 217 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 218 return 219 } 220 name := utils.GetName(r) 221 runtime := r.Context().Value("runtime").(*libpod.Runtime) 222 ctr, err := runtime.LookupContainer(name) 223 if err != nil { 224 utils.ContainerNotFound(w, name, err) 225 return 226 } 227 if query.Export { 228 tmpFile, err := ioutil.TempFile("", "checkpoint") 229 if err != nil { 230 utils.InternalServerError(w, err) 231 return 232 } 233 defer os.Remove(tmpFile.Name()) 234 if err := tmpFile.Close(); err != nil { 235 utils.InternalServerError(w, err) 236 return 237 } 238 targetFile = tmpFile.Name() 239 } 240 options := libpod.ContainerCheckpointOptions{ 241 Keep: query.Keep, 242 KeepRunning: query.LeaveRunning, 243 TCPEstablished: query.TCPEstablished, 244 IgnoreRootfs: query.IgnoreRootFS, 245 } 246 if query.Export { 247 options.TargetFile = targetFile 248 } 249 err = ctr.Checkpoint(r.Context(), options) 250 if err != nil { 251 utils.InternalServerError(w, err) 252 return 253 } 254 if query.Export { 255 f, err := os.Open(targetFile) 256 if err != nil { 257 utils.InternalServerError(w, err) 258 return 259 } 260 defer f.Close() 261 utils.WriteResponse(w, http.StatusOK, f) 262 return 263 } 264 utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()}) 265 } 266 267 func Restore(w http.ResponseWriter, r *http.Request) { 268 var ( 269 targetFile string 270 ) 271 decoder := r.Context().Value("decoder").(*schema.Decoder) 272 query := struct { 273 Keep bool `schema:"keep"` 274 TCPEstablished bool `schema:"tcpEstablished"` 275 Import bool `schema:"import"` 276 Name string `schema:"name"` 277 IgnoreRootFS bool `schema:"ignoreRootFS"` 278 IgnoreStaticIP bool `schema:"ignoreStaticIP"` 279 IgnoreStaticMAC bool `schema:"ignoreStaticMAC"` 280 }{ 281 // override any golang type defaults 282 } 283 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 284 utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, 285 errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 286 return 287 } 288 name := utils.GetName(r) 289 runtime := r.Context().Value("runtime").(*libpod.Runtime) 290 ctr, err := runtime.LookupContainer(name) 291 if err != nil { 292 utils.ContainerNotFound(w, name, err) 293 return 294 } 295 if query.Import { 296 t, err := ioutil.TempFile("", "restore") 297 if err != nil { 298 utils.InternalServerError(w, err) 299 return 300 } 301 defer t.Close() 302 if err := compat.SaveFromBody(t, r); err != nil { 303 utils.InternalServerError(w, err) 304 return 305 } 306 targetFile = t.Name() 307 } 308 309 options := libpod.ContainerCheckpointOptions{ 310 Keep: query.Keep, 311 TCPEstablished: query.TCPEstablished, 312 IgnoreRootfs: query.IgnoreRootFS, 313 IgnoreStaticIP: query.IgnoreStaticIP, 314 IgnoreStaticMAC: query.IgnoreStaticMAC, 315 } 316 if query.Import { 317 options.TargetFile = targetFile 318 options.Name = query.Name 319 } 320 err = ctr.Restore(r.Context(), options) 321 if err != nil { 322 utils.InternalServerError(w, err) 323 return 324 } 325 utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()}) 326 } 327 328 func InitContainer(w http.ResponseWriter, r *http.Request) { 329 name := utils.GetName(r) 330 runtime := r.Context().Value("runtime").(*libpod.Runtime) 331 ctr, err := runtime.LookupContainer(name) 332 if err != nil { 333 utils.ContainerNotFound(w, name, err) 334 return 335 } 336 err = ctr.Init(r.Context(), ctr.PodID() != "") 337 if errors.Cause(err) == define.ErrCtrStateInvalid { 338 utils.Error(w, "container already initialized", http.StatusNotModified, err) 339 return 340 } 341 if err != nil { 342 utils.InternalServerError(w, err) 343 return 344 } 345 utils.WriteResponse(w, http.StatusNoContent, "") 346 } 347 348 func ShouldRestart(w http.ResponseWriter, r *http.Request) { 349 runtime := r.Context().Value("runtime").(*libpod.Runtime) 350 // Now use the ABI implementation to prevent us from having duplicate 351 // code. 352 containerEngine := abi.ContainerEngine{Libpod: runtime} 353 354 name := utils.GetName(r) 355 report, err := containerEngine.ShouldRestart(r.Context(), name) 356 if err != nil { 357 if errors.Cause(err) == define.ErrNoSuchCtr { 358 utils.ContainerNotFound(w, name, err) 359 return 360 } 361 utils.InternalServerError(w, err) 362 return 363 364 } 365 if report.Value { 366 utils.WriteResponse(w, http.StatusNoContent, "") 367 } else { 368 utils.ContainerNotFound(w, name, define.ErrNoSuchCtr) 369 } 370 }