github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/daemon/list.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/container" 11 "github.com/docker/docker/image" 12 "github.com/docker/docker/volume" 13 "github.com/docker/engine-api/types" 14 "github.com/docker/engine-api/types/filters" 15 networktypes "github.com/docker/engine-api/types/network" 16 "github.com/docker/go-connections/nat" 17 ) 18 19 var acceptedVolumeFilterTags = map[string]bool{ 20 "dangling": true, 21 "name": true, 22 "driver": true, 23 } 24 25 var acceptedPsFilterTags = map[string]bool{ 26 "ancestor": true, 27 "before": true, 28 "exited": true, 29 "id": true, 30 "isolation": true, 31 "label": true, 32 "name": true, 33 "status": true, 34 "since": true, 35 "volume": true, 36 } 37 38 // iterationAction represents possible outcomes happening during the container iteration. 39 type iterationAction int 40 41 // containerReducer represents a reducer for a container. 42 // Returns the object to serialize by the api. 43 type containerReducer func(*container.Container, *listContext) (*types.Container, error) 44 45 const ( 46 // includeContainer is the action to include a container in the reducer. 47 includeContainer iterationAction = iota 48 // excludeContainer is the action to exclude a container in the reducer. 49 excludeContainer 50 // stopIteration is the action to stop iterating over the list of containers. 51 stopIteration 52 ) 53 54 // errStopIteration makes the iterator to stop without returning an error. 55 var errStopIteration = errors.New("container list iteration stopped") 56 57 // List returns an array of all containers registered in the daemon. 58 func (daemon *Daemon) List() []*container.Container { 59 return daemon.containers.List() 60 } 61 62 // listContext is the daemon generated filtering to iterate over containers. 63 // This is created based on the user specification from types.ContainerListOptions. 64 type listContext struct { 65 // idx is the container iteration index for this context 66 idx int 67 // ancestorFilter tells whether it should check ancestors or not 68 ancestorFilter bool 69 // names is a list of container names to filter with 70 names map[string][]string 71 // images is a list of images to filter with 72 images map[image.ID]bool 73 // filters is a collection of arguments to filter with, specified by the user 74 filters filters.Args 75 // exitAllowed is a list of exit codes allowed to filter with 76 exitAllowed []int 77 78 // FIXME Remove this for 1.12 as --since and --before are deprecated 79 // beforeContainer is a filter to ignore containers that appear before the one given 80 beforeContainer *container.Container 81 // sinceContainer is a filter to stop the filtering when the iterator arrive to the given container 82 sinceContainer *container.Container 83 84 // beforeFilter is a filter to ignore containers that appear before the one given 85 // this is used for --filter=before= and --before=, the latter is deprecated. 86 beforeFilter *container.Container 87 // sinceFilter is a filter to stop the filtering when the iterator arrive to the given container 88 // this is used for --filter=since= and --since=, the latter is deprecated. 89 sinceFilter *container.Container 90 // ContainerListOptions is the filters set by the user 91 *types.ContainerListOptions 92 } 93 94 // Containers returns the list of containers to show given the user's filtering. 95 func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) { 96 return daemon.reduceContainers(config, daemon.transformContainer) 97 } 98 99 // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer. 100 func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) { 101 containers := []*types.Container{} 102 103 ctx, err := daemon.foldFilter(config) 104 if err != nil { 105 return nil, err 106 } 107 108 for _, container := range daemon.List() { 109 t, err := daemon.reducePsContainer(container, ctx, reducer) 110 if err != nil { 111 if err != errStopIteration { 112 return nil, err 113 } 114 break 115 } 116 if t != nil { 117 containers = append(containers, t) 118 ctx.idx++ 119 } 120 } 121 return containers, nil 122 } 123 124 // reducePsContainer is the basic representation for a container as expected by the ps command. 125 func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) { 126 container.Lock() 127 defer container.Unlock() 128 129 // filter containers to return 130 action := includeContainerInList(container, ctx) 131 switch action { 132 case excludeContainer: 133 return nil, nil 134 case stopIteration: 135 return nil, errStopIteration 136 } 137 138 // transform internal container struct into api structs 139 return reducer(container, ctx) 140 } 141 142 // foldFilter generates the container filter based on the user's filtering options. 143 func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listContext, error) { 144 psFilters := config.Filter 145 146 if err := psFilters.Validate(acceptedPsFilterTags); err != nil { 147 return nil, err 148 } 149 150 var filtExited []int 151 152 err := psFilters.WalkValues("exited", func(value string) error { 153 code, err := strconv.Atoi(value) 154 if err != nil { 155 return err 156 } 157 filtExited = append(filtExited, code) 158 return nil 159 }) 160 if err != nil { 161 return nil, err 162 } 163 164 err = psFilters.WalkValues("status", func(value string) error { 165 if !container.IsValidStateString(value) { 166 return fmt.Errorf("Unrecognised filter value for status: %s", value) 167 } 168 169 config.All = true 170 return nil 171 }) 172 if err != nil { 173 return nil, err 174 } 175 176 var beforeContFilter, sinceContFilter *container.Container 177 // FIXME remove this for 1.12 as --since and --before are deprecated 178 var beforeContainer, sinceContainer *container.Container 179 180 err = psFilters.WalkValues("before", func(value string) error { 181 beforeContFilter, err = daemon.GetContainer(value) 182 return err 183 }) 184 if err != nil { 185 return nil, err 186 } 187 188 err = psFilters.WalkValues("since", func(value string) error { 189 sinceContFilter, err = daemon.GetContainer(value) 190 return err 191 }) 192 if err != nil { 193 return nil, err 194 } 195 196 imagesFilter := map[image.ID]bool{} 197 var ancestorFilter bool 198 if psFilters.Include("ancestor") { 199 ancestorFilter = true 200 psFilters.WalkValues("ancestor", func(ancestor string) error { 201 id, err := daemon.GetImageID(ancestor) 202 if err != nil { 203 logrus.Warnf("Error while looking up for image %v", ancestor) 204 return nil 205 } 206 if imagesFilter[id] { 207 // Already seen this ancestor, skip it 208 return nil 209 } 210 // Then walk down the graph and put the imageIds in imagesFilter 211 populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children) 212 return nil 213 }) 214 } 215 216 // FIXME remove this for 1.12 as --since and --before are deprecated 217 if config.Before != "" { 218 beforeContainer, err = daemon.GetContainer(config.Before) 219 if err != nil { 220 return nil, err 221 } 222 } 223 224 // FIXME remove this for 1.12 as --since and --before are deprecated 225 if config.Since != "" { 226 sinceContainer, err = daemon.GetContainer(config.Since) 227 if err != nil { 228 return nil, err 229 } 230 } 231 232 return &listContext{ 233 filters: psFilters, 234 ancestorFilter: ancestorFilter, 235 images: imagesFilter, 236 exitAllowed: filtExited, 237 beforeContainer: beforeContainer, 238 sinceContainer: sinceContainer, 239 beforeFilter: beforeContFilter, 240 sinceFilter: sinceContFilter, 241 ContainerListOptions: config, 242 names: daemon.nameIndex.GetAll(), 243 }, nil 244 } 245 246 // includeContainerInList decides whether a container should be included in the output or not based in the filter. 247 // It also decides if the iteration should be stopped or not. 248 func includeContainerInList(container *container.Container, ctx *listContext) iterationAction { 249 // Do not include container if it's in the list before the filter container. 250 // Set the filter container to nil to include the rest of containers after this one. 251 if ctx.beforeFilter != nil { 252 if container.ID == ctx.beforeFilter.ID { 253 ctx.beforeFilter = nil 254 } 255 return excludeContainer 256 } 257 258 // Stop iteration when the container arrives to the filter container 259 if ctx.sinceFilter != nil { 260 if container.ID == ctx.sinceFilter.ID { 261 return stopIteration 262 } 263 } 264 265 // Do not include container if it's stopped and we're not filters 266 // FIXME remove the ctx.beforContainer and ctx.sinceContainer part of the condition for 1.12 as --since and --before are deprecated 267 if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeContainer == nil && ctx.sinceContainer == nil { 268 return excludeContainer 269 } 270 271 // Do not include container if the name doesn't match 272 if !ctx.filters.Match("name", container.Name) { 273 return excludeContainer 274 } 275 276 // Do not include container if the id doesn't match 277 if !ctx.filters.Match("id", container.ID) { 278 return excludeContainer 279 } 280 281 // Do not include container if any of the labels don't match 282 if !ctx.filters.MatchKVList("label", container.Config.Labels) { 283 return excludeContainer 284 } 285 286 // Do not include container if isolation doesn't match 287 if excludeContainer == excludeByIsolation(container, ctx) { 288 return excludeContainer 289 } 290 291 // FIXME remove this for 1.12 as --since and --before are deprecated 292 if ctx.beforeContainer != nil { 293 if container.ID == ctx.beforeContainer.ID { 294 ctx.beforeContainer = nil 295 } 296 return excludeContainer 297 } 298 299 // FIXME remove this for 1.12 as --since and --before are deprecated 300 if ctx.sinceContainer != nil { 301 if container.ID == ctx.sinceContainer.ID { 302 return stopIteration 303 } 304 } 305 306 // Stop iteration when the index is over the limit 307 if ctx.Limit > 0 && ctx.idx == ctx.Limit { 308 return stopIteration 309 } 310 311 // Do not include container if its exit code is not in the filter 312 if len(ctx.exitAllowed) > 0 { 313 shouldSkip := true 314 for _, code := range ctx.exitAllowed { 315 if code == container.ExitCode && !container.Running { 316 shouldSkip = false 317 break 318 } 319 } 320 if shouldSkip { 321 return excludeContainer 322 } 323 } 324 325 // Do not include container if its status doesn't match the filter 326 if !ctx.filters.Match("status", container.State.StateString()) { 327 return excludeContainer 328 } 329 330 if ctx.filters.Include("volume") { 331 volumesByName := make(map[string]*volume.MountPoint) 332 for _, m := range container.MountPoints { 333 if m.Name != "" { 334 volumesByName[m.Name] = m 335 } else { 336 volumesByName[m.Source] = m 337 } 338 } 339 340 volumeExist := fmt.Errorf("volume mounted in container") 341 err := ctx.filters.WalkValues("volume", func(value string) error { 342 if _, exist := container.MountPoints[value]; exist { 343 return volumeExist 344 } 345 if _, exist := volumesByName[value]; exist { 346 return volumeExist 347 } 348 return nil 349 }) 350 if err != volumeExist { 351 return excludeContainer 352 } 353 } 354 355 if ctx.ancestorFilter { 356 if len(ctx.images) == 0 { 357 return excludeContainer 358 } 359 if !ctx.images[container.ImageID] { 360 return excludeContainer 361 } 362 } 363 364 return includeContainer 365 } 366 367 // transformContainer generates the container type expected by the docker ps command. 368 func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) { 369 newC := &types.Container{ 370 ID: container.ID, 371 Names: ctx.names[container.ID], 372 ImageID: container.ImageID.String(), 373 } 374 if newC.Names == nil { 375 // Dead containers will often have no name, so make sure the response isn't null 376 newC.Names = []string{} 377 } 378 379 image := container.Config.Image // if possible keep the original ref 380 if image != container.ImageID.String() { 381 id, err := daemon.GetImageID(image) 382 if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE { 383 return nil, err 384 } 385 if err != nil || id != container.ImageID { 386 image = container.ImageID.String() 387 } 388 } 389 newC.Image = image 390 391 if len(container.Args) > 0 { 392 args := []string{} 393 for _, arg := range container.Args { 394 if strings.Contains(arg, " ") { 395 args = append(args, fmt.Sprintf("'%s'", arg)) 396 } else { 397 args = append(args, arg) 398 } 399 } 400 argsAsString := strings.Join(args, " ") 401 402 newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString) 403 } else { 404 newC.Command = container.Path 405 } 406 newC.Created = container.Created.Unix() 407 newC.State = container.State.StateString() 408 newC.Status = container.State.String() 409 newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode) 410 // copy networks to avoid races 411 networks := make(map[string]*networktypes.EndpointSettings) 412 for name, network := range container.NetworkSettings.Networks { 413 if network == nil { 414 continue 415 } 416 networks[name] = &networktypes.EndpointSettings{ 417 EndpointID: network.EndpointID, 418 Gateway: network.Gateway, 419 IPAddress: network.IPAddress, 420 IPPrefixLen: network.IPPrefixLen, 421 IPv6Gateway: network.IPv6Gateway, 422 GlobalIPv6Address: network.GlobalIPv6Address, 423 GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen, 424 MacAddress: network.MacAddress, 425 } 426 if network.IPAMConfig != nil { 427 networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{ 428 IPv4Address: network.IPAMConfig.IPv4Address, 429 IPv6Address: network.IPAMConfig.IPv6Address, 430 } 431 } 432 } 433 newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks} 434 435 newC.Ports = []types.Port{} 436 for port, bindings := range container.NetworkSettings.Ports { 437 p, err := nat.ParsePort(port.Port()) 438 if err != nil { 439 return nil, err 440 } 441 if len(bindings) == 0 { 442 newC.Ports = append(newC.Ports, types.Port{ 443 PrivatePort: p, 444 Type: port.Proto(), 445 }) 446 continue 447 } 448 for _, binding := range bindings { 449 h, err := nat.ParsePort(binding.HostPort) 450 if err != nil { 451 return nil, err 452 } 453 newC.Ports = append(newC.Ports, types.Port{ 454 PrivatePort: p, 455 PublicPort: h, 456 Type: port.Proto(), 457 IP: binding.HostIP, 458 }) 459 } 460 } 461 462 if ctx.Size { 463 sizeRw, sizeRootFs := daemon.getSize(container) 464 newC.SizeRw = sizeRw 465 newC.SizeRootFs = sizeRootFs 466 } 467 newC.Labels = container.Config.Labels 468 newC.Mounts = addMountPoints(container) 469 470 return newC, nil 471 } 472 473 // Volumes lists known volumes, using the filter to restrict the range 474 // of volumes returned. 475 func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) { 476 var ( 477 volumesOut []*types.Volume 478 ) 479 volFilters, err := filters.FromParam(filter) 480 if err != nil { 481 return nil, nil, err 482 } 483 484 if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil { 485 return nil, nil, err 486 } 487 488 volumes, warnings, err := daemon.volumes.List() 489 filterVolumes, err := daemon.filterVolumes(volumes, volFilters) 490 if err != nil { 491 return nil, nil, err 492 } 493 for _, v := range filterVolumes { 494 volumesOut = append(volumesOut, volumeToAPIType(v)) 495 } 496 return volumesOut, warnings, nil 497 } 498 499 // filterVolumes filters volume list according to user specified filter 500 // and returns user chosen volumes 501 func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) { 502 // if filter is empty, return original volume list 503 if filter.Len() == 0 { 504 return vols, nil 505 } 506 507 var retVols []volume.Volume 508 for _, vol := range vols { 509 if filter.Include("name") { 510 if !filter.Match("name", vol.Name()) { 511 continue 512 } 513 } 514 if filter.Include("driver") { 515 if !filter.Match("driver", vol.DriverName()) { 516 continue 517 } 518 } 519 retVols = append(retVols, vol) 520 } 521 danglingOnly := false 522 if filter.Include("dangling") { 523 if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") { 524 danglingOnly = true 525 } else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") { 526 return nil, fmt.Errorf("Invalid filter 'dangling=%s'", filter.Get("dangling")) 527 } 528 retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly) 529 } 530 return retVols, nil 531 } 532 533 func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) { 534 if !ancestorMap[imageID] { 535 for _, id := range getChildren(imageID) { 536 populateImageFilterByParents(ancestorMap, id, getChildren) 537 } 538 ancestorMap[imageID] = true 539 } 540 }