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  }