Skip to content

Commit

Permalink
[target-allocator] Addtl server unit tests (open-telemetry#1357)
Browse files Browse the repository at this point in the history
* add DiscoveryManager interface, ScrapeConfigsHandler tests

* add job handler tests

* add changelog entry

* make LinkJSON with keyed fields

* fix whitespace
  • Loading branch information
Kristina Pathak committed Jan 25, 2023
1 parent 4d970a4 commit 3f5f1f1
Show file tree
Hide file tree
Showing 6 changed files with 889 additions and 39 deletions.
16 changes: 16 additions & 0 deletions .chloggen/improve-server-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, github action)
component: target allocator

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Added test coverage for server handling.

# One or more tracking issues related to the change
issues: [1392]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
263 changes: 263 additions & 0 deletions cmd/otel-allocator/server/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// Copyright The OpenTelemetry 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 server

import (
"fmt"
"math/rand"
"net/http/httptest"
"testing"
"time"

"github.com/prometheus/common/model"
promconfig "github.com/prometheus/prometheus/config"

"github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation"
"github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target"
)

func BenchmarkServerTargetsHandler(b *testing.B) {
rand.Seed(time.Now().UnixNano())
var table = []struct {
numCollectors int
numJobs int
}{
{numCollectors: 100, numJobs: 100},
{numCollectors: 100, numJobs: 1000},
{numCollectors: 100, numJobs: 10000},
{numCollectors: 100, numJobs: 100000},
{numCollectors: 1000, numJobs: 100},
{numCollectors: 1000, numJobs: 1000},
{numCollectors: 1000, numJobs: 10000},
{numCollectors: 1000, numJobs: 100000},
}

for _, allocatorName := range allocation.GetRegisteredAllocatorNames() {
for _, v := range table {
a, _ := allocation.New(allocatorName, logger)
cols := allocation.MakeNCollectors(v.numCollectors, 0)
targets := allocation.MakeNNewTargets(v.numJobs, v.numCollectors, 0)
listenAddr := ":8080"
a.SetCollectors(cols)
a.SetTargets(targets)
s := NewServer(logger, a, nil, &listenAddr)
b.Run(fmt.Sprintf("%s_num_cols_%d_num_jobs_%d", allocatorName, v.numCollectors, v.numJobs), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
randomJob := rand.Intn(v.numJobs) //nolint: gosec
randomCol := rand.Intn(v.numCollectors) //nolint: gosec
request := httptest.NewRequest("GET", fmt.Sprintf("/jobs/test-job-%d/targets?collector_id=collector-%d", randomJob, randomCol), nil)
w := httptest.NewRecorder()
s.server.Handler.ServeHTTP(w, request)
}
})
}
}
}

func BenchmarkScrapeConfigsHandler(b *testing.B) {
rand.Seed(time.Now().UnixNano())
s := &Server{
logger: logger,
}

tests := []int{0, 5, 10, 50, 100, 500}
for _, n := range tests {
data := makeNScrapeConfigs(n)
b.Run(fmt.Sprintf("%d_targets", n), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
s.compareHash = 0
s.discoveryManager = &mockDiscoveryManager{m: data}
resp := httptest.NewRecorder()
s.ScrapeConfigsHandler(resp, nil)
}
})
}
}

func BenchmarkCollectorMapJSONHandler(b *testing.B) {
rand.Seed(time.Now().UnixNano())
s := &Server{
logger: logger,
}

tests := []struct {
numCollectors int
numTargets int
}{
{
numCollectors: 0,
numTargets: 0,
},
{
numCollectors: 5,
numTargets: 5,
},
{
numCollectors: 5,
numTargets: 50,
},
{
numCollectors: 5,
numTargets: 500,
},
{
numCollectors: 50,
numTargets: 5,
},
{
numCollectors: 50,
numTargets: 50,
},
{
numCollectors: 50,
numTargets: 500,
},
{
numCollectors: 50,
numTargets: 5000,
},
}
for _, tc := range tests {
data := makeNCollectorJSON(tc.numCollectors, tc.numTargets)
b.Run(fmt.Sprintf("%d_collectors_%d_targets", tc.numCollectors, tc.numTargets), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
resp := httptest.NewRecorder()
s.jsonHandler(resp, data)
}
})
}
}

func BenchmarkTargetItemsJSONHandler(b *testing.B) {
rand.Seed(time.Now().UnixNano())
s := &Server{
logger: logger,
}

tests := []struct {
numTargets int
numLabels int
}{
{
numTargets: 0,
numLabels: 0,
},
{
numTargets: 5,
numLabels: 5,
},
{
numTargets: 5,
numLabels: 50,
},
{
numTargets: 50,
numLabels: 5,
},
{
numTargets: 50,
numLabels: 50,
},
{
numTargets: 500,
numLabels: 50,
},
{
numTargets: 500,
numLabels: 500,
},
{
numTargets: 5000,
numLabels: 50,
},
{
numTargets: 5000,
numLabels: 500,
},
}
for _, tc := range tests {
data := makeNTargetItems(tc.numTargets, tc.numLabels)
b.Run(fmt.Sprintf("%d_targets_%d_labels", tc.numTargets, tc.numLabels), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
resp := httptest.NewRecorder()
s.jsonHandler(resp, data)
}
})
}
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_/")

