Skip to content

Commit

Permalink
feat: transformer functions
Browse files Browse the repository at this point in the history
Add functions to template executer.

Signed-off-by: Serge Logvinov <[email protected]>
  • Loading branch information
sergelogvinov committed May 8, 2024
1 parent 0e8728c commit 386958d
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 16 deletions.
20 changes: 20 additions & 0 deletions docs/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,23 @@ Example output:
```txt
talosccm_csr_approval_count{status="approve"} 2
```

### Transformer rules calls

|Metric name|Metric type|Labels/tags|
|-----------|-----------|-----------|
|talosccm_transformer_duration_seconds|Histogram|`type`=<type_transformation>|
|talosccm_transformer_errors_total|Counter|`type`=<type_transformation>|

Example output:

```txt
talosccm_transformer_duration_seconds_bucket{type="metadata",le="0.001"} 16
talosccm_transformer_duration_seconds_bucket{type="metadata",le="0.01"} 16
talosccm_transformer_duration_seconds_bucket{type="metadata",le="0.05"} 16
talosccm_transformer_duration_seconds_bucket{type="metadata",le="0.1"} 16
talosccm_transformer_duration_seconds_bucket{type="metadata",le="+Inf"} 16
talosccm_transformer_duration_seconds_sum{type="metadata"} 0.0012434149999999999
talosccm_transformer_duration_seconds_count{type="metadata"} 16
talosccm_transformer_errors_total{type="metadata"} 6
```
51 changes: 51 additions & 0 deletions pkg/metrics/metrics_trans.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package metrics

import (
"time"

"k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
)

// TransformerMetrics contains the metrics for transformer.
type TransformerMetrics struct {
Duration *metrics.HistogramVec
Errors *metrics.CounterVec
}

var transformerMetrics = registerTransformerMetrics()

// ObserveTransformer records the transformer latency and counts the errors.
func (mc *MetricContext) ObserveTransformer(err error) error {
transformerMetrics.Duration.WithLabelValues(mc.attributes...).Observe(
time.Since(mc.start).Seconds())

if err != nil {
transformerMetrics.Errors.WithLabelValues(mc.attributes...).Inc()
}

return err
}

func registerTransformerMetrics() *TransformerMetrics {
metrics := &TransformerMetrics{
Duration: metrics.NewHistogramVec(
&metrics.HistogramOpts{
Name: "talosccm_transformer_duration_seconds",
Help: "Latency of an Transformer call",
Buckets: []float64{.001, .01, .05, .1},
}, []string{"type"}),
Errors: metrics.NewCounterVec(
&metrics.CounterOpts{
Name: "talosccm_transformer_errors_total",
Help: "Total number of errors for an Transformer call",
}, []string{"type"}),
}

legacyregistry.MustRegister(
metrics.Duration,
metrics.Errors,
)

return metrics
}
12 changes: 8 additions & 4 deletions pkg/talos/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/siderolabs/talos-cloud-controller-manager/pkg/metrics"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
utilsnet "github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/net"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"

Expand All @@ -23,9 +24,12 @@ import (
"k8s.io/utils/strings/slices"
)

