github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/util/filters.go (about)

     1  package util
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"path/filepath"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/hanks177/podman/v4/pkg/timetype"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // ComputeUntilTimestamp extracts until timestamp from filters
    16  func ComputeUntilTimestamp(filterValues []string) (time.Time, error) {
    17  	invalid := time.Time{}
    18  	if len(filterValues) != 1 {
    19  		return invalid, errors.Errorf("specify exactly one timestamp for until")
    20  	}
    21  	ts, err := timetype.GetTimestamp(filterValues[0], time.Now())
    22  	if err != nil {
    23  		return invalid, err
    24  	}
    25  	seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
    26  	if err != nil {
    27  		return invalid, err
    28  	}
    29  	return time.Unix(seconds, nanoseconds), nil
    30  }
    31  
    32  // filtersFromRequests extracts the "filters" parameter from the specified
    33  // http.Request.  The parameter can either be a `map[string][]string` as done
    34  // in new versions of Docker and libpod, or a `map[string]map[string]bool` as
    35  // done in older versions of Docker.  We have to do a bit of Yoga to support
    36  // both - just as Docker does as well.
    37  //
    38  // Please refer to https://github.com/containers/podman/issues/6899 for some
    39  // background.
    40  func FiltersFromRequest(r *http.Request) ([]string, error) {
    41  	var (
    42  		compatFilters map[string]map[string]bool
    43  		filters       map[string][]string
    44  		libpodFilters []string
    45  		raw           []byte
    46  	)
    47  
    48  	if _, found := r.URL.Query()["filters"]; found {
    49  		raw = []byte(r.Form.Get("filters"))
    50  	} else if _, found := r.URL.Query()["Filters"]; found {
    51  		raw = []byte(r.Form.Get("Filters"))
    52  	} else {
    53  		return []string{}, nil
    54  	}
    55  
    56  	// Backwards compat with older versions of Docker.
    57  	if err := json.Unmarshal(raw, &compatFilters); err == nil {
    58  		for filterKey, filterMap := range compatFilters {
    59  			for filterValue, toAdd := range filterMap {
    60  				if toAdd {
    61  					libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", filterKey, filterValue))
    62  				}
    63  			}
    64  		}
    65  		return libpodFilters, nil
    66  	}
    67  
    68  	if err := json.Unmarshal(raw, &filters); err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	for filterKey, filterSlice := range filters {
    73  		for _, filterValue := range filterSlice {
    74  			libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", filterKey, filterValue))
    75  		}
    76  	}
    77  
    78  	return libpodFilters, nil
    79  }
    80  
    81  // PrepareFilters prepares a *map[string][]string of filters to be later searched
    82  // in lipod and compat API to get desired filters
    83  func PrepareFilters(r *http.Request) (*map[string][]string, error) {
    84  	filtersList, err := FiltersFromRequest(r)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	filterMap := map[string][]string{}
    89  	for _, filter := range filtersList {
    90  		split := strings.SplitN(filter, "=", 2)
    91  		if len(split) > 1 {
    92  			filterMap[split[0]] = append(filterMap[split[0]], split[1])
    93  		}
    94  	}
    95  	return &filterMap, nil
    96  }
    97  
    98  func matchPattern(pattern string, value string) bool {
    99  	if strings.Contains(pattern, "*") {
   100  		filter := fmt.Sprintf("*%s*", pattern)
   101  		filter = strings.ReplaceAll(filter, string(filepath.Separator), "|")
   102  		newName := strings.ReplaceAll(value, string(filepath.Separator), "|")
   103  		match, _ := filepath.Match(filter, newName)
   104  		return match
   105  	}
   106  	return false
   107  }
   108  
   109  // MatchLabelFilters matches labels and returns true if they are valid
   110  func MatchLabelFilters(filterValues []string, labels map[string]string) bool {
   111  outer:
   112  	for _, filterValue := range filterValues {
   113  		filterArray := strings.SplitN(filterValue, "=", 2)
   114  		filterKey := filterArray[0]
   115  		if len(filterArray) > 1 {
   116  			filterValue = filterArray[1]
   117  		} else {
   118  			filterValue = ""
   119  		}
   120  		for labelKey, labelValue := range labels {
   121  			if ((labelKey == filterKey) || matchPattern(filterKey, labelKey)) && (filterValue == "" || labelValue == filterValue) {
   122  				continue outer
   123  			}
   124  		}
   125  		return false
   126  	}
   127  	return true
   128  }