mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-28 03:19:47 +08:00
Add MatchSearch to resources for fuzzy search (#9892)
Adds a method to resources MatchSearch, that goes through select resource field values, and matches it against a list of user provided search values. Part of RFD 55
This commit is contained in:
+9
-1
@@ -26,11 +26,12 @@ import (
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
)
|
||||
|
||||
// Application represents a web app.
|
||||
type Application interface {
|
||||
// ResourceWithOrigin provides common resource methods.
|
||||
// ResourceWithLabels provides common resource methods.
|
||||
ResourceWithLabels
|
||||
// GetNamespace returns the app namespace.
|
||||
GetNamespace() string
|
||||
@@ -249,6 +250,13 @@ func (a *AppV3) Copy() *AppV3 {
|
||||
return proto.Clone(a).(*AppV3)
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (a *AppV3) MatchSearch(values []string) bool {
|
||||
fieldVals := append(utils.MapToStrings(a.GetAllLabels()), a.GetName(), a.GetDescription(), a.GetPublicAddr())
|
||||
return MatchSearch(fieldVals, values, nil)
|
||||
}
|
||||
|
||||
// setStaticFields sets static resource header and metadata fields.
|
||||
func (a *AppV3) setStaticFields() {
|
||||
a.Kind = KindApp
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
|
||||
// AppServer represents a single proxied web app.
|
||||
type AppServer interface {
|
||||
// Resource provides common resource methods.
|
||||
// ResourceWithLabels provides common resource methods.
|
||||
ResourceWithLabels
|
||||
// GetNamespace returns server namespace.
|
||||
GetNamespace() string
|
||||
@@ -289,6 +289,12 @@ func (s *AppServerV3) Copy() AppServer {
|
||||
return proto.Clone(s).(*AppServerV3)
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (s *AppServerV3) MatchSearch(values []string) bool {
|
||||
return MatchSearch(nil, values, nil)
|
||||
}
|
||||
|
||||
// AppServers represents a list of app servers.
|
||||
type AppServers []AppServer
|
||||
|
||||
|
||||
+19
-4
@@ -24,13 +24,14 @@ import (
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
// Database represents a database proxied by a database server.
|
||||
type Database interface {
|
||||
// ResourceWithOrigin provides common resource methods.
|
||||
ResourceWithOrigin
|
||||
// ResourceWithLabels provides common resource methods.
|
||||
ResourceWithLabels
|
||||
// GetNamespace returns the database namespace.
|
||||
GetNamespace() string
|
||||
// GetStaticLabels returns the database static labels.
|
||||
@@ -41,8 +42,6 @@ type Database interface {
|
||||
GetDynamicLabels() map[string]CommandLabel
|
||||
// SetDynamicLabels sets the database dynamic labels.
|
||||
SetDynamicLabels(map[string]CommandLabel)
|
||||
// GetAllLabels returns combined static and dynamic labels.
|
||||
GetAllLabels() map[string]string
|
||||
// LabelsString returns all labels as a string.
|
||||
LabelsString() string
|
||||
// String returns string representation of the database.
|
||||
@@ -330,6 +329,22 @@ func (d *DatabaseV3) Copy() *DatabaseV3 {
|
||||
return proto.Clone(d).(*DatabaseV3)
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (d *DatabaseV3) MatchSearch(values []string) bool {
|
||||
fieldVals := append(utils.MapToStrings(d.GetAllLabels()), d.GetName(), d.GetDescription(), d.GetProtocol(), d.GetType())
|
||||
|
||||
var custom func(string) bool
|
||||
switch d.GetType() {
|
||||
case DatabaseTypeCloudSQL:
|
||||
custom = func(val string) bool {
|
||||
return strings.EqualFold(val, "cloud") || strings.EqualFold(val, "cloud sql")
|
||||
}
|
||||
}
|
||||
|
||||
return MatchSearch(fieldVals, values, custom)
|
||||
}
|
||||
|
||||
// setStaticFields sets static resource header and metadata fields.
|
||||
func (d *DatabaseV3) setStaticFields() {
|
||||
d.Kind = KindDatabase
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
|
||||
// DatabaseServer represents a database access server.
|
||||
type DatabaseServer interface {
|
||||
// Resource provides common resource methods.
|
||||
// ResourceWithLabels provides common resource methods.
|
||||
ResourceWithLabels
|
||||
// GetNamespace returns server namespace.
|
||||
GetNamespace() string
|
||||
@@ -276,6 +276,12 @@ func (s *DatabaseServerV3) Copy() DatabaseServer {
|
||||
return proto.Clone(s).(*DatabaseServerV3)
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (s *DatabaseServerV3) MatchSearch(values []string) bool {
|
||||
return MatchSearch(nil, values, nil)
|
||||
}
|
||||
|
||||
// DatabaseServers represents a list of database servers.
|
||||
type DatabaseServers []DatabaseServer
|
||||
|
||||
|
||||
+33
-6
@@ -17,13 +17,14 @@ limitations under the License.
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
// WindowsDesktopService represents a Windows desktop service instance.
|
||||
type WindowsDesktopService interface {
|
||||
// Resource provides common resource methods.
|
||||
Resource
|
||||
// ResourceWithLabels provides common resource methods.
|
||||
ResourceWithLabels
|
||||
// GetAddr returns the network address of this service.
|
||||
GetAddr() string
|
||||
// GetVersion returns the teleport binary version of this service.
|
||||
@@ -79,14 +80,33 @@ func (s *WindowsDesktopServiceV3) GetTeleportVersion() string {
|
||||
return s.Spec.TeleportVersion
|
||||
}
|
||||
|
||||
// Origin returns the origin value of the resource.
|
||||
func (s *WindowsDesktopServiceV3) Origin() string {
|
||||
return s.Metadata.Origin()
|
||||
}
|
||||
|
||||
// SetOrigin sets the origin value of the resource.
|
||||
func (s *WindowsDesktopServiceV3) SetOrigin(origin string) {
|
||||
s.Metadata.SetOrigin(origin)
|
||||
}
|
||||
|
||||
// GetAllLabels returns the resources labels.
|
||||
func (s *WindowsDesktopServiceV3) GetAllLabels() map[string]string {
|
||||
return s.Metadata.Labels
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (s *WindowsDesktopServiceV3) MatchSearch(values []string) bool {
|
||||
return MatchSearch(nil, values, nil)
|
||||
}
|
||||
|
||||
// WindowsDesktop represents a Windows desktop host.
|
||||
type WindowsDesktop interface {
|
||||
// Resource provides common resource methods.
|
||||
Resource
|
||||
// ResourceWithLabels provides common resource methods.
|
||||
ResourceWithLabels
|
||||
// GetAddr returns the network address of this host.
|
||||
GetAddr() string
|
||||
// GetAllLabels returns combined static and dynamic labels.
|
||||
GetAllLabels() map[string]string
|
||||
// LabelsString returns all labels as a string.
|
||||
LabelsString() string
|
||||
// GetDomain returns the ActiveDirectory domain of this host.
|
||||
@@ -160,3 +180,10 @@ func (d *WindowsDesktopV3) Origin() string {
|
||||
func (d *WindowsDesktopV3) SetOrigin(o string) {
|
||||
d.Metadata.Labels[OriginLabel] = o
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (d *WindowsDesktopV3) MatchSearch(values []string) bool {
|
||||
fieldVals := append(utils.MapToStrings(d.GetAllLabels()), d.GetName(), d.GetAddr())
|
||||
return MatchSearch(fieldVals, values, nil)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
@@ -164,6 +165,13 @@ func (k *KubernetesClusterV3) Copy() *KubernetesClusterV3 {
|
||||
return proto.Clone(k).(*KubernetesClusterV3)
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (k *KubernetesClusterV3) MatchSearch(values []string) bool {
|
||||
fieldVals := append(utils.MapToStrings(k.GetAllLabels()), k.GetName())
|
||||
return MatchSearch(fieldVals, values, nil)
|
||||
}
|
||||
|
||||
// setStaticFields sets static resource header and metadata fields.
|
||||
func (k *KubernetesClusterV3) setStaticFields() {
|
||||
k.Kind = KindKubernetesCluster
|
||||
|
||||
@@ -18,6 +18,7 @@ package types
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/defaults"
|
||||
@@ -81,6 +82,9 @@ type ResourceWithLabels interface {
|
||||
ResourceWithOrigin
|
||||
// GetAllLabels returns all resource's labels.
|
||||
GetAllLabels() map[string]string
|
||||
// MatchSearch goes through select field values of a resource
|
||||
// and tries to match against the list of search values.
|
||||
MatchSearch(searchValues []string) bool
|
||||
}
|
||||
|
||||
// ResourcesWithLabels is a list of labeled resources.
|
||||
@@ -277,3 +281,32 @@ var validLabelKey = regexp.MustCompile(LabelPattern)
|
||||
func IsValidLabelKey(s string) bool {
|
||||
return validLabelKey.MatchString(s)
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values from a resource
|
||||
// and tries to match against the list of search values, ignoring case and order.
|
||||
// Returns true if all search vals were matched (or if nil search vals).
|
||||
// Returns false if no or partial match (or nil field values).
|
||||
func MatchSearch(fieldVals []string, searchVals []string, customMatch func(val string) bool) bool {
|
||||
// Case fold all values to avoid repeated case folding while matching.
|
||||
caseFoldedSearchVals := utils.ToLowerStrings(searchVals)
|
||||
caseFoldedFieldVals := utils.ToLowerStrings(fieldVals)
|
||||
|
||||
Outer:
|
||||
for _, searchV := range caseFoldedSearchVals {
|
||||
// Iterate through field values to look for a match.
|
||||
for _, fieldV := range caseFoldedFieldVals {
|
||||
if strings.Contains(fieldV, searchV) {
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
|
||||
if customMatch != nil && customMatch(searchV) {
|
||||
continue
|
||||
}
|
||||
|
||||
// When no fields matched a value, prematurely end if we can.
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Copyright 2022 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMatchSearch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
expectMatch require.BoolAssertionFunc
|
||||
fieldVals []string
|
||||
searchVals []string
|
||||
customFn func(v string) bool
|
||||
}{
|
||||
{
|
||||
name: "no match",
|
||||
expectMatch: require.False,
|
||||
fieldVals: []string{"foo", "bar", "baz"},
|
||||
searchVals: []string{"cat"},
|
||||
customFn: func(v string) bool {
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no match for partial match",
|
||||
expectMatch: require.False,
|
||||
fieldVals: []string{"foo"},
|
||||
searchVals: []string{"foo", "dog"},
|
||||
},
|
||||
{
|
||||
name: "no match for partial custom match",
|
||||
expectMatch: require.False,
|
||||
fieldVals: []string{"foo", "bar", "baz"},
|
||||
searchVals: []string{"foo", "bee", "rat"},
|
||||
customFn: func(v string) bool {
|
||||
return v == "bee"
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no match for search phrase",
|
||||
expectMatch: require.False,
|
||||
fieldVals: []string{"foo", "dog", "dog foo", "foodog"},
|
||||
searchVals: []string{"foo dog"},
|
||||
},
|
||||
{
|
||||
name: "match",
|
||||
expectMatch: require.True,
|
||||
fieldVals: []string{"foo", "bar", "baz"},
|
||||
searchVals: []string{"baz"},
|
||||
},
|
||||
{
|
||||
name: "match with nil search values",
|
||||
expectMatch: require.True,
|
||||
},
|
||||
{
|
||||
name: "match with repeat search vals",
|
||||
expectMatch: require.True,
|
||||
fieldVals: []string{"foo", "bar", "baz"},
|
||||
searchVals: []string{"foo", "foo", "baz"},
|
||||
},
|
||||
{
|
||||
name: "match for a list of search vals contained within one field value",
|
||||
expectMatch: require.True,
|
||||
fieldVals: []string{"foo barbaz"},
|
||||
searchVals: []string{"baz", "foo", "bar"},
|
||||
},
|
||||
{
|
||||
name: "match with mix of single vals and phrases",
|
||||
expectMatch: require.True,
|
||||
fieldVals: []string{"foo baz", "bar"},
|
||||
searchVals: []string{"baz", "foo", "foo baz", "bar"},
|
||||
},
|
||||
{
|
||||
name: "match ignore case",
|
||||
expectMatch: require.True,
|
||||
fieldVals: []string{"FOO barBaz"},
|
||||
searchVals: []string{"baZ", "foo", "BaR"},
|
||||
},
|
||||
{
|
||||
name: "match with custom match",
|
||||
expectMatch: require.True,
|
||||
fieldVals: []string{"foo", "bar", "baz"},
|
||||
searchVals: []string{"foo", "bar", "tunnel"},
|
||||
customFn: func(v string) bool {
|
||||
return v == "tunnel"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
matched := MatchSearch(tc.fieldVals, tc.searchVals, tc.customFn)
|
||||
tc.expectMatch(t, matched)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchSearch_ResourceSpecific(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
labels := map[string]string{"env": "prod", "os": "mac"}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
// searchNotDefined refers to resources where the searcheable field values are not defined.
|
||||
searchNotDefined bool
|
||||
matchingSearchVals []string
|
||||
newResource func() ResourceWithLabels
|
||||
}{
|
||||
{
|
||||
name: "node",
|
||||
matchingSearchVals: []string{"foo", "bar", "prod", "os"},
|
||||
newResource: func() ResourceWithLabels {
|
||||
server, err := NewServerWithLabels("_", KindNode, ServerSpecV2{
|
||||
Hostname: "foo",
|
||||
Addr: "bar",
|
||||
}, labels)
|
||||
require.NoError(t, err)
|
||||
|
||||
return server
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "node using tunnel",
|
||||
matchingSearchVals: []string{"tunnel"},
|
||||
newResource: func() ResourceWithLabels {
|
||||
server, err := NewServer("_", KindNode, ServerSpecV2{
|
||||
UseTunnel: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return server
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "windows desktop",
|
||||
matchingSearchVals: []string{"foo", "bar", "env", "prod", "os"},
|
||||
newResource: func() ResourceWithLabels {
|
||||
desktop, err := NewWindowsDesktopV3("foo", labels, WindowsDesktopSpecV3{
|
||||
Addr: "bar",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return desktop
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
matchingSearchVals: []string{"foo", "bar", "baz", "mac"},
|
||||
newResource: func() ResourceWithLabels {
|
||||
app, err := NewAppV3(Metadata{
|
||||
Name: "foo",
|
||||
Description: "bar",
|
||||
Labels: labels,
|
||||
}, AppSpecV3{
|
||||
PublicAddr: "baz",
|
||||
URI: "_",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return app
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "kube cluster",
|
||||
matchingSearchVals: []string{"foo", "prod", "env"},
|
||||
newResource: func() ResourceWithLabels {
|
||||
kc, err := NewKubernetesClusterV3FromLegacyCluster("_", &KubernetesCluster{
|
||||
Name: "foo",
|
||||
StaticLabels: labels,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return kc
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "database",
|
||||
matchingSearchVals: []string{"foo", "bar", "baz", "prod", DatabaseTypeRedshift},
|
||||
newResource: func() ResourceWithLabels {
|
||||
db, err := NewDatabaseV3(Metadata{
|
||||
Name: "foo",
|
||||
Description: "bar",
|
||||
Labels: labels,
|
||||
}, DatabaseSpecV3{
|
||||
Protocol: "baz",
|
||||
URI: "_",
|
||||
AWS: AWS{
|
||||
Redshift: Redshift{
|
||||
ClusterID: "_",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return db
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "database with gcp keywords",
|
||||
matchingSearchVals: []string{"cloud", "cloud sql"},
|
||||
newResource: func() ResourceWithLabels {
|
||||
db, err := NewDatabaseV3(Metadata{
|
||||
Name: "_",
|
||||
Labels: labels,
|
||||
}, DatabaseSpecV3{
|
||||
Protocol: "_",
|
||||
URI: "_",
|
||||
GCP: GCPCloudSQL{
|
||||
ProjectID: "_",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return db
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "app server",
|
||||
searchNotDefined: true,
|
||||
newResource: func() ResourceWithLabels {
|
||||
appServer, err := NewAppServerV3(Metadata{
|
||||
Name: "_",
|
||||
}, AppServerSpecV3{
|
||||
HostID: "_",
|
||||
App: &AppV3{Metadata: Metadata{Name: "_"}, Spec: AppSpecV3{URI: "_"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return appServer
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "db server",
|
||||
searchNotDefined: true,
|
||||
newResource: func() ResourceWithLabels {
|
||||
dbServer, err := NewDatabaseServerV3(Metadata{
|
||||
Name: "_",
|
||||
}, DatabaseServerSpecV3{
|
||||
HostID: "_",
|
||||
Hostname: "_",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return dbServer
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "kube service",
|
||||
searchNotDefined: true,
|
||||
newResource: func() ResourceWithLabels {
|
||||
kubeServer, err := NewServer("_", KindKubeService, ServerSpecV2{})
|
||||
require.NoError(t, err)
|
||||
|
||||
return kubeServer
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "desktop service",
|
||||
searchNotDefined: true,
|
||||
newResource: func() ResourceWithLabels {
|
||||
desktopService, err := NewWindowsDesktopServiceV3("_", WindowsDesktopServiceSpecV3{
|
||||
Addr: "_",
|
||||
TeleportVersion: "_",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return desktopService
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
resource := tc.newResource()
|
||||
|
||||
// Nil search values, should always return true
|
||||
match := resource.MatchSearch(nil)
|
||||
require.True(t, match)
|
||||
|
||||
switch {
|
||||
case tc.searchNotDefined:
|
||||
// Trying to search something in resources without search field values defined
|
||||
// should always return false.
|
||||
match := resource.MatchSearch([]string{"_"})
|
||||
require.False(t, match)
|
||||
default:
|
||||
// Test no match.
|
||||
match := resource.MatchSearch([]string{"foo", "llama"})
|
||||
require.False(t, match)
|
||||
|
||||
// Test match.
|
||||
match = resource.MatchSearch(tc.matchingSearchVals)
|
||||
require.True(t, match)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+21
-3
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
"github.com/gravitational/teleport/api/utils"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/gravitational/trace"
|
||||
@@ -31,7 +32,7 @@ import (
|
||||
|
||||
// Server represents a Node, Proxy or Auth server in a Teleport cluster
|
||||
type Server interface {
|
||||
// Resource provides common resource headers
|
||||
// ResourceWithLabels provides common resource headers
|
||||
ResourceWithLabels
|
||||
// GetTeleportVersion returns the teleport version the server is running on
|
||||
GetTeleportVersion() string
|
||||
@@ -41,8 +42,6 @@ type Server interface {
|
||||
GetHostname() string
|
||||
// GetNamespace returns server namespace
|
||||
GetNamespace() string
|
||||
// GetAllLabels returns server's static and dynamic label values merged together
|
||||
GetAllLabels() map[string]string
|
||||
// GetLabels returns server's static label key pairs
|
||||
GetLabels() map[string]string
|
||||
// GetCmdLabels gets command labels
|
||||
@@ -370,6 +369,25 @@ func (s *ServerV2) CheckAndSetDefaults() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MatchSearch goes through select field values and tries to
|
||||
// match against the list of search values.
|
||||
func (s *ServerV2) MatchSearch(values []string) bool {
|
||||
var fieldVals []string
|
||||
var custom func(val string) bool
|
||||
|
||||
if s.GetKind() == KindNode {
|
||||
fieldVals = append(utils.MapToStrings(s.GetAllLabels()), s.GetName(), s.GetHostname(), s.GetAddr())
|
||||
|
||||
if s.GetUseTunnel() {
|
||||
custom = func(val string) bool {
|
||||
return strings.EqualFold(val, "tunnel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MatchSearch(fieldVals, values, custom)
|
||||
}
|
||||
|
||||
// DeepCopy creates a clone of this server value
|
||||
func (s *ServerV2) DeepCopy() Server {
|
||||
return proto.Clone(s).(*ServerV2)
|
||||
|
||||
Reference in New Issue
Block a user