Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add field-selector argument to installations list #2888

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
maniSbindra marked this conversation as resolved.
Show resolved Hide resolved
)

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, ",") {
maniSbindra marked this conversation as resolved.
Show resolved Hide resolved
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 {
maniSbindra marked this conversation as resolved.
Show resolved Hide resolved
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, ".")
maniSbindra marked this conversation as resolved.
Show resolved Hide resolved
current := reflect.ValueOf(installation)
maniSbindra marked this conversation as resolved.
Show resolved Hide resolved

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 {
maniSbindra marked this conversation as resolved.
Show resolved Hide resolved
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
Loading