Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kmsv2: reload metrics bug fix backport #120544

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ import (
"k8s.io/apiserver/pkg/apis/config/validation"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/options/encryptionconfig/metrics"
storagevalue "k8s.io/apiserver/pkg/storage/value"
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope"
envelopekmsv2 "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2"
kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2"
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics"
envelopemetrics "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics"
"k8s.io/apiserver/pkg/storage/value/encrypt/identity"
"k8s.io/apiserver/pkg/storage/value/encrypt/secretbox"
utilfeature "k8s.io/apiserver/pkg/util/feature"
Expand Down Expand Up @@ -104,6 +105,12 @@ const (
kmsReloadHealthCheckName = "kms-providers"
)

func init() {
metrics.RegisterMetrics()
storagevalue.RegisterMetrics()
envelopemetrics.RegisterMetrics()
}

type kmsPluginHealthzResponse struct {
err error
received time.Time
Expand Down Expand Up @@ -445,10 +452,10 @@ func (h *kmsv2PluginProbe) isKMSv2ProviderHealthyAndMaybeRotateDEK(ctx context.C
}

if errCode, err := envelopekmsv2.ValidateKeyID(response.KeyID); err != nil {
metrics.RecordInvalidKeyIDFromStatus(h.name, string(errCode))
envelopemetrics.RecordInvalidKeyIDFromStatus(h.name, string(errCode))
errs = append(errs, fmt.Errorf("got invalid KMSv2 KeyID hash %q: %w", envelopekmsv2.GetHashIfNotEmpty(response.KeyID), err))
} else {
metrics.RecordKeyIDFromStatus(h.name, response.KeyID)
envelopemetrics.RecordKeyIDFromStatus(h.name, response.KeyID)
// unconditionally append as we filter out nil errors below
errs = append(errs, h.rotateDEKOnKeyIDChange(ctx, response.KeyID, string(uuid.NewUUID())))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,26 @@ import (
"math/rand"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"

"github.com/google/go-cmp/cmp"
clientv3 "go.etcd.io/etcd/client/v3"
"golang.org/x/crypto/cryptobyte"

"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/storage/value"
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
mock "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/testing/v1beta1"
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
featuregatetesting "k8s.io/component-base/featuregate/testing"
kmsapi "k8s.io/kms/apis/v1beta1"
"k8s.io/kubernetes/test/integration"
Expand Down Expand Up @@ -319,6 +324,26 @@ resources:
test.cleanUp()
}
}()
ctx := testContext(t)
// the global metrics registry persists across test runs - reset it here so we can make assertions
copyConfig := rest.CopyConfig(test.kubeAPIServer.ClientConfig)
copyConfig.GroupVersion = &schema.GroupVersion{}
copyConfig.NegotiatedSerializer = unstructuredscheme.NewUnstructuredNegotiatedSerializer()
rc, err := rest.RESTClientFor(copyConfig)
if err != nil {
t.Fatal(err)
}
if err := rc.Delete().AbsPath("/metrics").Do(ctx).Error(); err != nil {
t.Fatal(err)
}

// assert that the metrics we collect during the test run match expectations
// NOTE: 2 successful automatic reload resulted from 2 config file updates
wantMetricStrings := []string{
`apiserver_encryption_config_controller_automatic_reload_failures_total 0`,
`apiserver_encryption_config_controller_automatic_reload_last_timestamp_seconds{status="success"} FP`,
`apiserver_encryption_config_controller_automatic_reload_success_total 2`,
}

test.secret, err = test.createSecret(testSecret, testNamespace)
if err != nil {
Expand Down Expand Up @@ -378,7 +403,6 @@ resources:

// run storage migration
// get secrets
ctx := testContext(t)
secretsList, err := test.restClient.CoreV1().Secrets("").List(
ctx,
metav1.ListOptions{},
Expand Down Expand Up @@ -522,6 +546,33 @@ resources:
if _, err = test.restClient.CoreV1().ConfigMaps("").List(ctx, metav1.ListOptions{}); err != nil {
t.Fatalf("failed to list configmaps, err: %v", err)
}
// recreate rest client with the new transformTest
copyConfig = rest.CopyConfig(test.kubeAPIServer.ClientConfig)
copyConfig.GroupVersion = &schema.GroupVersion{}
copyConfig.NegotiatedSerializer = unstructuredscheme.NewUnstructuredNegotiatedSerializer()
rc, err = rest.RESTClientFor(copyConfig)
if err != nil {
t.Fatal(err)
}
defer func() {
body, err := rc.Get().AbsPath("/metrics").DoRaw(ctx)
if err != nil {
t.Fatal(err)
}
var gotMetricStrings []string
trimFP := regexp.MustCompile(`(.*)(} \d+\.\d+.*)`)
for _, line := range strings.Split(string(body), "\n") {
if strings.HasPrefix(line, "apiserver_encryption_config_controller_") {
if strings.Contains(line, "_seconds") {
line = trimFP.ReplaceAllString(line, `$1`) + "} FP" // ignore floating point metric values
}
gotMetricStrings = append(gotMetricStrings, line)
}
}
if diff := cmp.Diff(wantMetricStrings, gotMetricStrings); diff != "" {
t.Errorf("unexpected metrics diff (-want +got): %s", diff)
}
}()
}

func TestEncryptAll(t *testing.T) {
Expand Down