Skip to content
This repository has been archived by the owner on Nov 5, 2021. It is now read-only.

Commit

Permalink
Make target based additional labels easier to identify.
Browse files Browse the repository at this point in the history
= Enclose target based labels within '@@', for example: @target.label.zone@.
= Add support for adding a label based on target's name, using the keyword @target.name@.

While here also standardize on how we export label through sysvars -- use "label_<labelname>" instead of "labels_<labelname>.

PiperOrigin-RevId: 324673535
  • Loading branch information
manugarg committed Aug 3, 2020
1 parent 5715f21 commit dd0bb3e
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 125 deletions.
110 changes: 110 additions & 0 deletions probes/options/labels.go
@@ -0,0 +1,110 @@
// Copyright 2017-2020 Google 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 options

import (
"regexp"
"strings"

configpb "github.com/google/cloudprober/probes/proto"
)

// TargetLabelType for target based additional labels
type TargetLabelType int

// TargetLabelType enum values.
const (
NotTargetLabel TargetLabelType = iota
TargetLabel
TargetName
)

var targetLabelRegex = regexp.MustCompile(`@target.label.(.*)@`)

// AdditionalLabel encapsulates additional labels to attach to probe results.
type AdditionalLabel struct {
Key string

Value string // static value
TargetLabelKey string // from target
TargetLabelType TargetLabelType

// This map will allow for quick label lookup for a target. It will be
// updated by the probe while updating targets.
LabelForTarget map[string]string
}

// UpdateForTarget updates target-based label's value.
func (al *AdditionalLabel) UpdateForTarget(tname string, tLabels map[string]string) {
if al.TargetLabelType == NotTargetLabel {
return
}

if al.LabelForTarget == nil {
al.LabelForTarget = make(map[string]string)
}

switch al.TargetLabelType {
case TargetLabel:
al.LabelForTarget[tname] = tLabels[al.TargetLabelKey]
case TargetName:
al.LabelForTarget[tname] = tname
}
}

// KeyValueForTarget returns key, value pair for the given target.
func (al *AdditionalLabel) KeyValueForTarget(targetName string) (key, val string) {
if al.Value != "" {
return al.Key, al.Value
}
return al.Key, al.LabelForTarget[targetName]
}

func parseAdditionalLabels(p *configpb.ProbeDef) []*AdditionalLabel {
var aLabels []*AdditionalLabel

for _, pb := range p.GetAdditionalLabel() {
al := &AdditionalLabel{
Key: pb.GetKey(),
}
aLabels = append(aLabels, al)

val := pb.GetValue()
if !strings.Contains(val, "@") {
al.Value = val
continue
}

if val == "@target.name@" {
al.TargetLabelType = TargetName
al.LabelForTarget = make(map[string]string)
continue
}

matches := targetLabelRegex.FindStringSubmatch(val)
if len(matches) == 2 {
al.TargetLabelType = TargetLabel
al.TargetLabelKey = matches[1]
al.LabelForTarget = make(map[string]string)
continue
}

// If a match is not found and target contains an "@" character, use it
// as it is.
al.Value = val
}

return aLabels
}
99 changes: 99 additions & 0 deletions probes/options/labels_test.go
@@ -0,0 +1,99 @@
// Copyright 2017-2020 Google 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 options

import (
"reflect"
"testing"

"github.com/golang/protobuf/proto"
configpb "github.com/google/cloudprober/probes/proto"
)

var configWithAdditionalLabels = &configpb.ProbeDef{
AdditionalLabel: []*configpb.AdditionalLabel{
{
Key: proto.String("src_zone"),
Value: proto.String("zoneA"),
},
{
Key: proto.String("dst_zone"),
Value: proto.String("@target.label.zone@"),
},
{
Key: proto.String("dst_name"),
Value: proto.String("@target.name@"),
},
},
}