func randSeq(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))] //nolint:gosec
}
return string(b)
}

func makeNScrapeConfigs(n int) map[string]*promconfig.ScrapeConfig {
items := make(map[string]*promconfig.ScrapeConfig, n)
for i := 0; i < n; i++ {
items[randSeq(20)] = &promconfig.ScrapeConfig{
JobName: randSeq(20),
ScrapeInterval: model.Duration(30 * time.Second),
ScrapeTimeout: model.Duration(time.Minute),
MetricsPath: randSeq(50),
SampleLimit: 5,
TargetLimit: 200,
LabelLimit: 20,
LabelNameLengthLimit: 50,
LabelValueLengthLimit: 100,
}
}
return items
}

func makeNCollectorJSON(numCollectors, numItems int) map[string]collectorJSON {
items := make(map[string]collectorJSON, numCollectors)
for i := 0; i < numCollectors; i++ {
items[randSeq(20)] = collectorJSON{
Link: randSeq(120),
Jobs: makeNTargetItems(numItems, 50),
}
}
return items
}

func makeNTargetItems(numItems, numLabels int) []*target.Item {
items := make([]*target.Item, 0, numItems)
for i := 0; i < numItems; i++ {
items = append(items, target.NewItem(
randSeq(80),
randSeq(150),
makeNNewLabels(numLabels),
randSeq(30),
))
}
return items
}

func makeNNewLabels(n int) model.LabelSet {
labels := make(map[model.LabelName]model.LabelValue, n)
for i := 0; i < n; i++ {
labels[model.LabelName(randSeq(20))] = model.LabelValue(randSeq(20))
}
return labels
}
51 changes: 51 additions & 0 deletions cmd/otel-allocator/server/mocks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright The OpenTelemetry 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 server

import (
promconfig "github.com/prometheus/prometheus/config"

"github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation"
"github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target"
)

var (
_ DiscoveryManager = &mockDiscoveryManager{}
_ allocation.Allocator = &mockAllocator{}
)

type mockDiscoveryManager struct {
m map[string]*promconfig.ScrapeConfig
}

func (m *mockDiscoveryManager) GetScrapeConfigs() map[string]*promconfig.ScrapeConfig {
return m.m
}

// mockAllocator implements the Allocator interface, but all funcs other than
// TargetItems() are a no-op.
type mockAllocator struct {
targetItems map[string]*target.Item
}

func (m *mockAllocator) SetCollectors(_ map[string]*allocation.Collector) {}
func (m *mockAllocator) SetTargets(_ map[string]*target.Item) {}
func (m *mockAllocator) Collectors() map[string]*allocation.Collector { return nil }
func (m *mockAllocator) GetTargetsForCollectorAndJob(_ string, _ string) []*target.Item { return nil }
func (m *mockAllocator) SetFilter(_ allocation.Filter) {}

func (m *mockAllocator) TargetItems() map[string]*target.Item {
return m.targetItems
}
11 changes: 8 additions & 3 deletions cmd/otel-allocator/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
promconfig "github.com/prometheus/prometheus/config"
"gopkg.in/yaml.v2"

"github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation"
Expand All @@ -47,17 +48,21 @@ type collectorJSON struct {
Jobs []*target.Item `json:"targets"`
}

type DiscoveryManager interface {
GetScrapeConfigs() map[string]*promconfig.ScrapeConfig
}

type Server struct {
logger logr.Logger
allocator allocation.Allocator
discoveryManager *target.Discoverer
discoveryManager DiscoveryManager
server *http.Server

compareHash uint64
scrapeConfigResponse []byte
}

func NewServer(log logr.Logger, allocator allocation.Allocator, discoveryManager *target.Discoverer, listenAddr *string) *Server {
func NewServer(log logr.Logger, allocator allocation.Allocator, discoveryManager DiscoveryManager, listenAddr *string) *Server {
s := &Server{
logger: log,
allocator: allocator,
Expand Down Expand Up @@ -166,7 +171,7 @@ func (s *Server) TargetsHandler(w http.ResponseWriter, r *http.Request) {
}

func (s *Server) errorHandler(w http.ResponseWriter, err error) {
w.WriteHeader(500)
w.WriteHeader(http.StatusInternalServerError)
s.jsonHandler(w, err)
}

Expand Down
Loading

0 comments on commit 3f5f1f1

Please sign in to comment.