mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
1024 lines
36 KiB
Go
1024 lines
36 KiB
Go
// Copyright 2023 The gVisor Authors.
|
|
//
|
|
// 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 container
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/cleanup"
|
|
"gvisor.dev/gvisor/pkg/test/testutil"
|
|
"gvisor.dev/gvisor/runsc/config"
|
|
"gvisor.dev/gvisor/runsc/sandbox"
|
|
"gvisor.dev/gvisor/test/metricclient"
|
|
)
|
|
|
|
const (
|
|
// podAnnotation contains the name of the pod that a sandbox represents when running in
|
|
// Kubernetes.
|
|
podAnnotation = "io.kubernetes.cri.sandbox-name"
|
|
// namespaceAnnotation contains the name of the namespace that a sandbox is in when running in
|
|
// Kubernetes.
|
|
namespaceAnnotation = "io.kubernetes.cri.sandbox-namespace"
|
|
)
|
|
|
|
// metricsTest is returned by setupMetrics.
|
|
type metricsTest struct {
|
|
testCtx context.Context
|
|
rootDir string
|
|
bundleDir string
|
|
sleepSpec *specs.Spec
|
|
sleepConf *config.Config
|
|
udsPath string
|
|
client *metricclient.MetricClient
|
|
serverExtraArgs []string
|
|
}
|
|
|
|
// applyConf applies metric-server-related configuration options to the given config.
|
|
// Returns the passed-in config itself.
|
|
func (mt *metricsTest) applyConf(conf *config.Config) *config.Config {
|
|
conf.MetricServer = mt.sleepConf.MetricServer
|
|
conf.RootDir = mt.rootDir
|
|
return conf
|
|
}
|
|
|
|
// setupMetrics sets up a container configuration with metrics enabled, and returns it all.
|
|
// Also returns a cleanup function.
|
|
func setupMetrics(t *testing.T, forceTempUDS bool) (*metricsTest, func()) {
|
|
// Start the child reaper.
|
|
childReaper := &testutil.Reaper{}
|
|
childReaper.Start()
|
|
cu := cleanup.Make(childReaper.Stop)
|
|
|
|
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 3*time.Minute+30*time.Second)
|
|
cu.Add(cleanupCancel)
|
|
testCtx, testCancel := context.WithTimeout(cleanupCtx, 3*time.Minute)
|
|
cu.Add(testCancel)
|
|
|
|
spec, conf := sleepSpecConf(t)
|
|
conf.MetricServer = "%RUNTIME_ROOT%/metrics.sock"
|
|
serverExtraArgs := []string{"--exporter-prefix=testmetric_"}
|
|
rootDir, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
|
|
if err != nil {
|
|
t.Fatalf("error setting up container: %v", err)
|
|
}
|
|
cu.Add(cleanup)
|
|
tmpDir, err := os.MkdirTemp("/tmp", "metrics-")
|
|
if err != nil {
|
|
t.Fatalf("Cannot create temporary directory in /tmp: %v", err)
|
|
}
|
|
cu.Add(func() { os.RemoveAll(tmpDir) })
|
|
udsPath := filepath.Join(rootDir, "metrics.sock")
|
|
if forceTempUDS || len(udsPath) >= 100 {
|
|
udsPath = filepath.Join(tmpDir, "metrics.sock")
|
|
}
|
|
if len(udsPath) >= 100 {
|
|
t.Fatalf("Cannot come up with a UDS path shorter than the maximum length allowed by Linux (tried to use %q)", udsPath)
|
|
}
|
|
conf.MetricServer = udsPath
|
|
// The UDS should be deleted by the metrics server itself, but we clean it up here anyway just in case:
|
|
cu.Add(func() { os.Remove(udsPath) })
|
|
|
|
metricClient := metricclient.NewMetricClient(udsPath, rootDir)
|
|
if err := metricClient.SpawnServer(testCtx, conf, serverExtraArgs...); err != nil {
|
|
t.Fatalf("Cannot start metric server: %v", err)
|
|
}
|
|
cu.Add(func() { metricClient.ShutdownServer(cleanupCtx) })
|
|
|
|
return &metricsTest{
|
|
testCtx: testCtx,
|
|
rootDir: rootDir,
|
|
bundleDir: bundleDir,
|
|
sleepSpec: spec,
|
|
sleepConf: conf,
|
|
udsPath: udsPath,
|
|
client: metricClient,
|
|
serverExtraArgs: serverExtraArgs,
|
|
}, cu.Clean
|
|
}
|
|
|
|
// TestContainerMetrics verifies basic functionality of the metric server works.
|
|
func TestContainerMetrics(t *testing.T) {
|
|
targetOpens := 200
|
|
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, false)
|
|
defer cleanup()
|
|
|
|
if _, err := te.client.GetMetrics(te.testCtx, nil); err != nil {
|
|
t.Fatal("GetMetrics failed prior to container start")
|
|
}
|
|
if te.sleepSpec.Annotations == nil {
|
|
te.sleepSpec.Annotations = make(map[string]string)
|
|
}
|
|
te.sleepSpec.Annotations[podAnnotation] = "foopod"
|
|
te.sleepSpec.Annotations[namespaceAnnotation] = "foons"
|
|
args := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container: %v", err)
|
|
}
|
|
defer cont.Destroy()
|
|
udsStat, udsStatErr := os.Stat(te.udsPath)
|
|
if udsStatErr != nil {
|
|
t.Fatalf("Stat(%s) failed after creating container: %v", te.udsPath, udsStatErr)
|
|
}
|
|
if udsStat.Mode()&os.ModeSocket == 0 {
|
|
t.Errorf("Stat(%s): Got mode %x, expected socket (mode %x)", te.udsPath, udsStat.Mode(), os.ModeSocket)
|
|
}
|
|
initialData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("Cannot get metrics after creating container: %v", err)
|
|
}
|
|
gotSandboxMetadata, err := initialData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
Pod: "foopod",
|
|
Namespace: "foons",
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata: %v", err)
|
|
}
|
|
if gotSandboxMetadata["platform"] == "" || gotSandboxMetadata["platform"] != te.sleepConf.Platform {
|
|
t.Errorf("Invalid platform: Metric metadata says %v, config says %v", gotSandboxMetadata["platform"], te.sleepConf.Platform)
|
|
}
|
|
gotSpecMetadata, err := initialData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_spec_metadata",
|
|
Sandbox: args.ID,
|
|
Pod: "foopod",
|
|
Namespace: "foons",
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get spec metadata: %v", err)
|
|
}
|
|
if gotSpecMetadata["hasuid0"] == "" || (gotSpecMetadata["hasuid0"] != "true" && gotSpecMetadata["hasuid0"] != "false") {
|
|
t.Errorf("Invalid or absent hasuid0 key from spec metadata: %v", gotSpecMetadata["hasuid0"])
|
|
}
|
|
t.Logf("Metrics prior to container start:\n\n%s\n\n", initialData)
|
|
if err := cont.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start container: %v", err)
|
|
}
|
|
postStartData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after starting container: %v", err)
|
|
}
|
|
postStartOpens, postStartTimestamp, err := postStartData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
Pod: "foopod",
|
|
Namespace: "foons",
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens from following data (err: %v):\n\n%s\n\n", err, postStartData)
|
|
}
|
|
t.Logf("After container start, fs_opens=%d (snapshotted at %v)", postStartOpens, postStartTimestamp)
|
|
// The touch operation may fail from permission errors, but the metric should still be incremented.
|
|
shOutput, err := executeCombinedOutput(te.sleepConf, cont, nil, "/bin/bash", "-c", fmt.Sprintf("for i in $(seq 1 %d); do touch /tmp/$i || true; done", targetOpens))
|
|
if err != nil {
|
|
t.Fatalf("Exec failed: %v; output: %v", err, shOutput)
|
|
}
|
|
postExecData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after a bunch of open() calls: %v", err)
|
|
}
|
|
postExecOpens, postExecTimestamp, err := postExecData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
Pod: "foopod",
|
|
Namespace: "foons",
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens from following data (err: %v):\n\n%s\n\n", err, postExecData)
|
|
}
|
|
t.Logf("After exec'ing %d open()s, fs_opens=%d (snapshotted at %v)", targetOpens, postExecOpens, postExecTimestamp)
|
|
diffOpens := postExecOpens - postStartOpens
|
|
if diffOpens < int64(targetOpens) {
|
|
t.Errorf("testmetric_fs_opens went from %d to %d (diff: %d), expected the difference to be at least %d", postStartOpens, postExecOpens, diffOpens, targetOpens)
|
|
}
|
|
}
|
|
|
|
// TestContainerMetricsIterationID verifies that two successive containers with the same ID
|
|
// do not have the same iteration ID.
|
|
func TestContainerMetricsIterationID(t *testing.T) {
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, false)
|
|
defer cleanup()
|
|
|
|
args := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont1, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container 1: %v", err)
|
|
}
|
|
defer cont1.Destroy()
|
|
data1, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("Cannot get metrics after creating container 1: %v", err)
|
|
}
|
|
metadata1, err := data1.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox 1 metadata: %v", err)
|
|
}
|
|
t.Logf("Container 1 metadata: %v", metadata1)
|
|
iterationID1 := metadata1["iteration"]
|
|
if iterationID1 == "" {
|
|
t.Fatalf("Cannot find iteration ID in metadata 1: %v", metadata1)
|
|
}
|
|
if err := cont1.Destroy(); err != nil && !strings.Contains(err.Error(), "no child process") {
|
|
t.Fatalf("Cannot destroy container 1: %v", err)
|
|
}
|
|
cont2, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container 2: %v", err)
|
|
}
|
|
defer cont2.Destroy()
|
|
data2, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("Cannot get metrics after creating container 2: %v", err)
|
|
}
|
|
metadata2, err := data2.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox 2 metadata: %v", err)
|
|
}
|
|
t.Logf("Container 2 metadata: %v", metadata2)
|
|
iterationID2 := metadata2["iteration"]
|
|
if iterationID2 == "" {
|
|
t.Fatalf("Cannot find iteration ID in metadata 2: %v", metadata2)
|
|
}
|
|
if iterationID1 == iterationID2 {
|
|
t.Errorf("Iteration IDs of successive instances with the same ID unexpectedly matched: %v", iterationID1)
|
|
}
|
|
}
|
|
|
|
// TestContainerMetricsRobustAgainstRestarts that exporting metrics is robust against metric server
|
|
// unavailability or restarts.
|
|
func TestContainerMetricsRobustAgainstRestarts(t *testing.T) {
|
|
targetOpens := 200
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, false)
|
|
defer cleanup()
|
|
|
|
// First, start a container which will kick off the metric server as normal.
|
|
args := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container: %v", err)
|
|
}
|
|
defer cont.Destroy()
|
|
if err := cont.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start container: %v", err)
|
|
}
|
|
shOutput, err := executeCombinedOutput(te.sleepConf, cont, nil, "/bin/bash", "-c", fmt.Sprintf("for i in $(seq 1 %d); do touch /tmp/$i || true; done", targetOpens))
|
|
if err != nil {
|
|
t.Fatalf("Exec failed: %v; output: %v", err, shOutput)
|
|
}
|
|
preRestartData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after a bunch of open() calls: %v", err)
|
|
}
|
|
|
|
// Retain the value of fs_opens for the first container. We'll use it when comparing to the data
|
|
// from the restarted metric server.
|
|
preRestartOpens, postExecTimestamp, err := preRestartData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens from following data (err: %v):\n\n%s\n\n", err, preRestartData)
|
|
}
|
|
preRestartMetadata, err := preRestartData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata: %v", err)
|
|
}
|
|
t.Logf("After exec'ing %d open()s, fs_opens=%d (snapshotted at %v)", targetOpens, preRestartOpens, postExecTimestamp)
|
|
|
|
// Now shut down the metric server and verify we can no longer fetch metrics.
|
|
if err := te.client.ShutdownServer(te.testCtx); err != nil {
|
|
t.Fatalf("Cannot shutdown server: %v", err)
|
|
}
|
|
if rawData, err := te.client.GetMetrics(te.testCtx, nil); err == nil {
|
|
t.Fatalf("Unexpectedly was able to get metric data despite shutting down server:\n\n%s\n\n", rawData)
|
|
}
|
|
|
|
// Do a bunch of touches again. The metric server is down during this time.
|
|
// This verifies that metric value modifications does not depend on the metric server being up.
|
|
shOutput, err = executeCombinedOutput(te.sleepConf, cont, nil, "/bin/bash", "-c", fmt.Sprintf("for i in $(seq 1 %d); do touch /tmp/$i || true; done", targetOpens))
|
|
if err != nil {
|
|
t.Fatalf("Exec failed: %v; output: %v", err, shOutput)
|
|
}
|
|
|
|
// Start a second container.
|
|
// This container should be picked up by a metric server we will start afterwards.
|
|
// This verifies that a metric server being down does not cause sandbox creation to fail.
|
|
args2 := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont2, err := New(te.sleepConf, args2)
|
|
if err != nil {
|
|
t.Fatalf("error creating second container: %v", err)
|
|
}
|
|
defer cont2.Destroy()
|
|
if rawData, err := te.client.GetMetrics(te.testCtx, nil); err == nil {
|
|
t.Fatalf("Unexpectedly was able to get metric data after creating second container:\n\n%s\n\n", rawData)
|
|
}
|
|
if err := cont2.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start second container: %v", err)
|
|
}
|
|
if rawData, err := te.client.GetMetrics(te.testCtx, nil); err == nil {
|
|
t.Fatalf("Unexpectedly was able to get metric data after starting second container:\n\n%s\n\n", rawData)
|
|
}
|
|
|
|
// Start the metric server.
|
|
if err := te.client.SpawnServer(te.testCtx, te.sleepConf, te.serverExtraArgs...); err != nil {
|
|
t.Fatalf("Cannot re-spawn server: %v", err)
|
|
}
|
|
|
|
// Now start a third container.
|
|
// This should be picked up by the server we just started.
|
|
args3 := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont3, err := New(te.sleepConf, args3)
|
|
if err != nil {
|
|
t.Fatalf("error creating second container: %v", err)
|
|
}
|
|
defer cont3.Destroy()
|
|
if err := cont3.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start third container: %v", err)
|
|
}
|
|
|
|
// Verify that the metric server was restarted and that we can indeed get all the data we expect
|
|
// from all the containers this test has started.
|
|
postRestartData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after restarting server: %v", err)
|
|
}
|
|
postRestartOpens, _, err := postRestartData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get testmetric_fs_opens for first container (%s) from following data (err: %v):\n\n%s\n\n", args.ID, err, postRestartData)
|
|
}
|
|
if diff := postRestartOpens - preRestartOpens; diff < int64(targetOpens) {
|
|
t.Errorf("testmetric_fs_opens for first container did not increase by at least %d after metric server restart: went from %d to %d (diff: %d)", targetOpens, preRestartOpens, postRestartOpens, diff)
|
|
}
|
|
postRestartMetadata, err := postRestartData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get post-restart sandbox metadata: %v", err)
|
|
}
|
|
if diff := cmp.Diff(preRestartMetadata, postRestartMetadata); diff != "" {
|
|
t.Errorf("Sandbox metadata changed after restart:\nBefore: %v\nAfter: %v\nDiff: %v", preRestartMetadata, postRestartMetadata, diff)
|
|
}
|
|
_, _, err = postRestartData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args2.ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get testmetric_fs_opens for second container (%s) from following data (err: %v):\n\n%s\n\n", args2.ID, err, postRestartData)
|
|
}
|
|
_, _, err = postRestartData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args3.ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get testmetric_fs_opens for third container (%s) from following data (err: %v):\n\n%s\n\n", args3.ID, err, postRestartData)
|
|
}
|
|
}
|
|
|
|
// TestContainerMetricsMultiple verifies that the metric server spawned for one container
|
|
// serves metrics for all containers, and survives past its initial container's lifetime.
|
|
func TestContainerMetricsMultiple(t *testing.T) {
|
|
numConcurrentContainers := 5
|
|
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, false)
|
|
defer cleanup()
|
|
var containers []*Container
|
|
needCleanup := map[*Container]struct{}{}
|
|
toDestroy := map[*Container]struct{}{}
|
|
defer func() {
|
|
for container := range needCleanup {
|
|
container.Destroy()
|
|
}
|
|
}()
|
|
|
|
// Start a bunch of containers with metrics.
|
|
for i := 0; i < numConcurrentContainers; i++ {
|
|
cont, err := New(te.sleepConf, Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error creating container: %v", err)
|
|
}
|
|
containers = append(containers, cont)
|
|
needCleanup[cont] = struct{}{}
|
|
// Note that this includes the first container, which will be the one that
|
|
// starts the metrics server.
|
|
if i%2 == 0 {
|
|
toDestroy[cont] = struct{}{}
|
|
}
|
|
if err := cont.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start container: %v", err)
|
|
}
|
|
}
|
|
|
|
// Start one container with metrics turned off.
|
|
sleepConfNoMetrics := *te.sleepConf
|
|
sleepConfNoMetrics.MetricServer = ""
|
|
noMetricsCont, err := New(&sleepConfNoMetrics, Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error creating no-metrics container: %v", err)
|
|
}
|
|
defer noMetricsCont.Destroy()
|
|
|
|
// Verify that the metrics server says what we expect.
|
|
gotData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after starting containers: %v", err)
|
|
}
|
|
t.Logf("Metrics after starting all containers:\n\n%s\n\n", gotData)
|
|
for _, container := range containers {
|
|
if _, _, err := gotData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: container.ID,
|
|
}); err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens for container %s: %v", container.ID, err)
|
|
}
|
|
}
|
|
if val, _, err := gotData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: noMetricsCont.ID,
|
|
}); err == nil {
|
|
t.Errorf("Unexpectedly found testmetric_fs_opens metric data for no-metrics container %s: %v", noMetricsCont.ID, val)
|
|
}
|
|
|
|
// Stop every other container.
|
|
for container := range toDestroy {
|
|
if err := container.Destroy(); err != nil {
|
|
t.Logf("Warning: cannot destroy container %s: %v", container.ID, err)
|
|
continue
|
|
}
|
|
delete(needCleanup, container)
|
|
}
|
|
|
|
// Verify that now we only have half the containers.
|
|
gotData, err = te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after stopping half the containers: %v", err)
|
|
}
|
|
t.Logf("Metrics after stopping half the containers:\n\n%s\n\n", gotData)
|
|
for _, container := range containers {
|
|
val, _, err := gotData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: container.ID,
|
|
})
|
|
_, wantErr := toDestroy[container]
|
|
if gotErr := err != nil; gotErr && !wantErr {
|
|
t.Errorf("Wanted to find data for container %s but didn't: %v", container.ID, err)
|
|
} else if !gotErr && wantErr {
|
|
t.Errorf("Wanted to find no data for container %s but found this value instead: %v", container.ID, val)
|
|
}
|
|
}
|
|
if val, _, err := gotData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: noMetricsCont.ID,
|
|
}); err == nil {
|
|
t.Errorf("Unexpectedly found testmetric_fs_opens metric data for no-metrics container %s: %v", noMetricsCont.ID, val)
|
|
}
|
|
}
|
|
|
|
// TestContainerMetricsFilter verifies the ability to filter metrics in /metrics requests.
|
|
func TestContainerMetricsFilter(t *testing.T) {
|
|
te, cleanup := setupMetrics(t, false /* forceTempUDS */)
|
|
defer cleanup()
|
|
|
|
args := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container: %v", err)
|
|
}
|
|
defer cont.Destroy()
|
|
if err := cont.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start container: %v", err)
|
|
}
|
|
|
|
// First pass: Unfiltered data.
|
|
unfilteredData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
_, _, err = unfilteredData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens: %v", err)
|
|
}
|
|
_, err = unfilteredData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata: %v", err)
|
|
}
|
|
|
|
// Second pass: Filter such that fs_opens does not match.
|
|
filteredData, err := te.client.GetMetrics(te.testCtx, map[string]string{
|
|
"runsc-sandbox-metrics-filter": "^$", // Matches nothing.
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
_, _, err = filteredData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err == nil {
|
|
t.Errorf("Was unexpectedly able to get fs_opens data from filtered data:\n\n%v\n\n", filteredData)
|
|
}
|
|
_, err = filteredData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata from filtered data: %v", err)
|
|
}
|
|
|
|
// Third pass: Filter such that fs_opens does match.
|
|
filteredData2, err := te.client.GetMetrics(te.testCtx, map[string]string{
|
|
"runsc-sandbox-metrics-filter": "^fs_.*$",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
_, _, err = filteredData2.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens from filtered data: %v", err)
|
|
}
|
|
_, err = filteredData2.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata from filtered data: %v", err)
|
|
}
|
|
|
|
// Fourth pass: Filter such that fs_opens does not match, then request with no filtering,
|
|
// to ensure that the filter regex caching is correctly applied.
|
|
_, err = te.client.GetMetrics(te.testCtx, map[string]string{
|
|
"runsc-sandbox-metrics-filter": "^$",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
unfilteredData2, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
_, _, err = unfilteredData2.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_fs_opens from unfiltered data: %v", err)
|
|
}
|
|
_, err = unfilteredData2.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata from unfiltered data: %v", err)
|
|
}
|
|
|
|
// Fifth pass: Use alternate URL encoding to mimic Prometheus's URL-encoding
|
|
// behavior.
|
|
alternatePathData, err := te.client.GetMetrics(te.testCtx, map[string]string{
|
|
// Encoded version of "/metrics?runsc-sandbox-metrics-filter=^$", this should match nothing.
|
|
"": "/metrics%3Frunsc-sandbox-metrics-filter=%5E%24",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
_, err = alternatePathData.GetSandboxMetadataMetric(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_metadata",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get sandbox metadata from data obtained from alternate path: %v\n\nData:\n\n%v\n\n", err, alternatePathData)
|
|
}
|
|
_, _, err = alternatePathData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_fs_opens",
|
|
Sandbox: args.ID,
|
|
})
|
|
if err == nil {
|
|
t.Errorf("Was unexpectedly able to get testmetric_fs_opens from data obtained from alternate path which was supposed to filter it out:\n\n%v\n\n", alternatePathData)
|
|
}
|
|
}
|
|
|
|
// TestContainerCapabilityFilter verifies the ability to filter capabilities in /metrics requests.
|
|
func TestContainerCapabilityFilter(t *testing.T) {
|
|
te, cleanup := setupMetrics(t, false /* forceTempUDS */)
|
|
defer cleanup()
|
|
te.sleepSpec.Process.Capabilities.Bounding = append(
|
|
te.sleepSpec.Process.Capabilities.Bounding,
|
|
linux.CAP_SYS_NICE.String(),
|
|
linux.CAP_NET_RAW.String())
|
|
|
|
args := Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container: %v", err)
|
|
}
|
|
defer cont.Destroy()
|
|
if err := cont.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start container: %v", err)
|
|
}
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
filter string
|
|
want map[linux.Capability]bool
|
|
}{
|
|
{
|
|
name: "unfiltered",
|
|
filter: "",
|
|
want: map[linux.Capability]bool{linux.CAP_SYS_NICE: true, linux.CAP_NET_RAW: true},
|
|
},
|
|
{
|
|
name: "all filtered out",
|
|
filter: "^$",
|
|
want: map[linux.Capability]bool{linux.CAP_SYS_NICE: false, linux.CAP_NET_RAW: false},
|
|
},
|
|
{
|
|
name: "simple filter with prefix",
|
|
filter: fmt.Sprintf("^%s$", linux.CAP_SYS_NICE.String()),
|
|
want: map[linux.Capability]bool{linux.CAP_SYS_NICE: true, linux.CAP_NET_RAW: false},
|
|
},
|
|
{
|
|
name: "simple filter without prefix",
|
|
filter: fmt.Sprintf("^%s$", linux.CAP_SYS_NICE.TrimmedString()),
|
|
want: map[linux.Capability]bool{linux.CAP_SYS_NICE: true, linux.CAP_NET_RAW: false},
|
|
},
|
|
{
|
|
name: "unfiltered again to test regexp caching",
|
|
filter: "",
|
|
want: map[linux.Capability]bool{linux.CAP_SYS_NICE: true, linux.CAP_NET_RAW: true},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var params map[string]string
|
|
if test.filter != "" {
|
|
params = map[string]string{
|
|
"runsc-capability-filter": test.filter,
|
|
}
|
|
}
|
|
data, err := te.client.GetMetrics(te.testCtx, params)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
for cap, want := range test.want {
|
|
got, _, err := data.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_capabilities",
|
|
Sandbox: args.ID,
|
|
ExtraLabels: map[string]string{"capability": cap.TrimmedString()},
|
|
})
|
|
if err != nil && want {
|
|
t.Errorf("Cannot get testmetric_meta_sandbox_capabilities[capability=%q]: %v", cap.TrimmedString(), err)
|
|
} else if err == nil && !want {
|
|
t.Errorf("Unexpectedly able to get testmetric_meta_sandbox_capabilities[capability=%q]: %v", cap.TrimmedString(), got)
|
|
}
|
|
}
|
|
if t.Failed() {
|
|
t.Logf("Metric data:\n\n%s\n\n", data)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMetricServerChecksRootDirectoryAccess(t *testing.T) {
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, false)
|
|
defer cleanup()
|
|
if err := te.client.ShutdownServer(te.testCtx); err != nil {
|
|
t.Fatalf("Cannot stop metric server: %v", err)
|
|
}
|
|
prevStat, err := os.Lstat(te.sleepConf.RootDir)
|
|
if err != nil {
|
|
t.Fatalf("cannot stat %q: %v", te.sleepConf.RootDir, err)
|
|
}
|
|
if err := os.Chmod(te.sleepConf.RootDir, 0); err != nil {
|
|
t.Fatalf("cannot chmod %q as 000: %v", te.sleepConf.RootDir, err)
|
|
}
|
|
defer os.Chmod(te.sleepConf.RootDir, prevStat.Mode())
|
|
if _, err := os.ReadDir(te.sleepConf.RootDir); err == nil {
|
|
t.Logf("Can still read directory %v despite chmodding it to 0. Maybe we are running as root? Skipping test.", te.sleepConf.RootDir)
|
|
return
|
|
}
|
|
shorterCtx, shorterCtxCancel := context.WithTimeout(te.testCtx, time.Second)
|
|
defer shorterCtxCancel()
|
|
if err := te.client.SpawnServer(shorterCtx, te.sleepConf, te.serverExtraArgs...); err == nil {
|
|
t.Error("Metric server was successfully able to be spawned despite not having access to the root directory")
|
|
}
|
|
}
|
|
|
|
func TestMetricServerToleratesNoRootDirectory(t *testing.T) {
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, true)
|
|
defer cleanup()
|
|
if err := te.client.ShutdownServer(te.testCtx); err != nil {
|
|
t.Fatalf("Cannot stop metric server: %v", err)
|
|
}
|
|
if err := os.RemoveAll(te.sleepConf.RootDir); err != nil {
|
|
t.Fatalf("cannot remove root directory %q: %v", te.sleepConf.RootDir, err)
|
|
}
|
|
shortCtx, shortCtxCancel := context.WithTimeout(te.testCtx, time.Second)
|
|
defer shortCtxCancel()
|
|
if err := te.client.SpawnServer(shortCtx, te.sleepConf, append([]string{"--allow-unknown-root=false"}, te.serverExtraArgs...)...); err == nil {
|
|
t.Fatalf("Metric server was successfully able to be spawned despite a non-existent root directory")
|
|
}
|
|
if err := te.client.SpawnServer(te.testCtx, te.sleepConf, append([]string{"--allow-unknown-root=true"}, te.serverExtraArgs...)...); err != nil {
|
|
t.Errorf("Metric server was not able to be spawned despite being configured to tolerate a non-existent root directory: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMetricServerDoesNotExportZeroValueCounters(t *testing.T) {
|
|
te, cleanup := setupMetrics(t, false /* forceTempUDS */)
|
|
defer cleanup()
|
|
app, err := testutil.FindFile("test/cmd/test_app/test_app")
|
|
if err != nil {
|
|
t.Fatalf("error finding test_app: %v", err)
|
|
}
|
|
unimpl1Spec := testutil.NewSpecWithArgs("sh", "-c", fmt.Sprintf("%s syscall --syscall=1337; sleep 1h", app))
|
|
unimpl1Conf := te.applyConf(testutil.TestConfig(t))
|
|
unimpl1Bundle, cleanup, err := testutil.SetupBundleDir(unimpl1Spec)
|
|
if err != nil {
|
|
t.Fatalf("error setting up container: %v", err)
|
|
}
|
|
defer cleanup()
|
|
unimpl2Spec := testutil.NewSpecWithArgs("sh", "-c", fmt.Sprintf("%s syscall --syscall=1338; sleep 1h", app))
|
|
unimpl2Conf := te.applyConf(testutil.TestConfig(t))
|
|
unimpl2Bundle, cleanup, err := testutil.SetupBundleDir(unimpl2Spec)
|
|
if err != nil {
|
|
t.Fatalf("error setting up container: %v", err)
|
|
}
|
|
defer cleanup()
|
|
unimpl1, err := New(unimpl1Conf, Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: unimpl1Spec,
|
|
BundleDir: unimpl1Bundle,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error creating first container: %v", err)
|
|
}
|
|
defer unimpl1.Destroy()
|
|
if err := unimpl1.Start(unimpl1Conf); err != nil {
|
|
t.Fatalf("Cannot start first container: %v", err)
|
|
}
|
|
unimpl2, err := New(unimpl2Conf, Args{
|
|
ID: testutil.RandomContainerID(),
|
|
Spec: unimpl2Spec,
|
|
BundleDir: unimpl2Bundle,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error creating second container: %v", err)
|
|
}
|
|
defer unimpl2.Destroy()
|
|
if err := unimpl2.Start(unimpl2Conf); err != nil {
|
|
t.Fatalf("Cannot start second container: %v", err)
|
|
}
|
|
metricData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
metricDataPtr := &metricData
|
|
|
|
// For this test to work, it must wait for long enough such that the containers have
|
|
// actually tried to call the unimplemented syscall so that it shows up in metrics.
|
|
waitCtx, waitCtxCancel := context.WithTimeout(te.testCtx, 50*time.Second)
|
|
defer waitCtxCancel()
|
|
|
|
for _, test := range []struct {
|
|
cont *Container
|
|
sysno uintptr
|
|
wantExistence bool
|
|
}{
|
|
{unimpl1, 1337, true},
|
|
{unimpl1, 1338, false},
|
|
{unimpl2, 1337, false},
|
|
{unimpl2, 1338, true},
|
|
} {
|
|
t.Run(fmt.Sprintf("container %s syscall %d", test.cont.ID, test.sysno), func(t *testing.T) {
|
|
check := func() error {
|
|
got, _, err := metricDataPtr.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_unimplemented_syscalls",
|
|
Sandbox: test.cont.sandboxID(),
|
|
ExtraLabels: map[string]string{"sysno": strconv.Itoa(int(test.sysno))},
|
|
})
|
|
if test.wantExistence {
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get unimplemented syscall metric for sysno=%d even though we expected its presence: %v", test.sysno, err)
|
|
}
|
|
if got != 1 {
|
|
return fmt.Errorf("expected counter value for unimplemented syscall %d be exactly 1, got %d", test.sysno, got)
|
|
}
|
|
} else /* !test.wantExistence */ {
|
|
if err == nil {
|
|
return fmt.Errorf("unimplemented syscall metric for sysno=%d was unexpectedly present (value: %d)", test.sysno, got)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
for waitCtx.Err() == nil {
|
|
if check() == nil {
|
|
break
|
|
}
|
|
select {
|
|
case <-time.After(20 * time.Millisecond):
|
|
newMetricData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics: %v", err)
|
|
}
|
|
*metricDataPtr = newMetricData
|
|
case <-waitCtx.Done():
|
|
}
|
|
}
|
|
if err := check(); err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
})
|
|
}
|
|
if t.Failed() {
|
|
t.Logf("Last metric data:\n\n%s\n\n", metricData)
|
|
}
|
|
}
|
|
|
|
func TestContainerMetricsAfterRestore(t *testing.T) {
|
|
te, cleanup := setupMetrics(t /* forceTempUDS= */, false)
|
|
defer cleanup()
|
|
|
|
imagePath, err := os.MkdirTemp(testutil.TmpDir(), "checkpoint")
|
|
if err != nil {
|
|
t.Fatalf("os.MkdirTemp failed: %v", err)
|
|
}
|
|
defer os.RemoveAll(imagePath)
|
|
|
|
containerID := testutil.RandomContainerID()
|
|
args := Args{
|
|
ID: containerID,
|
|
Spec: te.sleepSpec,
|
|
BundleDir: te.bundleDir,
|
|
}
|
|
cont, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container: %v", err)
|
|
}
|
|
|
|
if err := cont.Start(te.sleepConf); err != nil {
|
|
t.Fatalf("Cannot start container: %v", err)
|
|
}
|
|
|
|
t.Logf("Running ps to consume some resources...")
|
|
if output, err := executeCombinedOutput(te.sleepConf, cont, nil, "/bin/bash", "-c", "for i in $(seq 1 100); do ps aux > /dev/null; done"); err != nil {
|
|
t.Fatalf("Exec of ps failed: %v; output: %v", err, output)
|
|
}
|
|
t.Logf("Finished running ps.")
|
|
|
|
if err := cont.Checkpoint(te.sleepConf, imagePath, sandbox.CheckpointOpts{Resume: true}); err != nil {
|
|
t.Fatalf("Checkpoint failed: %v", err)
|
|
}
|
|
|
|
// Check time saved metrics are zero before restore.
|
|
ckpData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
t.Fatalf("Cannot get metrics after restoring container: %v", err)
|
|
}
|
|
cpuTimeSavedBefore, _, err := ckpData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_cpu_time_saved_milliseconds",
|
|
Sandbox: containerID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_meta_sandbox_cpu_time_saved_milliseconds from following data (err: %v):\n\n%s\n\n", err, ckpData)
|
|
}
|
|
if cpuTimeSavedBefore != 0 {
|
|
t.Errorf("testmetric_meta_sandbox_cpu_time_saved_milliseconds should be zero, got %v", cpuTimeSavedBefore)
|
|
}
|
|
wallTimeSavedBefore, _, err := ckpData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_wall_time_saved_milliseconds",
|
|
Sandbox: containerID,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Cannot get testmetric_meta_sandbox_wall_time_saved_milliseconds from following data (err: %v):\n\n%s\n\n", err, ckpData)
|
|
}
|
|
if wallTimeSavedBefore != 0 {
|
|
t.Errorf("testmetric_meta_sandbox_wall_time_saved_milliseconds should be zero, got %v", wallTimeSavedBefore)
|
|
}
|
|
|
|
if err := cont.Destroy(); err != nil {
|
|
t.Fatalf("Destroy failed: %v", err)
|
|
}
|
|
|
|
// Create new container to restore into.
|
|
restoreCont, err := New(te.sleepConf, args)
|
|
if err != nil {
|
|
t.Fatalf("error creating container for restore: %v", err)
|
|
}
|
|
defer restoreCont.Destroy()
|
|
|
|
if err := restoreCont.Restore(te.sleepConf, imagePath, false, false, nil); err != nil {
|
|
t.Fatalf("Restore failed: %v", err)
|
|
}
|
|
|
|
var cpuTimeSaved, wallTimeSaved int64
|
|
err = testutil.Poll(func() error {
|
|
restoreData, err := te.client.GetMetrics(te.testCtx, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get metrics after restoring container: %v", err)
|
|
}
|
|
|
|
// Check time saved metrics are non-zero after restore.
|
|
cpuTimeSaved, _, err = restoreData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_cpu_time_saved_milliseconds",
|
|
Sandbox: containerID,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get testmetric_meta_sandbox_cpu_time_saved_milliseconds from following data (err: %v):\n%s", err, restoreData)
|
|
}
|
|
wallTimeSaved, _, err = restoreData.GetPrometheusContainerInteger(metricclient.WantMetric{
|
|
Metric: "testmetric_meta_sandbox_wall_time_saved_milliseconds",
|
|
Sandbox: containerID,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get testmetric_meta_sandbox_wall_time_saved_milliseconds from following data (err: %v):\n%s", err, restoreData)
|
|
}
|
|
if cpuTimeSaved == 0 || wallTimeSaved == 0 {
|
|
return fmt.Errorf("time saved metrics are zero: cpu: %d, wall: %d", cpuTimeSaved, wallTimeSaved)
|
|
}
|
|
return nil
|
|
}, 500*time.Millisecond)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get non-zero time saved metrics: %v", err)
|
|
}
|
|
t.Logf("After restore, cpu_time_saved_milliseconds=%d", cpuTimeSaved)
|
|
t.Logf("After restore, wall_time_saved_milliseconds=%d", wallTimeSaved)
|
|
}
|