mirror of
https://github.com/cirruslabs/orchard.git
synced 2026-09-29 19:41:15 +02:00
Implement server-side filtering for VMs by worker (#392)
* Implement server-side filtering for VMs by worker * Parse more than one filter but error out when more than one is provided * Fix off-by-one * No need to use "\n" in Debugf()
This commit is contained in:
@@ -2,8 +2,11 @@ package client
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cirruslabs/orchard/internal/dialer"
|
||||
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
|
||||
)
|
||||
|
||||
type Option func(*Client)
|
||||
@@ -32,3 +35,25 @@ func WithDialer(dialer dialer.Dialer) Option {
|
||||
client.dialer = dialer
|
||||
}
|
||||
}
|
||||
|
||||
type ListInput struct {
|
||||
Filters []v1.Filter
|
||||
}
|
||||
|
||||
type ListOption func(params map[string]string)
|
||||
|
||||
func WithListFilters(filters ...v1.Filter) ListOption {
|
||||
return func(params map[string]string) {
|
||||
if len(filters) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var pairs []string
|
||||
|
||||
for _, filter := range filters {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%s", filter.Path, filter.Value))
|
||||
}
|
||||
|
||||
params["filter"] = strings.Join(pairs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -57,13 +57,18 @@ func (service *VMsService) Create(ctx context.Context, vm *v1.VM) error {
|
||||
}
|
||||
|
||||
func (service *VMsService) FindForWorker(ctx context.Context, worker string) ([]v1.VM, error) {
|
||||
allVms, err := service.List(ctx)
|
||||
allVms, err := service.List(ctx, WithListFilters(v1.Filter{
|
||||
Path: "worker",
|
||||
Value: worker,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []v1.VM
|
||||
|
||||
// Backwards compatibility with older Orchard Controllers
|
||||
// that do not support the "filter" query parameter
|
||||
for _, vmResource := range allVms {
|
||||
if vmResource.Worker != worker {
|
||||
continue
|
||||
@@ -75,11 +80,18 @@ func (service *VMsService) FindForWorker(ctx context.Context, worker string) ([]
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (service *VMsService) List(ctx context.Context) ([]v1.VM, error) {
|
||||
func (service *VMsService) List(ctx context.Context, opts ...ListOption) ([]v1.VM, error) {
|
||||
params := map[string]string{}
|
||||
|
||||
// Apply options
|
||||
for _, opt := range opts {
|
||||
opt(params)
|
||||
}
|
||||
|
||||
var vms []v1.VM
|
||||
|
||||
err := service.client.request(ctx, http.MethodGet, "vms",
|
||||
nil, &vms, nil)
|
||||
nil, &vms, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrInvalidFilter = errors.New("invalid filter")
|
||||
|
||||
type Filter struct {
|
||||
Path string
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewFilter(s string) (Filter, error) {
|
||||
parts := strings.SplitN(s, "=", 2)
|
||||
|
||||
if len(parts) != 2 {
|
||||
return Filter{}, fmt.Errorf("%w: expected path=value", ErrInvalidFilter)
|
||||
}
|
||||
|
||||
if parts[0] == "" {
|
||||
return Filter{}, fmt.Errorf("%w: path cannot be empty", ErrInvalidFilter)
|
||||
}
|
||||
|
||||
return Filter{
|
||||
Path: parts[0],
|
||||
Value: parts[1],
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package v1_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewFilter(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Input string
|
||||
Err error
|
||||
Path string
|
||||
Value string
|
||||
}{
|
||||
{
|
||||
Name: "simple",
|
||||
Input: "a.b.c=value",
|
||||
Path: "a.b.c",
|
||||
Value: "value",
|
||||
},
|
||||
{
|
||||
Name: "value with equals",
|
||||
Input: "a.b.c=d=e",
|
||||
Path: "a.b.c",
|
||||
Value: "d=e",
|
||||
},
|
||||
{
|
||||
Name: "empty value",
|
||||
Input: "a.b.c=",
|
||||
Path: "a.b.c",
|
||||
},
|
||||
{
|
||||
Name: "missing value",
|
||||
Input: "abc",
|
||||
Err: v1.ErrInvalidFilter,
|
||||
},
|
||||
{
|
||||
Name: "missing path",
|
||||
Input: "=value",
|
||||
Err: v1.ErrInvalidFilter,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
filter, err := v1.NewFilter(testCase.Input)
|
||||
require.ErrorIs(t, err, testCase.Err)
|
||||
require.Equal(t, testCase.Path, filter.Path)
|
||||
require.Equal(t, testCase.Value, filter.Value)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,3 +8,7 @@ type ServiceAccount struct {
|
||||
}
|
||||
|
||||
func (serviceAccount *ServiceAccount) SetVersion(_ uint64) {}
|
||||
|
||||
func (serviceAccount *ServiceAccount) Match(filter Filter) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -102,6 +102,15 @@ func (vm *VM) SetVersion(version uint64) {
|
||||
vm.Version = version
|
||||
}
|
||||
|
||||
func (vm *VM) Match(filter Filter) bool {
|
||||
switch filter.Path {
|
||||
case "worker":
|
||||
return vm.Worker == filter.Value
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) IsScheduled() bool {
|
||||
if ConditionExists(vm.Conditions, ConditionTypeScheduled) {
|
||||
return ConditionIsTrue(vm.Conditions, ConditionTypeScheduled)
|
||||
|
||||
@@ -32,3 +32,7 @@ func (worker Worker) Offline(workerOfflineTimeout time.Duration) bool {
|
||||
}
|
||||
|
||||
func (worker *Worker) SetVersion(_ uint64) {}
|
||||
|
||||
func (worker *Worker) Match(filter Filter) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user