youtubebeat/vendor/github.com/elastic/beats/metricbeat/module/apache/status/status_test.go

259 lines
8.1 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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.
// +build !integration
package status
import (
"bufio"
"net"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"sync"
"testing"
"time"
"github.com/elastic/beats/libbeat/common"
mbtest "github.com/elastic/beats/metricbeat/mb/testing"
"github.com/stretchr/testify/assert"
)
// response is a raw response copied from an Apache web server.
const response = `apache
ServerVersion: Apache/2.4.18 (Unix)
ServerMPM: event
Server Built: Mar 2 2016 21:08:47
CurrentTime: Thursday, 12-May-2016 20:30:25 UTC
RestartTime: Saturday, 30-Apr-2016 23:17:22 UTC
ParentServerConfigGeneration: 1
ParentServerMPMGeneration: 0
ServerUptimeSeconds: 1026782
ServerUptime: 11 days 21 hours 13 minutes 2 seconds
Load1: 0.02
Load5: 0.01
Load15: 0.05
Total Accesses: 167
Total kBytes: 63
CPUUser: 14076.6
CPUSystem: 6750.8
CPUChildrenUser: 10.1
CPUChildrenSystem: 11.2
CPULoad: 2.02841
Uptime: 1026782
ReqPerSec: .000162644
BytesPerSec: .0628293
BytesPerReq: 386.299
BusyWorkers: 1
IdleWorkers: 99
ConnsTotal: 6
ConnsAsyncWriting: 1
ConnsAsyncKeepAlive: 2
ConnsAsyncClosing: 3
Scoreboard: __________________________________________________________________________________W_________________............................................................................................................................................................................................................................................................................................................`
// TestFetchEventContents verifies the contents of the returned event against
// the raw Apache response.
func TestFetchEventContents(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "text/plain; charset=ISO-8859-1")
w.Write([]byte(response))
}))
defer server.Close()
config := map[string]interface{}{
"module": "apache",
"metricsets": []string{"status"},
"hosts": []string{server.URL},
}
f := mbtest.NewEventFetcher(t, config)
event, err := f.Fetch()
if !assert.NoError(t, err) {
t.FailNow()
}
t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event.StringToPrint())
assert.Equal(t, 386.299, event["bytes_per_request"])
assert.Equal(t, .0628293, event["bytes_per_sec"])
workers := event["workers"].(common.MapStr)
assert.EqualValues(t, 1, workers["busy"])
assert.EqualValues(t, 99, workers["idle"])
connections := event["connections"].(common.MapStr)
async := connections["async"].(common.MapStr)
assert.EqualValues(t, 3, async["closing"])
assert.EqualValues(t, 2, async["keep_alive"])
assert.EqualValues(t, 1, async["writing"])
assert.EqualValues(t, 6, connections["total"])
cpu := event["cpu"].(common.MapStr)
assert.Equal(t, 11.2, cpu["children_system"])
assert.Equal(t, 10.1, cpu["children_user"])
assert.Equal(t, 2.02841, cpu["load"])
assert.Equal(t, 6750.8, cpu["system"])
assert.Equal(t, 14076.6, cpu["user"])
assert.Equal(t, server.URL[7:], event["hostname"])
load := event["load"].(common.MapStr)
assert.Equal(t, .02, load["1"])
assert.Equal(t, .05, load["15"])
assert.Equal(t, .01, load["5"])
assert.Equal(t, .000162644, event["requests_per_sec"])
scoreboard := event["scoreboard"].(common.MapStr)
assert.Equal(t, 0, scoreboard["closing_connection"])
assert.Equal(t, 0, scoreboard["dns_lookup"])
assert.Equal(t, 0, scoreboard["gracefully_finishing"])
assert.Equal(t, 0, scoreboard["idle_cleanup"])
assert.Equal(t, 0, scoreboard["keepalive"])
assert.Equal(t, 0, scoreboard["logging"])
assert.Equal(t, 300, scoreboard["open_slot"]) // Number of '.'
assert.Equal(t, 0, scoreboard["reading_request"])
assert.Equal(t, 1, scoreboard["sending_reply"]) // Number of 'W'
assert.Equal(t, 400, scoreboard["total"]) // Number of scorecard chars.
assert.Equal(t, 99, scoreboard["waiting_for_connection"]) // Number of '_'
assert.EqualValues(t, 167, event["total_accesses"])
assert.EqualValues(t, 63, event["total_kbytes"])
uptime := event["uptime"].(common.MapStr)
assert.EqualValues(t, 1026782, uptime["uptime"])
assert.EqualValues(t, 1026782, uptime["server_uptime"])
}
// TestFetchTimeout verifies that the HTTP request times out and an error is
// returned.
func TestFetchTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "text/plain; charset=ISO-8859-1")
w.Write([]byte(response))
<-r.Context().Done()
}))
defer server.Close()
config := map[string]interface{}{
"module": "apache",
"metricsets": []string{"status"},
"hosts": []string{server.URL},
"timeout": "50ms",
}
f := mbtest.NewEventFetcher(t, config)
start := time.Now()
_, err := f.Fetch()
elapsed := time.Since(start)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "request canceled (Client.Timeout exceeded")
}
// Elapsed should be ~50ms, sometimes it can be up to 1s
assert.True(t, elapsed < 5*time.Second, "elapsed time: %s", elapsed.String())
}
// TestMultipleFetches verifies that the server connection is reused when HTTP
// keep-alive is supported by the server.
func TestMultipleFetches(t *testing.T) {
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "text/plain; charset=ISO-8859-1")
w.Write([]byte(response))
}))
connLock := sync.Mutex{}
conns := map[string]struct{}{}
server.Config.ConnState = func(conn net.Conn, state http.ConnState) {
connLock.Lock()
conns[conn.RemoteAddr().String()] = struct{}{}
connLock.Unlock()
}
server.Start()
defer server.Close()
config := map[string]interface{}{
"module": "apache",
"metricsets": []string{"status"},
"hosts": []string{server.URL},
}
f := mbtest.NewEventFetcher(t, config)
for i := 0; i < 20; i++ {
_, err := f.Fetch()
if !assert.NoError(t, err) {
t.FailNow()
}
}
connLock.Lock()
assert.Len(t, conns, 1,
"only a single connection should exist because of keep-alives")
connLock.Unlock()
}
func TestHostParser(t *testing.T) {
var tests = []struct {
host string
url string
err string
}{
{"", "", "empty host"},
{":80", "", "empty host"},
{"localhost", "http://localhost/server-status?auto=", ""},
{"localhost/ServerStatus", "http://localhost/ServerStatus?auto=", ""},
{"127.0.0.1", "http://127.0.0.1/server-status?auto=", ""},
{"https://127.0.0.1", "https://127.0.0.1/server-status?auto=", ""},
{"[2001:db8:0:1]:80", "http://[2001:db8:0:1]:80/server-status?auto=", ""},
{"https://admin:secret@127.0.0.1", "https://admin:secret@127.0.0.1/server-status?auto=", ""},
}
for _, test := range tests {
hostData, err := hostParser(mbtest.NewTestModule(t, map[string]interface{}{}), test.host)
if err != nil && test.err != "" {
assert.Contains(t, err.Error(), test.err)
} else if assert.NoError(t, err, "unexpected error") {
assert.Equal(t, test.url, hostData.URI)
}
}
}
// Test event mapping for different apache status outputs
func TestStatusOutputs(t *testing.T) {
files, err := filepath.Glob("./_meta/test/status_*")
assert.NoError(t, err)
for _, filename := range files {
f, err := os.Open(filename)
assert.NoError(t, err, "cannot open test file "+filename)
scanner := bufio.NewScanner(f)
_, err = eventMapping(scanner, "localhost")
assert.NoError(t, err, "error mapping "+filename)
}
}