func ipDescovery(nodeIPs []string, ifaces []network.AddressStatusSpec) (publicIPv4s, publicIPv6s []string) {
func ipDiscovery(nodeIPs []string, ifaces []network.AddressStatusSpec) (publicIPv4s, publicIPv6s []string) {
for _, iface := range ifaces {
if iface.LinkName == "kubespan" || iface.LinkName == "lo" {
if iface.LinkName == constants.KubeSpanLinkName ||
iface.LinkName == constants.SideroLinkName ||
iface.LinkName == "lo" ||
strings.HasPrefix(iface.LinkName, "dummy") {
continue
}

Expand All @@ -52,7 +56,7 @@ func getNodeAddresses(config *cloudConfig, platform string, features *transforme
switch platform {
// Those platforms don't expose public IPs information in metadata
case "nocloud", "metal", "openstack", "oracle":
publicIPv4s, publicIPv6s = ipDescovery(nodeIPs, ifaces)
publicIPv4s, publicIPv6s = ipDiscovery(nodeIPs, ifaces)
default:
for _, iface := range ifaces {
if iface.LinkName == "external" {
Expand All @@ -72,7 +76,7 @@ func getNodeAddresses(config *cloudConfig, platform string, features *transforme
}

if features != nil && features.PublicIPDiscovery {
ipv4, ipv6 := ipDescovery(nodeIPs, ifaces)
ipv4, ipv6 := ipDiscovery(nodeIPs, ifaces)
publicIPv4s = append(publicIPv4s, ipv4...)
publicIPv6s = append(publicIPv6s, ipv6...)
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/talos/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud
}
}

mct := metrics.NewMetricContext("metadata")

nodeSpec, err := transformer.TransformNode(i.c.config.Transformations, meta)
if err != nil {
if mct.ObserveTransformer(err) != nil {
return nil, fmt.Errorf("error transforming node: %w", err)
}

Expand Down
85 changes: 85 additions & 0 deletions pkg/transformer/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package transformer

import (
"encoding/base64"
"regexp"
"strings"
)

var genericMap = map[string]interface{}{
// String functions:
"upper": strings.ToUpper,
"lower": strings.ToLower,
"trim": strings.TrimSpace,
"trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
"trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },

"replace": func(o, n, s string) string { return strings.ReplaceAll(s, o, n) },
"regexFind": regexFind,
"regexFindString": regexFindString,
"regexReplaceAll": regexReplaceAll,

"contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
"hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
"hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },

// Encoding functions:
"b64enc": base64encode,
"b64dec": base64decode,
}

// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
func GenericFuncMap() map[string]interface{} {
gfm := make(map[string]interface{}, len(genericMap))
for k, v := range genericMap {
gfm[k] = v
}

return gfm
}

func regexFindString(regex string, s string, n int) (string, error) {
r, err := regexp.Compile(regex)
if err != nil {
return "", err
}

matches := r.FindStringSubmatch(s)

if len(matches) < n+1 {
return "", nil
}

return matches[n], nil
}

func regexReplaceAll(regex string, s string, repl string) (string, error) {
r, err := regexp.Compile(regex)
if err != nil {
return "", err
}

return r.ReplaceAllString(s, repl), nil
}

func regexFind(regex string, s string) (string, error) {
r, err := regexp.Compile(regex)
if err != nil {
return "", err
}

return r.FindString(s), nil
}

func base64encode(v string) string {
return base64.StdEncoding.EncodeToString([]byte(v))
}

func base64decode(v string) (string, error) {
data, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return "", err
}

return string(data), nil
}
4 changes: 1 addition & 3 deletions pkg/transformer/transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,14 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS
}
}
}

return node, nil
}
}

return node, nil
}

func executeTemplate(tmpl string, data interface{}) (string, error) {
t, err := template.New("transformer").Parse(tmpl)
t, err := template.New("transformer").Funcs(GenericFuncMap()).Parse(tmpl)
if err != nil {
return "", fmt.Errorf("failed to parse template %q: %w", tmpl, err)
}
Expand Down
63 changes: 55 additions & 8 deletions pkg/transformer/transformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,24 @@ func TestMatch(t *testing.T) {
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
Labels: map[string]string{
"karpenter.sh/capacity-type": "{{ if .Spot }}spot{{ else }}on-demand{{ end }}",
},
PlatformMetadata: map[string]string{
"Spot": "true",
"Zone": "us-west1",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
Spot: true,
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{},
Labels: map[string]string{},
Labels: map[string]string{
"karpenter.sh/capacity-type": "spot",
},
},
expectedMeta: &runtime.PlatformMetadataSpec{
Platform: "test-platform",
Expand All @@ -164,25 +169,67 @@ func TestMatch(t *testing.T) {
{
Name: "my-transformer",
PlatformMetadata: map[string]string{
"Hostname": "fake-hostname",
"spot": "true",
"zoNe": "us-west1",
"wrong": "value",
"Hostname": "fake-hostname",
"spot": "true",
"zoNe": "us-west1",
"wrong": "value",
"InstanceType": `{{ regexFindString "^type-([a-z0-9]+)-(.*)$" .Hostname 1 }}`,
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
Hostname: "type-c1m5-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{},
Labels: map[string]string{},
},
expectedMeta: &runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "type-c1m5-hostname",
Spot: true,
Zone: "us-west1",
InstanceType: "c1m5",
},
},
{
name: "Multiple transformers",
terms: []transformer.NodeTerm{
{
Name: "first-rule",
Annotations: map[string]string{
"first-annotation": "first-value",
},
Labels: map[string]string{
"karpenter.sh/capacity-type": "on-demand",
},
},
{
Name: "second-rule",
Labels: map[string]string{
"karpenter.sh/capacity-type": "spot",
},
PlatformMetadata: map[string]string{
"Zone": "us-west1",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{
"first-annotation": "first-value",
},
Labels: map[string]string{
"karpenter.sh/capacity-type": "spot",
},
},
expectedMeta: &runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
Spot: true,
Zone: "us-west1",
},
},
Expand Down

0 comments on commit 386958d

Please sign in to comment.