func TestParseAdditionalLabel(t *testing.T) {
expectedAdditionalLabels := []*AdditionalLabel{
{
Key: "src_zone",
Value: "zoneA",
},
{
Key: "dst_zone",
TargetLabelType: TargetLabel,
TargetLabelKey: "zone",
LabelForTarget: make(map[string]string),
},
{
Key: "dst_name",
TargetLabelType: TargetName,
LabelForTarget: make(map[string]string),
},
}

aLabels := parseAdditionalLabels(configWithAdditionalLabels)

// Verify that we got the correct additional lables and also update them while
// iterating over them.
for i, al := range aLabels {
if !reflect.DeepEqual(al, expectedAdditionalLabels[i]) {
t.Errorf("Additional labels not parsed correctly. Got=%v, Wanted=%v", al, expectedAdditionalLabels[i])
}
}
}

func TestUpdateAdditionalLabel(t *testing.T) {
aLabels := parseAdditionalLabels(configWithAdditionalLabels)

// Verify that we got the correct additional lables and also update them while
// iterating over them.
for _, al := range aLabels {
al.UpdateForTarget("target1", map[string]string{})
al.UpdateForTarget("target2", map[string]string{"zone": "zoneB"})
}

expectedLabels := map[string][][2]string{
"target1": {{"src_zone", "zoneA"}, {"dst_zone", ""}, {"dst_name", "target1"}},
"target2": {{"src_zone", "zoneA"}, {"dst_zone", "zoneB"}, {"dst_name", "target2"}},
}

for target, labels := range expectedLabels {
var gotLabels [][2]string

for _, al := range aLabels {
k, v := al.KeyValueForTarget(target)
gotLabels = append(gotLabels, [2]string{k, v})
}

if !reflect.DeepEqual(gotLabels, labels) {
t.Errorf("Didn't get expected labels for the target: %s. Got=%v, Expected=%v", target, gotLabels, labels)
}
}
}
55 changes: 2 additions & 53 deletions probes/options/options.go
@@ -1,4 +1,4 @@
// Copyright 2017-2019 Google Inc.
// Copyright 2017-2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,6 @@ package options
import (
"fmt"
"net"
"strings"
"time"

"github.com/google/cloudprober/common/iputils"
Expand All @@ -33,38 +32,6 @@ import (
"github.com/google/cloudprober/validators"
)

// AdditionalLabel encapsulates additional labels to attach to probe results.
type AdditionalLabel struct {
Key string

Value string // static value
TargetLabelKey string // from target

// This map will allow for quick label lookup for a target. It will be
// updated by the probe while updating targets.
LabelForTarget map[string]string
}

// UpdateForTarget updates target-based label's value.
func (al *AdditionalLabel) UpdateForTarget(targetName string, targetLabels map[string]string) {
if al.TargetLabelKey == "" {
return
}

if al.LabelForTarget == nil {
al.LabelForTarget = make(map[string]string)
}
al.LabelForTarget[targetName] = targetLabels[al.TargetLabelKey]
}

// KeyValueForTarget returns key, value pair for the given target.
func (al *AdditionalLabel) KeyValueForTarget(targetName string) (key, val string) {
if al.Value != "" {
return al.Key, al.Value
}
return al.Key, al.LabelForTarget[targetName]
}

// Options encapsulates common probe options.
type Options struct {
Targets targets.Targets
Expand Down Expand Up @@ -200,7 +167,7 @@ func BuildProbeOptions(p *configpb.ProbeDef, ldLister lameduck.Lister, globalTar
}
}

opts.parseAdditionalLabels(p)
opts.AdditionalLabels = parseAdditionalLabels(p)

if !p.GetDebugOptions().GetLogMetrics() {
opts.LogMetrics = func(em *metrics.EventMetrics) {}
Expand All @@ -215,24 +182,6 @@ func BuildProbeOptions(p *configpb.ProbeDef, ldLister lameduck.Lister, globalTar
return opts, nil
}

func (opts *Options) parseAdditionalLabels(p *configpb.ProbeDef) {
for _, pb := range p.GetAdditionalLabel() {
al := &AdditionalLabel{
Key: pb.GetKey(),
}

val := pb.GetValue()
if !strings.HasPrefix(val, "target.labels.") {
al.Value = val
} else {
al.TargetLabelKey = strings.TrimPrefix(val, "target.labels.")
al.LabelForTarget = make(map[string]string)
}

opts.AdditionalLabels = append(opts.AdditionalLabels, al)
}
}

// DefaultOptions returns default options, capturing default values for the
// various fields.
func DefaultOptions() *Options {
Expand Down
71 changes: 1 addition & 70 deletions probes/options/options_test.go
@@ -1,4 +1,4 @@
// Copyright 2017-2019 Google Inc.
// Copyright 2017-2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,6 @@ package options
import (
"errors"
"net"
"reflect"
"testing"
"time"

Expand Down Expand Up @@ -280,74 +279,6 @@ func TestStatsExportInterval(t *testing.T) {
}
}

var coonfigWithAdditionalLabels = &configpb.ProbeDef{
AdditionalLabel: []*configpb.AdditionalLabel{
{
Key: proto.String("src_zone"),
Value: proto.String("zoneA"),
},
{
Key: proto.String("dst_zone"),
Value: proto.String("target.labels.zone"),
},
},
}

func TestParseAdditionalLabel(t *testing.T) {
expectedAdditionalLabels := []*AdditionalLabel{
{
Key: "src_zone",
Value: "zoneA",
},
{
Key: "dst_zone",
TargetLabelKey: "zone",
LabelForTarget: make(map[string]string),
},
}

opts := &Options{}
opts.parseAdditionalLabels(coonfigWithAdditionalLabels)

// Verify that we got the correct additional lables and also update them while
// iterating over them.
for i, al := range opts.AdditionalLabels {
if !reflect.DeepEqual(al, expectedAdditionalLabels[i]) {
t.Errorf("Additional labels not parsed correctly. Got=%v, Wanted=%v", al, expectedAdditionalLabels[i])
}
}
}

func TestUpdateAdditionalLabel(t *testing.T) {
opts := &Options{}
opts.parseAdditionalLabels(coonfigWithAdditionalLabels)

// Verify that we got the correct additional lables and also update them while
// iterating over them.
for _, al := range opts.AdditionalLabels {
al.UpdateForTarget("target1", map[string]string{})
al.UpdateForTarget("target2", map[string]string{"zone": "zoneB"})
}

expectedLabels := map[string][][2]string{
"target1": {{"src_zone", "zoneA"}, {"dst_zone", ""}},
"target2": {{"src_zone", "zoneA"}, {"dst_zone", "zoneB"}},
}

for target, labels := range expectedLabels {
var gotLabels [][2]string

for _, al := range opts.AdditionalLabels {
k, v := al.KeyValueForTarget(target)
gotLabels = append(gotLabels, [2]string{k, v})
}

if !reflect.DeepEqual(gotLabels, labels) {
t.Errorf("Didn't get expected labels for the target: %s. Got=%v, Expected=%v", target, gotLabels, labels)
}
}
}

func TestDefaultOptions(t *testing.T) {
// Most of all, it verifies that DefaultOptions() doesn't generate panic.
opts := DefaultOptions()
Expand Down
4 changes: 2 additions & 2 deletions sysvars/sysvars_gce.go
Expand Up @@ -92,9 +92,9 @@ var gceVars = func(vars map[string]string, l *logger.Logger) (bool, error) {
}

for k, v := range labels {
// Adds GCE labels to the dictionary with a 'labels_' prefix so they can be
// Adds GCE labels to the dictionary with a 'label_' prefix so they can be
// referenced in the cfg file.
vars["labels_"+k] = v
vars["label_"+k] = v

}
return onGCE, nil
Expand Down

0 comments on commit dd0bb3e

Please sign in to comment.