Skip to content

Commit

Permalink
feat(e2e): use a fake metric service for e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
TheSpiritXIII committed May 30, 2024
1 parent 42f1ac3 commit e3051fd
Show file tree
Hide file tree
Showing 12 changed files with 1,345 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ endif
ifeq ($(KIND_PERSIST), 1)
E2E_DOCKER_ARGS += --env KIND_PERSIST=1
endif
E2E_DEPS:=config-reloader operator rule-evaluator go-synthetic
E2E_DEPS:=config-reloader operator rule-evaluator go-synthetic fake-metric-service
REGISTRY_NAME=kind-registry
REGISTRY_PORT=5001
KIND_PARALLEL?=5
Expand Down
29 changes: 29 additions & 0 deletions cmd/fake-metric-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2024 Google LLC
#
# 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.

FROM google-go.pkg.dev/golang:1.22.3@sha256:efc6b824652199ede8bc25e2d5a57076e0e1ffbe90735dcbda3ee956fe33b612 as buildbase
WORKDIR /app
COPY go.mod go.mod
COPY go.sum go.sum
COPY cmd cmd
COPY pkg pkg
COPY vendor vendor

FROM buildbase as appbase
RUN CGO_ENABLED=1 GOEXPERIMENT=boringcrypto \
go build -tags boring -mod=vendor -o fake-metric-service cmd/fake-metric-service/*.go

FROM gke.gcr.io/gke-distroless/libc:gke_distroless_20240307.00_p0@sha256:4f834e207f2721977094aeec4c9daee7032c5daec2083c0be97760f4306e4f88
COPY --from=appbase /app/fake-metric-service /bin/fake-metric-service
ENTRYPOINT ["/bin/fake-metric-service"]
123 changes: 123 additions & 0 deletions cmd/fake-metric-service/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2024 Google LLC
//
// 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
//
// https://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 main

import (
"context"
"flag"
"net"
"net/http"
"os"
"sync"

"cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
"github.com/GoogleCloudPlatform/prometheus-engine/pkg/e2e"
"github.com/go-logr/logr"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
)

func main() {
logVerbosity := 0
probeAddr := ":8080"
metricServiceAddr := ":8081"

flag.IntVar(&logVerbosity, "v", logVerbosity, "Logging verbosity")
flag.StringVar(&probeAddr, "probe-addr", probeAddr, "Address to outputs probe statuses (e.g. /readyz and /livez)")
flag.StringVar(&metricServiceAddr, "metric-service-addr", metricServiceAddr, "Address to serve a mock metric service server.")
flag.Parse()

logger := zap.New(zap.Level(zapcore.Level(-logVerbosity)))
ctrl.SetLogger(logger)

ctx := signals.SetupSignalHandler()
if err := run(ctx, logger, probeAddr, metricServiceAddr); err != nil {
logger.Error(err, "exit with error")
os.Exit(1)
}
}

func run(ctx context.Context, logger logr.Logger, probeAddr, metricServiceAddr string) error {
listener, err := net.Listen("tcp", metricServiceAddr)
if err != nil {
return err
}

wg := sync.WaitGroup{}
errs := make(chan error, 1)

logger.Info("starting server...")

{
s := grpc.NewServer()
monitoringpb.RegisterMetricServiceServer(s, e2e.NewFakeMetricServer())

wg.Add(1)
go func() {
for range ctx.Done() {
s.GracefulStop()
return
}
}()
go func() {
defer wg.Done()
if err := s.Serve(listener); err != nil {
errs <- err
}
}()
}

{
wg.Add(1)
mux := http.NewServeMux()
mux.Handle("/readyz", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
mux.Handle("/livez", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
server := http.Server{
Addr: probeAddr,
Handler: mux,
}
go func() {
for range ctx.Done() {
// Start new context because ours is done.
if err := server.Shutdown(context.Background()); err != nil {
errs <- err
}
}
}()
go func() {
defer wg.Done()
if err := server.ListenAndServe(); err != nil {
errs <- err
}
}()
}

go func() {
wg.Wait()
close(errs)
}()

for err := range errs {
return err
}
return nil
}
17 changes: 5 additions & 12 deletions e2e/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"testing"
"time"

gcm "cloud.google.com/go/monitoring/apiv3/v2"
gcmpb "cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
"github.com/GoogleCloudPlatform/prometheus-engine/e2e/kube"
monitoringv1 "github.com/GoogleCloudPlatform/prometheus-engine/pkg/operator/apis/monitoring/v1"
Expand Down Expand Up @@ -79,9 +78,7 @@ func TestCollectorPodMonitoring(t *testing.T) {
},
}
t.Run("self-podmonitoring-ready", testEnsurePodMonitoringReady(ctx, kubeClient, pm))
if !skipGCM {
t.Run("self-podmonitoring-gcm", testValidateCollectorUpMetrics(ctx, kubeClient, "collector-podmon"))
}
t.Run("self-podmonitoring-gcm", testValidateCollectorUpMetrics(ctx, kubeClient, "collector-podmon"))
}

func TestCollectorClusterPodMonitoring(t *testing.T) {
Expand Down Expand Up @@ -120,9 +117,7 @@ func TestCollectorClusterPodMonitoring(t *testing.T) {
},
}
t.Run("self-clusterpodmonitoring-ready", testEnsureClusterPodMonitoringReady(ctx, kubeClient, cpm))
if !skipGCM {
t.Run("self-clusterpodmonitoring-gcm", testValidateCollectorUpMetrics(ctx, kubeClient, "collector-cmon"))
}
t.Run("self-clusterpodmonitoring-gcm", testValidateCollectorUpMetrics(ctx, kubeClient, "collector-cmon"))
}

func TestCollectorKubeletScraping(t *testing.T) {
Expand All @@ -138,9 +133,7 @@ func TestCollectorKubeletScraping(t *testing.T) {
t.Run("collector-operatorconfig", testCollectorOperatorConfig(ctx, kubeClient))

t.Run("enable-kubelet-scraping", testEnableKubeletScraping(ctx, kubeClient))
if !skipGCM {
t.Run("scrape-kubelet", testCollectorScrapeKubelet(ctx, kubeClient))
}
t.Run("scrape-kubelet", testCollectorScrapeKubelet(ctx, kubeClient))
}

// testCollectorDeployed does a high-level verification on whether the
Expand Down Expand Up @@ -445,7 +438,7 @@ func testValidateCollectorUpMetrics(ctx context.Context, kubeClient client.Clien
t.Log("checking for metrics in Cloud Monitoring")

// Wait for metric data to show up in Cloud Monitoring.
metricClient, err := gcm.NewMetricClient(ctx)
metricClient, err := newMetricClient(ctx)
if err != nil {
t.Fatalf("create metric client: %s", err)
}
Expand Down Expand Up @@ -524,7 +517,7 @@ func testCollectorScrapeKubelet(ctx context.Context, kubeClient client.Client) f
t.Log("checking for metrics in Cloud Monitoring")

// Wait for metric data to show up in Cloud Monitoring.
metricClient, err := gcm.NewMetricClient(ctx)
metricClient, err := newMetricClient(ctx)
if err != nil {
t.Fatalf("create GCM metric client: %s", err)
}
Expand Down
30 changes: 21 additions & 9 deletions e2e/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type deployOptions struct {
cluster string
location string
disableGCM bool
gcmEndpoint string
}

func (opts *deployOptions) setDefaults() {
Expand Down Expand Up @@ -92,6 +93,12 @@ func WithDisableGCM(disableGCM bool) DeployOption {
}
}

func WithGCMService(gcmService string) DeployOption {
return func(opts *deployOptions) {
opts.gcmEndpoint = gcmService
}
}

func createResources(ctx context.Context, kubeClient client.Client, normalizeFn func(client.Object) (client.Object, error)) error {
resources, err := resources(kubeClient.Scheme())
if err != nil {
Expand Down Expand Up @@ -130,16 +137,18 @@ func resources(scheme *runtime.Scheme) ([]client.Object, error) {
}

func normalizeDeamonSets(opts *deployOptions, obj *appsv1.DaemonSet) (client.Object, error) {
if !opts.disableGCM {
return obj, nil
}
if obj.GetName() != operator.NameCollector {
return obj, nil
}
for i := range obj.Spec.Template.Spec.Containers {
container := &obj.Spec.Template.Spec.Containers[i]
if container.Name == operator.CollectorPrometheusContainerName {
container.Args = append(container.Args, "--export.debug.disable-auth")
if opts.disableGCM || opts.gcmEndpoint != "" {
container.Args = append(container.Args, "--export.debug.disable-auth")
}
if opts.gcmEndpoint != "" {
container.Args = append(container.Args, fmt.Sprintf("--export.endpoint=%s", opts.gcmEndpoint))
}
return obj, nil
}
}
Expand Down Expand Up @@ -169,15 +178,18 @@ func normalizeDeployments(opts *deployOptions, obj *appsv1.Deployment) (client.O
container.Args = append(container.Args, fmt.Sprintf("--public-namespace=%s", opts.publicNamespace))
}
case operator.NameRuleEvaluator:
if !opts.disableGCM {
break
}
container, err := kube.DeploymentContainer(obj, operator.RuleEvaluatorContainerName)
if err != nil {
return nil, fmt.Errorf("unable to find rule-evaluator %q container: %w", operator.RuleEvaluatorContainerName, err)
}
container.Args = append(container.Args, "--export.debug.disable-auth")
container.Args = append(container.Args, "--query.debug.disable-auth")
if opts.disableGCM || opts.gcmEndpoint != "" {
container.Args = append(container.Args, "--export.debug.disable-auth")
container.Args = append(container.Args, "--query.debug.disable-auth")
}
if opts.gcmEndpoint != "" {
container.Args = append(container.Args, fmt.Sprintf("--export.endpoint=%s", opts.gcmEndpoint))
container.Args = append(container.Args, fmt.Sprintf("--query.target-url=%s", opts.gcmEndpoint))
}
}
return obj, nil
}
Expand Down
105 changes: 105 additions & 0 deletions e2e/deploy/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2024 Google LLC
//
// 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
//
// https://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 deploy

import (
"context"
"fmt"

"github.com/GoogleCloudPlatform/prometheus-engine/e2e/kube"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func CreateFakeMetricService(ctx context.Context, kubeClient client.Client, namespace, name, image string) error {
labels := map[string]string{
"app.kubernetes.io/name": "fake-metric-service",
}
if err := kubeClient.Create(ctx, &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "server",
Image: image,
Ports: []corev1.ContainerPort{
{
Name: "metric-service",
ContainerPort: 8081,
},
},
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Path: "livez",
Port: intstr.FromInt(8080),
Scheme: "HTTP",
},
},
},
ReadinessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Path: "readyz",
Port: intstr.FromInt(8080),
Scheme: "HTTP",
},
},
},
},
},
},
},
},
}); err != nil {
return fmt.Errorf("create metric-service deployment: %w", err)
}
if err := kubeClient.Create(ctx, &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: corev1.ServiceSpec{
Selector: labels,
Ports: []corev1.ServicePort{
{
Port: 8080,
TargetPort: intstr.FromString("metric-service"),
},
},
},
}); err != nil {
return fmt.Errorf("create metric-service service: %w", err)
}
return kube.WaitForDeploymentReady(ctx, kubeClient, namespace, name)
}

func FakeMetricServiceEndpoint() string {
return "metric-service.default.svc.cluster.local:8081"
}
Loading

0 comments on commit e3051fd

Please sign in to comment.