caching/vendor/knative.dev/pkg/test/mako/sidecar.go

189 lines
5.8 KiB
Go

/*
Copyright 2019 The Knative 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 mako
import (
"context"
"fmt"
"log"
"path/filepath"
"runtime"
"strings"
"cloud.google.com/go/compute/metadata"
kubeclient "knative.dev/pkg/client/injection/kube/client"
"github.com/google/mako/go/quickstore"
qpb "github.com/google/mako/proto/quickstore/quickstore_go_proto"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"knative.dev/pkg/changeset"
"knative.dev/pkg/controller"
"knative.dev/pkg/injection"
"knative.dev/pkg/test/mako/alerter"
"knative.dev/pkg/test/mako/config"
)
const (
// sidecarAddress is the address of the Mako sidecar to which we locally
// write results, and it authenticates and publishes them to Mako after
// assorted preprocessing.
sidecarAddress = "localhost:9813"
// org is the orgnization name that is used by Github client
org = "knative"
// slackUserName is the slack user name that is used by Slack client
slackUserName = "Knative Testgrid Robot"
// These token settings are for alerter.
// If we want to enable the alerter for a benchmark, we need to mount the
// token to the pod, with the same name and path.
// See https://github.com/knative/serving/blob/master/test/performance/benchmarks/dataplane-probe/continuous/dataplane-probe.yaml
tokenFolder = "/var/secret"
githubToken = "github-token"
slackReadToken = "slack-read-token"
slackWriteToken = "slack-write-token"
)
// Client is a wrapper that wraps all Mako related operations
type Client struct {
Quickstore *quickstore.Quickstore
Context context.Context
ShutDownFunc func(context.Context)
benchmarkKey string
benchmarkName string
alerter *alerter.Alerter
}
// StoreAndHandleResult stores the benchmarking data and handles the result.
func (c *Client) StoreAndHandleResult() error {
out, err := c.Quickstore.Store()
return c.alerter.HandleBenchmarkResult(c.benchmarkKey, c.benchmarkName, out, err)
}
var tagEscaper = strings.NewReplacer("+", "-", "\t", "_", " ", "_")
// EscapeTag replaces characters that Mako doesn't accept with ones it does.
func EscapeTag(tag string) string {
return tagEscaper.Replace(tag)
}
// SetupHelper sets up the mako client for the provided benchmarkKey.
// It will add a few common tags and allow each benchmark to add custm tags as well.
// It returns the mako client handle to store metrics, a method to close the connection
// to mako server once done and error in case of failures.
func SetupHelper(ctx context.Context, benchmarkKey *string, benchmarkName *string, extraTags ...string) (*Client, error) {
tags := append(config.MustGetTags(), extraTags...)
// Get the commit of the benchmarks
commitID, err := changeset.Get()
if err != nil {
return nil, err
}
// Setup a deployment informer, so that we can use the lister to track
// desired and available pod counts.
cfg, err := rest.InClusterConfig()
if err != nil {
return nil, err
}
ctx, informers := injection.Default.SetupInformers(ctx, cfg)
if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
return nil, err
}
// Get the Kubernetes version from the API server.
kc := kubeclient.Get(ctx)
version, err := kc.Discovery().ServerVersion()
if err != nil {
return nil, err
}
// Determine the number of Kubernetes nodes through the kubernetes client.
nodes, err := kc.CoreV1().Nodes().List(metav1.ListOptions{})
if err != nil {
return nil, err
}
tags = append(tags, "nodes="+fmt.Sprintf("%d", len(nodes.Items)))
// Decorate GCP metadata as tags (when we're running on GCP).
if projectID, err := metadata.ProjectID(); err != nil {
log.Printf("GCP project ID is not available: %v", err)
} else {
tags = append(tags, "project-id="+EscapeTag(projectID))
}
if zone, err := metadata.Zone(); err != nil {
log.Printf("GCP zone is not available: %v", err)
} else {
tags = append(tags, "zone="+EscapeTag(zone))
}
if machineType, err := metadata.Get("instance/machine-type"); err != nil {
log.Printf("GCP machine type is not available: %v", err)
} else if parts := strings.Split(machineType, "/"); len(parts) != 4 {
tags = append(tags, "instanceType="+EscapeTag(parts[3]))
}
tags = append(tags,
"commit="+commitID,
"kubernetes="+EscapeTag(version.String()),
"goversion="+EscapeTag(runtime.Version()),
)
log.Printf("The tags for this run are: %+v", tags)
// Create a new Quickstore that connects to the microservice
qs, qclose, err := quickstore.NewAtAddress(ctx, &qpb.QuickstoreInput{
BenchmarkKey: benchmarkKey,
Tags: tags,
}, sidecarAddress)
if err != nil {
return nil, err
}
// Create a new Alerter that alerts for performance regressions
alerter := &alerter.Alerter{}
alerter.SetupGitHub(
org,
config.GetRepository(),
tokenPath(githubToken),
)
alerter.SetupSlack(
slackUserName,
tokenPath(slackReadToken),
tokenPath(slackWriteToken),
config.GetSlackChannels(*benchmarkName),
)
client := &Client{
Quickstore: qs,
Context: ctx,
ShutDownFunc: qclose,
alerter: alerter,
benchmarkKey: *benchmarkKey,
benchmarkName: *benchmarkName,
}
return client, nil
}
func Setup(ctx context.Context, extraTags ...string) (*Client, error) {
bench := config.MustGetBenchmark()
return SetupHelper(ctx, bench.BenchmarkKey, bench.BenchmarkName, extraTags...)
}
func tokenPath(token string) string {
return filepath.Join(tokenFolder, token)
}