github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/filters/containers.go (about) 1 package filters 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 9 cutil "github.com/containers/common/pkg/util" 10 "github.com/hanks177/podman/v4/libpod" 11 "github.com/hanks177/podman/v4/libpod/define" 12 "github.com/hanks177/podman/v4/pkg/util" 13 "github.com/pkg/errors" 14 ) 15 16 // GenerateContainerFilterFuncs return ContainerFilter functions based of filter. 17 func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { 18 switch filter { 19 case "id": 20 // we only have to match one ID 21 return func(c *libpod.Container) bool { 22 return util.StringMatchRegexSlice(c.ID(), filterValues) 23 }, nil 24 case "label": 25 // we have to match that all given labels exits on that container 26 return func(c *libpod.Container) bool { 27 return util.MatchLabelFilters(filterValues, c.Labels()) 28 }, nil 29 case "name": 30 // we only have to match one name 31 return func(c *libpod.Container) bool { 32 return util.StringMatchRegexSlice(c.Name(), filterValues) 33 }, nil 34 case "exited": 35 var exitCodes []int32 36 for _, exitCode := range filterValues { 37 ec, err := strconv.ParseInt(exitCode, 10, 32) 38 if err != nil { 39 return nil, errors.Wrapf(err, "exited code out of range %q", ec) 40 } 41 exitCodes = append(exitCodes, int32(ec)) 42 } 43 return func(c *libpod.Container) bool { 44 ec, exited, err := c.ExitCode() 45 if err == nil && exited { 46 for _, exitCode := range exitCodes { 47 if ec == exitCode { 48 return true 49 } 50 } 51 } 52 return false 53 }, nil 54 case "status": 55 for _, filterValue := range filterValues { 56 if _, err := define.StringToContainerStatus(filterValue); err != nil { 57 return nil, err 58 } 59 } 60 return func(c *libpod.Container) bool { 61 status, err := c.State() 62 if err != nil { 63 return false 64 } 65 state := status.String() 66 if status == define.ContainerStateConfigured { 67 state = "created" 68 } else if status == define.ContainerStateStopped { 69 state = "exited" 70 } 71 for _, filterValue := range filterValues { 72 if filterValue == "stopped" { 73 filterValue = "exited" 74 } 75 if state == filterValue { 76 return true 77 } 78 } 79 return false 80 }, nil 81 case "ancestor": 82 // This needs to refine to match docker 83 // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant. 84 return func(c *libpod.Container) bool { 85 for _, filterValue := range filterValues { 86 containerConfig := c.Config() 87 var imageTag string 88 var imageNameWithoutTag string 89 // Compare with ImageID, ImageName 90 // Will match ImageName if running image has tag latest for other tags exact complete filter must be given 91 imageNameSlice := strings.SplitN(containerConfig.RootfsImageName, ":", 2) 92 if len(imageNameSlice) == 2 { 93 imageNameWithoutTag = imageNameSlice[0] 94 imageTag = imageNameSlice[1] 95 } 96 97 if (containerConfig.RootfsImageID == filterValue) || 98 (containerConfig.RootfsImageName == filterValue) || 99 (imageNameWithoutTag == filterValue && imageTag == "latest") { 100 return true 101 } 102 } 103 return false 104 }, nil 105 case "before": 106 var createTime time.Time 107 for _, filterValue := range filterValues { 108 ctr, err := r.LookupContainer(filterValue) 109 if err != nil { 110 return nil, err 111 } 112 containerConfig := ctr.Config() 113 if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { 114 createTime = containerConfig.CreatedTime 115 } 116 } 117 return func(c *libpod.Container) bool { 118 cc := c.Config() 119 return createTime.After(cc.CreatedTime) 120 }, nil 121 case "since": 122 var createTime time.Time 123 for _, filterValue := range filterValues { 124 ctr, err := r.LookupContainer(filterValue) 125 if err != nil { 126 return nil, err 127 } 128 containerConfig := ctr.Config() 129 if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { 130 createTime = containerConfig.CreatedTime 131 } 132 } 133 return func(c *libpod.Container) bool { 134 cc := c.Config() 135 return createTime.Before(cc.CreatedTime) 136 }, nil 137 case "volume": 138 //- volume=(<volume-name>|<mount-point-destination>) 139 return func(c *libpod.Container) bool { 140 containerConfig := c.Config() 141 var dest string 142 for _, filterValue := range filterValues { 143 arr := strings.SplitN(filterValue, ":", 2) 144 source := arr[0] 145 if len(arr) == 2 { 146 dest = arr[1] 147 } 148 for _, mount := range containerConfig.Spec.Mounts { 149 if dest != "" && (mount.Source == source && mount.Destination == dest) { 150 return true 151 } 152 if dest == "" && mount.Source == source { 153 return true 154 } 155 } 156 for _, vname := range containerConfig.NamedVolumes { 157 if dest != "" && (vname.Name == source && vname.Dest == dest) { 158 return true 159 } 160 if dest == "" && vname.Name == source { 161 return true 162 } 163 } 164 } 165 return false 166 }, nil 167 case "health": 168 return func(c *libpod.Container) bool { 169 hcStatus, err := c.HealthCheckStatus() 170 if err != nil { 171 return false 172 } 173 for _, filterValue := range filterValues { 174 if hcStatus == filterValue { 175 return true 176 } 177 } 178 return false 179 }, nil 180 case "until": 181 return prepareUntilFilterFunc(filterValues) 182 case "pod": 183 var pods []*libpod.Pod 184 for _, podNameOrID := range filterValues { 185 p, err := r.LookupPod(podNameOrID) 186 if err != nil { 187 if errors.Cause(err) == define.ErrNoSuchPod { 188 continue 189 } 190 return nil, err 191 } 192 pods = append(pods, p) 193 } 194 return func(c *libpod.Container) bool { 195 // if no pods match, quick out 196 if len(pods) < 1 { 197 return false 198 } 199 // if the container has no pod id, quick out 200 if len(c.PodID()) < 1 { 201 return false 202 } 203 for _, p := range pods { 204 // we already looked up by name or id, so id match 205 // here is ok 206 if p.ID() == c.PodID() { 207 return true 208 } 209 } 210 return false 211 }, nil 212 case "network": 213 var inputNetNames []string 214 for _, val := range filterValues { 215 net, err := r.Network().NetworkInspect(val) 216 if err != nil { 217 if errors.Is(err, define.ErrNoSuchNetwork) { 218 continue 219 } 220 return nil, err 221 } 222 inputNetNames = append(inputNetNames, net.Name) 223 } 224 return func(c *libpod.Container) bool { 225 networkMode := c.NetworkMode() 226 // support docker like `--filter network=container:<IDorName>` 227 // check if networkMode is configured as `container:<ctr>` 228 // perform a match against filter `container:<IDorName>` 229 // networks is already going to be empty if `container:<ctr>` is configured as Mode 230 if strings.HasPrefix(networkMode, "container:") { 231 networkModeContainerPart := strings.SplitN(networkMode, ":", 2) 232 if len(networkModeContainerPart) < 2 { 233 return false 234 } 235 networkModeContainerID := networkModeContainerPart[1] 236 for _, val := range filterValues { 237 if strings.HasPrefix(val, "container:") { 238 filterNetworkModePart := strings.SplitN(val, ":", 2) 239 if len(filterNetworkModePart) < 2 { 240 return false 241 } 242 filterNetworkModeIDorName := filterNetworkModePart[1] 243 filterID, err := r.LookupContainerID(filterNetworkModeIDorName) 244 if err != nil { 245 return false 246 } 247 if filterID == networkModeContainerID { 248 return true 249 } 250 } 251 } 252 return false 253 } 254 255 networks, err := c.Networks() 256 // if err or no networks, quick out 257 if err != nil || len(networks) == 0 { 258 return false 259 } 260 for _, net := range networks { 261 if cutil.StringInSlice(net, inputNetNames) { 262 return true 263 } 264 } 265 return false 266 }, nil 267 case "restart-policy": 268 invalidPolicyNames := []string{} 269 for _, policy := range filterValues { 270 if _, ok := define.RestartPolicyMap[policy]; !ok { 271 invalidPolicyNames = append(invalidPolicyNames, policy) 272 } 273 } 274 var filterValueError error 275 if len(invalidPolicyNames) > 0 { 276 errPrefix := "invalid restart policy" 277 if len(invalidPolicyNames) > 1 { 278 errPrefix = "invalid restart policies" 279 } 280 filterValueError = fmt.Errorf("%s %s", strings.Join(invalidPolicyNames, ", "), errPrefix) 281 } 282 return func(c *libpod.Container) bool { 283 for _, policy := range filterValues { 284 if policy == "none" && c.RestartPolicy() == define.RestartPolicyNone { 285 return true 286 } 287 if c.RestartPolicy() == policy { 288 return true 289 } 290 } 291 return false 292 }, filterValueError 293 } 294 return nil, errors.Errorf("%s is an invalid filter", filter) 295 } 296 297 // GeneratePruneContainerFilterFuncs return ContainerFilter functions based of filter for prune operation 298 func GeneratePruneContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { 299 switch filter { 300 case "label": 301 return func(c *libpod.Container) bool { 302 return util.MatchLabelFilters(filterValues, c.Labels()) 303 }, nil 304 case "until": 305 return prepareUntilFilterFunc(filterValues) 306 } 307 return nil, errors.Errorf("%s is an invalid filter", filter) 308 } 309 310 func prepareUntilFilterFunc(filterValues []string) (func(container *libpod.Container) bool, error) { 311 until, err := util.ComputeUntilTimestamp(filterValues) 312 if err != nil { 313 return nil, err 314 } 315 return func(c *libpod.Container) bool { 316 if !until.IsZero() && c.CreatedTime().Before(until) { 317 return true 318 } 319 return false 320 }, nil 321 }