consul/agent/event_endpoint_test.go
hashicorp-copywrite[bot] 5fb9df1640
[COMPLIANCE] License changes (#18443)
* Adding explicit MPL license for sub-package

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Adding explicit MPL license for sub-package

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Updating the license from MPL to Business Source License

Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl.

* add missing license headers

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

---------

Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 09:12:13 -04:00

391 lines
8.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package agent
import (
"bytes"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/sdk/testutil/retry"
"github.com/hashicorp/consul/testrpc"
)
func TestEventFire(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
body := bytes.NewBuffer([]byte("test"))
url := "/v1/event/fire/test?node=Node&service=foo&tag=bar"
req, _ := http.NewRequest("PUT", url, body)
resp := httptest.NewRecorder()
obj, err := a.srv.EventFire(resp, req)
if err != nil {
t.Fatalf("err: %v", err)
}
event, ok := obj.(*UserEvent)
if !ok {
t.Fatalf("bad: %#v", obj)
}
if event.ID == "" {
t.Fatalf("bad: %#v", event)
}
if event.Name != "test" {
t.Fatalf("bad: %#v", event)
}
if string(event.Payload) != "test" {
t.Fatalf("bad: %#v", event)
}
if event.NodeFilter != "Node" {
t.Fatalf("bad: %#v", event)
}
if event.ServiceFilter != "foo" {
t.Fatalf("bad: %#v", event)
}
if event.TagFilter != "bar" {
t.Fatalf("bad: %#v", event)
}
}
func TestEventFire_token(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, TestACLConfig()+`
acl_default_policy = "deny"
`)
defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1")
token := createToken(t, a, testEventPolicy)
type tcase struct {
event string
allowed bool
}
tcases := []tcase{
{"foo", false},
{"bar", false},
{"baz", true},
}
for _, c := range tcases {
// Try to fire the event over the HTTP interface
url := fmt.Sprintf("/v1/event/fire/%s", c.event)
req, _ := http.NewRequest("PUT", url, nil)
req.Header.Add("X-Consul-Token", token)
resp := httptest.NewRecorder()
_, err := a.srv.EventFire(resp, req)
// Check the result
if c.allowed {
body := resp.Body.String()
if acl.IsErrPermissionDenied(errors.New(body)) {
t.Fatalf("bad: %s", body)
}
if resp.Code != 200 {
t.Fatalf("bad: %d", resp.Code)
}
} else {
if !acl.IsErrPermissionDenied(err) {
t.Fatalf("bad: %s", err.Error())
}
if err, ok := err.(HTTPError); ok {
if err.StatusCode != 403 {
t.Fatalf("Expected 403 but got %d", err.StatusCode)
}
} else {
t.Fatalf("Expected HTTP Error %v", err)
}
}
}
}
func TestEventList(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
p := &UserEvent{Name: "test"}
if err := a.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err)
}
retry.Run(t, func(r *retry.R) {
req, _ := http.NewRequest("GET", "/v1/event/list", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
if err != nil {
r.Fatal(err)
}
list, ok := obj.([]*UserEvent)
if !ok {
r.Fatalf("bad: %#v", obj)
}
if len(list) != 1 || list[0].Name != "test" {
r.Fatalf("bad: %#v", list)
}
header := resp.Header().Get("X-Consul-Index")
if header == "" || header == "0" {
r.Fatalf("bad: %#v", header)
}
})
}
func TestEventList_Filter(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
p := &UserEvent{Name: "test"}
if err := a.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err)
}
p = &UserEvent{Name: "foo"}
if err := a.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err)
}
retry.Run(t, func(r *retry.R) {
req, _ := http.NewRequest("GET", "/v1/event/list?name=foo", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
if err != nil {
r.Fatal(err)
}
list, ok := obj.([]*UserEvent)
if !ok {
r.Fatalf("bad: %#v", obj)
}
if len(list) != 1 || list[0].Name != "foo" {
r.Fatalf("bad: %#v", list)
}
header := resp.Header().Get("X-Consul-Index")
if header == "" || header == "0" {
r.Fatalf("bad: %#v", header)
}
})
}
func TestEventList_ACLFilter(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, TestACLConfig())
defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1")
// Fire some events.
events := []*UserEvent{
{Name: "foo"},
{Name: "bar"},
}
for _, e := range events {
err := a.UserEvent("dc1", "root", e)
require.NoError(t, err)
}
t.Run("no token", func(t *testing.T) {
retry.Run(t, func(r *retry.R) {
req := httptest.NewRequest("GET", "/v1/event/list", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
require.NoError(r, err)
list, ok := obj.([]*UserEvent)
require.True(r, ok)
require.Empty(r, list)
require.Empty(r, resp.Header().Get("X-Consul-Results-Filtered-By-ACLs"))
})
})
t.Run("token with access to one event type", func(t *testing.T) {
retry.Run(t, func(r *retry.R) {
token := testCreateToken(t, a, `
event "foo" {
policy = "read"
}
`)
req := httptest.NewRequest("GET", "/v1/event/list", nil)
req.Header.Add("X-Consul-Token", token)
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
require.NoError(r, err)
list, ok := obj.([]*UserEvent)
require.True(r, ok)
require.Len(r, list, 1)
require.Equal(r, "foo", list[0].Name)
require.NotEmpty(r, resp.Header().Get("X-Consul-Results-Filtered-By-ACLs"))
})
})
t.Run("root token", func(t *testing.T) {
retry.Run(t, func(r *retry.R) {
req := httptest.NewRequest("GET", "/v1/event/list", nil)
req.Header.Add("X-Consul-Token", "root")
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
require.NoError(r, err)
list, ok := obj.([]*UserEvent)
require.True(r, ok)
require.Len(r, list, 2)
var names []string
for _, e := range list {
names = append(names, e.Name)
}
require.ElementsMatch(r, []string{"foo", "bar"}, names)
require.Empty(r, resp.Header().Get("X-Consul-Results-Filtered-By-ACLs"))
})
})
}
func TestEventList_Blocking(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
p := &UserEvent{Name: "test"}
if err := a.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err)
}
var index string
retry.Run(t, func(r *retry.R) {
req, _ := http.NewRequest("GET", "/v1/event/list", nil)
resp := httptest.NewRecorder()
if _, err := a.srv.EventList(resp, req); err != nil {
r.Fatal(err)
}
header := resp.Header().Get("X-Consul-Index")
if header == "" || header == "0" {
r.Fatalf("bad: %#v", header)
}
index = header
})
go func() {
time.Sleep(50 * time.Millisecond)
p := &UserEvent{Name: "second"}
if err := a.UserEvent("dc1", "root", p); err != nil {
t.Errorf("err: %v", err)
}
}()
retry.Run(t, func(r *retry.R) {
url := "/v1/event/list?index=" + index
req, _ := http.NewRequest("GET", url, nil)
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
if err != nil {
r.Fatal(err)
}
list, ok := obj.([]*UserEvent)
if !ok {
r.Fatalf("bad: %#v", obj)
}
if len(list) != 2 || list[1].Name != "second" {
r.Fatalf("bad: %#v", list)
}
})
}
func TestEventList_EventBufOrder(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := NewTestAgent(t, "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
// Fire some events in a non-sequential order
expected := &UserEvent{Name: "foo"}
for _, e := range []*UserEvent{
{Name: "foo"},
{Name: "bar"},
{Name: "foo"},
expected,
{Name: "bar"},
} {
if err := a.UserEvent("dc1", "root", e); err != nil {
t.Fatalf("err: %v", err)
}
}
// Test that the event order is preserved when name
// filtering on a list of > 1 matching event.
retry.Run(t, func(r *retry.R) {
url := "/v1/event/list?name=foo"
req, _ := http.NewRequest("GET", url, nil)
resp := httptest.NewRecorder()
obj, err := a.srv.EventList(resp, req)
if err != nil {
r.Fatal(err)
}
list, ok := obj.([]*UserEvent)
if !ok {
r.Fatalf("bad: %#v", obj)
}
if len(list) != 3 || list[2].ID != expected.ID {
r.Fatalf("bad: %#v", list)
}
})
}
func TestUUIDToUint64(t *testing.T) {
t.Parallel()
inp := "cb9a81ad-fff6-52ac-92a7-5f70687805ec"
// Output value was computed using python
if uuidToUint64(inp) != 6430540886266763072 {
t.Fatalf("bad")
}
}