Skip to content

Commit

Permalink
Add field-selector argument to installations list (#2888)
Browse files Browse the repository at this point in the history
* Add field-selector argument to installations list

Signed-off-by: Maninderjit Bindra <[email protected]>

---------

Signed-off-by: Maninderjit Bindra <[email protected]>
  • Loading branch information
maniSbindra authored Sep 14, 2023
1 parent 8dc0095 commit 513c0a0
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 0 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ and we will add you. **All** contributors belong here. 💯
* [Troy Connor](https://github.com/troy0820)
* [Phill Gibson](https://github.com/phillipgibson)
* [Ludvig Liljenberg](https://github.com/ludfjig)
* [Maninderjit Bindra](https://github.com/manisbindra)
1 change: 1 addition & 0 deletions cmd/porter/installations.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Optional output formats include json and yaml.`,
"Skip the number of installations by a certain amount. Defaults to 0.")
f.Int64Var(&opts.Limit, "limit", 0,
"Limit the number of installations by a certain amount. Defaults to 0.")
f.StringVar(&opts.FieldSelector, "field-selector", "", "Selector (field query) to filter on, supports '=' (e.g. --field-selector bundle.version=0.2.0,status.action=install). All fields from the json output are supported.")

return cmd
}
Expand Down
77 changes: 77 additions & 0 deletions pkg/porter/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"get.porter.sh/porter/pkg/storage"
"get.porter.sh/porter/pkg/tracing"
dtprinter "github.com/carolynvs/datetime-printer"

"reflect"
)

const (
Expand All @@ -34,6 +36,7 @@ type ListOptions struct {
Labels []string
Skip int64
Limit int64
FieldSelector string
}

func (o *ListOptions) Validate() error {
Expand Down Expand Up @@ -246,9 +249,21 @@ func (p *Porter) ListInstallations(ctx context.Context, opts ListOptions) (Displ
}

var displayInstallations DisplayInstallations
var fieldSelectorMap map[string]string
if opts.FieldSelector != "" {
fieldSelectorMap, err = parseFieldSelector(opts.FieldSelector)
if err != nil {
return nil, err
}
}

for _, installation := range installations {
di := NewDisplayInstallation(installation)
if opts.FieldSelector != "" && !doesInstallationMatchFieldSelectors(di, fieldSelectorMap) {
continue
}
displayInstallations = append(displayInstallations, di)

}
sort.Sort(sort.Reverse(displayInstallations))

Expand Down Expand Up @@ -322,3 +337,65 @@ func getDisplayInstallationStatus(installation storage.Installation) string {

return status
}

// Split the fieldSelector into a map of fields and values
// e.g. "bundle.version=0.2.0,status.action=install" => map[string]string{"bundle.version": "0.2.0", "status.action": "install"}
func parseFieldSelector(fieldSelector string) (map[string]string, error) {
fieldSelectorMap := make(map[string]string)
for _, field := range strings.Split(fieldSelector, ",") {
fieldParts := strings.Split(field, "=")
if len(fieldParts) != 2 {
return nil, fmt.Errorf("invalid field selector: %s", fieldSelector)
}
fieldSelectorMap[fieldParts[0]] = fieldParts[1]
}

return fieldSelectorMap, nil
}

// Check if the installation matches the field selectors
func doesInstallationMatchFieldSelectors(installation DisplayInstallation, fieldSelectorMap map[string]string) bool {
for field, value := range fieldSelectorMap {
if !installationHasFieldWithValue(installation, field, value) {
return false
}
}
return true
}

// Check if the installation has the field with the value
// e.g. installationHasFieldWithValue(installation, "bundle.version", "0.2.0") => true if installation.Bundle.Version (for which json tag is bunde.version) == "0.2.0"
func installationHasFieldWithValue(installation DisplayInstallation, fieldJsonTagPath string, value string) bool {

fieldJsonTagPathParts := strings.Split(fieldJsonTagPath, ".")
current := reflect.ValueOf(installation)

for _, fieldJsonTagPart := range fieldJsonTagPathParts {
if current.Kind() != reflect.Struct {
return false
}
field := getFieldByJSONTag(current, fieldJsonTagPart)
if !field.IsValid() {
return false
}
current = field
}

return reflect.DeepEqual(current.Interface(), value)
}

// Return the reflect.value based on the field's json tag
func getFieldByJSONTag(value reflect.Value, fieldJsonTag string) reflect.Value {
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)

reflectTag := field.Tag.Get("json")
if strings.Contains(reflectTag, ",") {
reflectTag = strings.Split(reflectTag, ",")[0]
}
if reflectTag == fieldJsonTag {
return value.Field(i)
}
}
return reflect.Value{}
}
Loading

0 comments on commit 513c0a0

Please sign in to comment.