diff --git a/hcloud/exp/labelutils/selector.go b/hcloud/exp/labelutils/selector.go new file mode 100644 index 00000000..bdc9c0a6 --- /dev/null +++ b/hcloud/exp/labelutils/selector.go @@ -0,0 +1,24 @@ +package labelutils + +import ( + "fmt" + "sort" + "strings" +) + +// Selector combines the label set into a [label selector](https://docs.hetzner.cloud/#label-selector) that only selects +// resources have all specified labels set. +// +// The selector string can be used to filter resources when listing, for example with [hcloud.ServerClient.AllWithOpts()]. +func Selector(labels map[string]string) string { + selectors := make([]string, 0, len(labels)) + + for k, v := range labels { + selectors = append(selectors, fmt.Sprintf("%s=%s", k, v)) + } + + // Reproducible result for tests + sort.Strings(selectors) + + return strings.Join(selectors, ",") +} diff --git a/hcloud/exp/labelutils/selector_test.go b/hcloud/exp/labelutils/selector_test.go new file mode 100644 index 00000000..2faefdf9 --- /dev/null +++ b/hcloud/exp/labelutils/selector_test.go @@ -0,0 +1,39 @@ +package labelutils + +import "testing" + +func TestSelector(t *testing.T) { + tests := []struct { + name string + labels map[string]string + expectedSelector string + }{ + { + name: "empty selector", + labels: map[string]string{}, + expectedSelector: "", + }, + { + name: "single label", + labels: map[string]string{"foo": "bar"}, + expectedSelector: "foo=bar", + }, + { + name: "multiple labels", + labels: map[string]string{"foo": "bar", "foz": "baz"}, + expectedSelector: "foo=bar,foz=baz", + }, + { + name: "nil map", + labels: nil, + expectedSelector: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Selector(tt.labels); got != tt.expectedSelector { + t.Errorf("Selector() = %v, want %v", got, tt.expectedSelector) + } + }) + } +}