inject transformer prefix into metric

Change-Id: Iacab685a710d8f8d5b80ed0d35e5ccc22bd929cb
This commit is contained in:
Shihang Zhang 2019-08-01 14:49:37 -07:00
parent f4521bf5a2
commit 099484ee5f
4 changed files with 167 additions and 14 deletions

View File

@ -63,7 +63,7 @@ var (
Name: "transformation_operations_total", Name: "transformation_operations_total",
Help: "Total number of transformations.", Help: "Total number of transformations.",
}, },
[]string{"transformation_type", "status"}, []string{"transformation_type", "transformer_prefix", "status"},
) )
deprecatedTransformerFailuresTotal = prometheus.NewCounterVec( deprecatedTransformerFailuresTotal = prometheus.NewCounterVec(
@ -130,8 +130,8 @@ func RegisterMetrics() {
// RecordTransformation records latencies and count of TransformFromStorage and TransformToStorage operations. // RecordTransformation records latencies and count of TransformFromStorage and TransformToStorage operations.
// Note that transformation_failures_total metric is deprecated, use transformation_operations_total instead. // Note that transformation_failures_total metric is deprecated, use transformation_operations_total instead.
func RecordTransformation(transformationType string, start time.Time, err error) { func RecordTransformation(transformationType, transformerPrefix string, start time.Time, err error) {
transformerOperationsTotal.WithLabelValues(transformationType, status.Code(err).String()).Inc() transformerOperationsTotal.WithLabelValues(transformationType, transformerPrefix, status.Code(err).String()).Inc()
switch { switch {
case err == nil: case err == nil:

View File

@ -49,7 +49,7 @@ func TestTotals(t *testing.T) {
apiserver_storage_transformation_failures_total{transformation_type="encrypt"} 1 apiserver_storage_transformation_failures_total{transformation_type="encrypt"} 1
# HELP apiserver_storage_transformation_operations_total Total number of transformations. # HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter # TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="Unknown",transformation_type="encrypt"} 1 apiserver_storage_transformation_operations_total{status="Unknown",transformation_type="encrypt",transformer_prefix="k8s:enc:kms:v1:"} 1
`, `,
}, },
{ {
@ -61,7 +61,7 @@ func TestTotals(t *testing.T) {
want: ` want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations. # HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter # TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="OK",transformation_type="encrypt"} 1 apiserver_storage_transformation_operations_total{status="OK",transformation_type="encrypt",transformer_prefix="k8s:enc:kms:v1:"} 1
`, `,
}, },
{ {
@ -77,7 +77,7 @@ func TestTotals(t *testing.T) {
apiserver_storage_transformation_failures_total{transformation_type="encrypt"} 1 apiserver_storage_transformation_failures_total{transformation_type="encrypt"} 1
# HELP apiserver_storage_transformation_operations_total Total number of transformations. # HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter # TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="FailedPrecondition",transformation_type="encrypt"} 1 apiserver_storage_transformation_operations_total{status="FailedPrecondition",transformation_type="encrypt",transformer_prefix="k8s:enc:kms:v1:"} 1
`, `,
}, },
} }
@ -86,7 +86,7 @@ func TestTotals(t *testing.T) {
for _, tt := range testCases { for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) { t.Run(tt.desc, func(t *testing.T) {
RecordTransformation("encrypt", time.Now(), tt.error) RecordTransformation("encrypt", "k8s:enc:kms:v1:", time.Now(), tt.error)
defer transformerOperationsTotal.Reset() defer transformerOperationsTotal.Reset()
defer deprecatedTransformerFailuresTotal.Reset() defer deprecatedTransformerFailuresTotal.Reset()
if err := testutil.GatherAndCompare(prometheus.DefaultGatherer, strings.NewReader(tt.want), tt.metrics...); err != nil { if err := testutil.GatherAndCompare(prometheus.DefaultGatherer, strings.NewReader(tt.want), tt.metrics...); err != nil {

View File

@ -85,18 +85,12 @@ func (t *MutableTransformer) Set(transformer Transformer) {
} }
func (t *MutableTransformer) TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error) { func (t *MutableTransformer) TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error) {
defer func(start time.Time) {
RecordTransformation("from_storage", start, err)
}(time.Now())
t.lock.RLock() t.lock.RLock()
transformer := t.transformer transformer := t.transformer
t.lock.RUnlock() t.lock.RUnlock()
return transformer.TransformFromStorage(data, context) return transformer.TransformFromStorage(data, context)
} }
func (t *MutableTransformer) TransformToStorage(data []byte, context Context) (out []byte, err error) { func (t *MutableTransformer) TransformToStorage(data []byte, context Context) (out []byte, err error) {
defer func(start time.Time) {
RecordTransformation("to_storage", start, err)
}(time.Now())
t.lock.RLock() t.lock.RLock()
transformer := t.transformer transformer := t.transformer
t.lock.RUnlock() t.lock.RUnlock()
@ -134,28 +128,37 @@ func NewPrefixTransformers(err error, transformers ...PrefixTransformer) Transfo
// the result of transforming the value. It will always mark any transformation as stale that is not using // the result of transforming the value. It will always mark any transformation as stale that is not using
// the first transformer. // the first transformer.
func (t *prefixTransformers) TransformFromStorage(data []byte, context Context) ([]byte, bool, error) { func (t *prefixTransformers) TransformFromStorage(data []byte, context Context) ([]byte, bool, error) {
start := time.Now()
for i, transformer := range t.transformers { for i, transformer := range t.transformers {
if bytes.HasPrefix(data, transformer.Prefix) { if bytes.HasPrefix(data, transformer.Prefix) {
result, stale, err := transformer.Transformer.TransformFromStorage(data[len(transformer.Prefix):], context) result, stale, err := transformer.Transformer.TransformFromStorage(data[len(transformer.Prefix):], context)
// To migrate away from encryption, user can specify an identity transformer higher up // To migrate away from encryption, user can specify an identity transformer higher up
// (in the config file) than the encryption transformer. In that scenario, the identity transformer needs to // (in the config file) than the encryption transformer. In that scenario, the identity transformer needs to
// identify (during reads from disk) whether the data being read is encrypted or not. If the data is encrypted, // identify (during reads from disk) whether the data being read is encrypted or not. If the data is encrypted,
// it shall throw an error, but that error should not prevent subsequent transformers from being tried. // it shall throw an error, but that error should not prevent the next subsequent transformer from being tried.
if len(transformer.Prefix) == 0 && err != nil { if len(transformer.Prefix) == 0 && err != nil {
continue continue
} }
if len(transformer.Prefix) == 0 {
RecordTransformation("from_storage", "identity", start, err)
} else {
RecordTransformation("from_storage", string(transformer.Prefix), start, err)
}
return result, stale || i != 0, err return result, stale || i != 0, err
} }
} }
RecordTransformation("from_storage", "unknown", start, t.err)
return nil, false, t.err return nil, false, t.err
} }
// TransformToStorage uses the first transformer and adds its prefix to the data. // TransformToStorage uses the first transformer and adds its prefix to the data.
func (t *prefixTransformers) TransformToStorage(data []byte, context Context) ([]byte, error) { func (t *prefixTransformers) TransformToStorage(data []byte, context Context) ([]byte, error) {
start := time.Now()
transformer := t.transformers[0] transformer := t.transformers[0]
prefixedData := make([]byte, len(transformer.Prefix), len(data)+len(transformer.Prefix)) prefixedData := make([]byte, len(transformer.Prefix), len(data)+len(transformer.Prefix))
copy(prefixedData, transformer.Prefix) copy(prefixedData, transformer.Prefix)
result, err := transformer.Transformer.TransformToStorage(data, context) result, err := transformer.Transformer.TransformToStorage(data, context)
RecordTransformation("to_storage", string(transformer.Prefix), start, err)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,7 +19,11 @@ package value
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
) )
type testTransformer struct { type testTransformer struct {
@ -99,3 +103,149 @@ func TestPrefixTo(t *testing.T) {
} }
} }
} }
func TestPrefixFromMetrics(t *testing.T) {
testErr := fmt.Errorf("test error")
transformerErr := fmt.Errorf("test error")
identityTransformer := PrefixTransformer{Prefix: []byte{}, Transformer: &testTransformer{from: []byte("value1")}}
identityTransformerErr := PrefixTransformer{Prefix: []byte{}, Transformer: &testTransformer{err: transformerErr}}
otherTransformer := PrefixTransformer{Prefix: []byte("other:"), Transformer: &testTransformer{from: []byte("value1")}}
otherTransformerErr := PrefixTransformer{Prefix: []byte("other:"), Transformer: &testTransformer{err: transformerErr}}
testCases := []struct {
desc string
input []byte
prefix Transformer
metrics []string
want string
err error
}{
{
desc: "identity prefix",
input: []byte("value"),
prefix: NewPrefixTransformers(testErr, identityTransformer, otherTransformer),
metrics: []string{
"apiserver_storage_transformation_operations_total",
},
want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="OK",transformation_type="from_storage",transformer_prefix="identity"} 1
`,
err: nil,
},
{
desc: "other prefix (ok)",
input: []byte("other:value"),
prefix: NewPrefixTransformers(testErr, identityTransformerErr, otherTransformer),
metrics: []string{
"apiserver_storage_transformation_operations_total",
},
want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="OK",transformation_type="from_storage",transformer_prefix="other:"} 1
`,
err: nil,
},
{
desc: "other prefix (error)",
input: []byte("other:value"),
prefix: NewPrefixTransformers(testErr, identityTransformerErr, otherTransformerErr),
metrics: []string{
"apiserver_storage_transformation_operations_total",
},
want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="Unknown",transformation_type="from_storage",transformer_prefix="other:"} 1
`,
err: nil,
},
{
desc: "unknown prefix",
input: []byte("foo:value"),
prefix: NewPrefixTransformers(testErr, identityTransformerErr, otherTransformer),
metrics: []string{
"apiserver_storage_transformation_operations_total",
},
want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="Unknown",transformation_type="from_storage",transformer_prefix="unknown"} 1
`,
err: nil,
},
}
RegisterMetrics()
transformerOperationsTotal.Reset()
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
tc.prefix.TransformFromStorage(tc.input, nil)
defer transformerOperationsTotal.Reset()
if err := testutil.GatherAndCompare(prometheus.DefaultGatherer, strings.NewReader(tc.want), tc.metrics...); err != nil {
t.Fatal(err)
}
})
}
}
func TestPrefixToMetrics(t *testing.T) {
testErr := fmt.Errorf("test error")
transformerErr := fmt.Errorf("test error")
otherTransformer := PrefixTransformer{Prefix: []byte("other:"), Transformer: &testTransformer{from: []byte("value1")}}
otherTransformerErr := PrefixTransformer{Prefix: []byte("other:"), Transformer: &testTransformer{err: transformerErr}}
testCases := []struct {
desc string
input []byte
prefix Transformer
metrics []string
want string
err error
}{
{
desc: "ok",
input: []byte("value"),
prefix: NewPrefixTransformers(testErr, otherTransformer),
metrics: []string{
"apiserver_storage_transformation_operations_total",
},
want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="OK",transformation_type="to_storage",transformer_prefix="other:"} 1
`,
err: nil,
},
{
desc: "error",
input: []byte("value"),
prefix: NewPrefixTransformers(testErr, otherTransformerErr),
metrics: []string{
"apiserver_storage_transformation_operations_total",
},
want: `
# HELP apiserver_storage_transformation_operations_total Total number of transformations.
# TYPE apiserver_storage_transformation_operations_total counter
apiserver_storage_transformation_operations_total{status="Unknown",transformation_type="to_storage",transformer_prefix="other:"} 1
`,
err: nil,
},
}
RegisterMetrics()
transformerOperationsTotal.Reset()
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
tc.prefix.TransformToStorage(tc.input, nil)
defer transformerOperationsTotal.Reset()
if err := testutil.GatherAndCompare(prometheus.DefaultGatherer, strings.NewReader(tc.want), tc.metrics...); err != nil {
t.Fatal(err)
}
})
}
}