Skip to content

Commit 6a8bf29

Browse files
authored
Merge pull request #1982 from Abdomash/profile-server-side-filtering
Add server side filtering for `incus profile list`
2 parents 61c622e + 5346910 commit 6a8bf29

File tree

18 files changed

+1603
-1418
lines changed

18 files changed

+1603
-1418
lines changed

client/incus_profiles.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ func (r *ProtocolIncus) GetProfiles() ([]api.Profile, error) {
3636
return profiles, nil
3737
}
3838

39+
// GetProfilesWithFilter returns a filtered list of available Profile structs.
40+
func (r *ProtocolIncus) GetProfilesWithFilter(filters []string) ([]api.Profile, error) {
41+
profiles := []api.Profile{}
42+
43+
v := url.Values{}
44+
v.Set("recursion", "1")
45+
v.Set("filter", parseFilters(filters))
46+
47+
_, err := r.queryStruct("GET", fmt.Sprintf("/profiles?%s", v.Encode()), nil, "", &profiles)
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
return profiles, nil
53+
}
54+
3955
// GetProfilesAllProjects returns a list of profiles across all projects as Profile structs.
4056
func (r *ProtocolIncus) GetProfilesAllProjects() ([]api.Profile, error) {
4157
err := r.CheckExtension("profiles_all_projects")
@@ -52,6 +68,28 @@ func (r *ProtocolIncus) GetProfilesAllProjects() ([]api.Profile, error) {
5268
return profiles, nil
5369
}
5470

71+
// GetProfilesAllProjectsWithFilter returns a filtered list of profiles across all projects as Profile structs.
72+
func (r *ProtocolIncus) GetProfilesAllProjectsWithFilter(filters []string) ([]api.Profile, error) {
73+
err := r.CheckExtension("profiles_all_projects")
74+
if err != nil {
75+
return nil, fmt.Errorf(`The server is missing the required "profiles_all_projects" API extension`)
76+
}
77+
78+
profiles := []api.Profile{}
79+
80+
v := url.Values{}
81+
v.Set("recursion", "1")
82+
v.Set("all-projects", "true")
83+
v.Set("filter", parseFilters(filters))
84+
85+
_, err = r.queryStruct("GET", fmt.Sprintf("/profiles?%s", v.Encode()), nil, "", &profiles)
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
return profiles, nil
91+
}
92+
5593
// GetProfile returns a Profile entry for the provided name.
5694
func (r *ProtocolIncus) GetProfile(name string) (*api.Profile, string, error) {
5795
profile := api.Profile{}

client/interfaces.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,10 @@ type InstanceServer interface {
290290

291291
// Profile functions
292292
GetProfilesAllProjects() (profiles []api.Profile, err error)
293+
GetProfilesAllProjectsWithFilter(filters []string) ([]api.Profile, error)
293294
GetProfileNames() (names []string, err error)
294295
GetProfiles() (profiles []api.Profile, err error)
296+
GetProfilesWithFilter(filters []string) ([]api.Profile, error)
295297
GetProfile(name string) (profile *api.Profile, ETag string, err error)
296298
CreateProfile(profile api.ProfilesPost) (err error)
297299
UpdateProfile(name string, profile api.ProfilePut, ETag string) (err error)

cmd/incus/profile.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"net/http"
88
"os"
9+
"regexp"
910
"slices"
1011
"sort"
1112
"strings"
@@ -729,12 +730,20 @@ type cmdProfileList struct {
729730
// Command returns a cobra.Command for use with (*cobra.Command).AddCommand.
730731
func (c *cmdProfileList) Command() *cobra.Command {
731732
cmd := &cobra.Command{}
732-
cmd.Use = usage("list", i18n.G("[<remote>:]"))
733+
cmd.Use = usage("list", i18n.G("[<remote>:] [<filter>...]"))
733734
cmd.Aliases = []string{"ls"}
734735
cmd.Short = i18n.G("List profiles")
735736
cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
736737
`List profiles
737738
739+
Filters may be of the <key>=<value> form for property based filtering,
740+
or part of the profile name. Filters must be delimited by a ','.
741+
742+
Examples:
743+
- "foo" lists all profiles that start with the name foo
744+
- "name=foo" lists all profiles that exactly have the name foo
745+
- "description=.*bar.*" lists all profiles with a description that contains "bar"
746+
738747
The -c option takes a (optionally comma-separated) list of arguments
739748
that control which image attributes to output when displaying in table
740749
or csv format.
@@ -826,7 +835,7 @@ func (c *cmdProfileList) usedByColumnData(profile api.Profile) string {
826835
// Run runs the actual command logic.
827836
func (c *cmdProfileList) Run(cmd *cobra.Command, args []string) error {
828837
// Quick checks.
829-
exit, err := c.global.checkArgs(cmd, args, 0, 1)
838+
exit, err := c.global.checkArgs(cmd, args, 0, -1)
830839
if exit {
831840
return err
832841
}
@@ -835,10 +844,16 @@ func (c *cmdProfileList) Run(cmd *cobra.Command, args []string) error {
835844
return errors.New(i18n.G("Can't specify --project with --all-projects"))
836845
}
837846

838-
// Parse remote
847+
// Parse remote and filters.
839848
remote := ""
840-
if len(args) > 0 {
841-
remote = args[0]
849+
filters := []string{}
850+
851+
if len(args) != 0 {
852+
filters = args
853+
if strings.Contains(args[0], ":") && !strings.Contains(args[0], "=") {
854+
remote = args[0]
855+
filters = args[1:]
856+
}
842857
}
843858

844859
resources, err := c.global.parseServers(remote)
@@ -848,18 +863,29 @@ func (c *cmdProfileList) Run(cmd *cobra.Command, args []string) error {
848863

849864
resource := resources[0]
850865

866+
flattenedFilters := []string{}
867+
for _, filter := range filters {
868+
flattenedFilters = append(flattenedFilters, strings.Split(filter, ",")...)
869+
}
870+
871+
filters = flattenedFilters
872+
873+
if len(filters) > 0 && !strings.Contains(filters[0], "=") {
874+
filters[0] = fmt.Sprintf("name=^%s($|.*)", regexp.QuoteMeta(filters[0]))
875+
}
876+
877+
serverFilters, _ := getServerSupportedFilters(filters, []string{}, false)
878+
851879
// List profiles
852880
var profiles []api.Profile
853881
if c.flagAllProjects {
854-
profiles, err = resource.server.GetProfilesAllProjects()
855-
if err != nil {
856-
return err
857-
}
882+
profiles, err = resource.server.GetProfilesAllProjectsWithFilter(serverFilters)
858883
} else {
859-
profiles, err = resource.server.GetProfiles()
860-
if err != nil {
861-
return err
862-
}
884+
profiles, err = resource.server.GetProfilesWithFilter(serverFilters)
885+
}
886+
887+
if err != nil {
888+
return err
863889
}
864890

865891
columns, err := c.parseColumns()

0 commit comments

Comments
 (0)