mirror of https://github.com/hashicorp/boundary
No longer enforce version matching between daemon and cli (#4271)
* No longer check version matching between daemon and cli * Add Log Service Handler and report if the daemon is running in background modepull/4286/head
parent
27bf15f3f3
commit
e32a25f2a4
@ -0,0 +1,50 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/event"
|
||||
)
|
||||
|
||||
// LogRequest is the request body to this handler.
|
||||
type LogRequest struct {
|
||||
// Message is a required field for all requests
|
||||
Message string `json:"message,omitempty"`
|
||||
Op string `json:"op,omitempty"`
|
||||
}
|
||||
|
||||
// newLogHandlerFunc creates a handler that logs a system event using the
|
||||
// daemon's eventer.
|
||||
func newLogHandlerFunc(ctx context.Context) (http.HandlerFunc, error) {
|
||||
const op = "daemon.newLogHandlerFunc"
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
writeError(w, "only method POST allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
var perReq LogRequest
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
writeError(w, "unable to read request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(data, &perReq); err != nil {
|
||||
// If, for whatever reason, we can't parse the request body as json
|
||||
// can still log that the request to log was received and print out
|
||||
// the body of the request.
|
||||
event.WriteError(ctx, op, err, event.WithInfo("body", string(data)))
|
||||
writeError(w, "unable to parse request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
event.WriteSysEvent(ctx, op, perReq.Message, "requester_op", perReq.Op)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}, nil
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/event"
|
||||
"github.com/hashicorp/eventlogger/formatter_filters/cloudevents"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLogHandler(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
lh, err := newLogHandlerFunc(ctx)
|
||||
require.NoError(t, err)
|
||||
expectedErroringMux := http.NewServeMux()
|
||||
expectedErroringMux.HandleFunc("/v1/log", lh)
|
||||
|
||||
t.Run("get", func(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/log", nil)
|
||||
expectedErroringMux.ServeHTTP(rec, req)
|
||||
assert.Equal(t, http.StatusMethodNotAllowed, rec.Result().StatusCode)
|
||||
})
|
||||
|
||||
t.Run("delete", func(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodDelete, "/v1/log", nil)
|
||||
expectedErroringMux.ServeHTTP(rec, req)
|
||||
assert.Equal(t, http.StatusMethodNotAllowed, rec.Result().StatusCode)
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
c := event.TestEventerConfig(t, "TestLogHandler_success")
|
||||
testLock := &sync.Mutex{}
|
||||
testLogger := hclog.New(&hclog.LoggerOptions{
|
||||
Mutex: testLock,
|
||||
Name: "test",
|
||||
JSONFormat: true,
|
||||
})
|
||||
require.NoError(t, event.InitSysEventer(testLogger, testLock, "TestLogHandler_success", event.WithEventerConfig(&c.EventerConfig)))
|
||||
ctx, err := event.NewEventerContext(context.Background(), event.SysEventer())
|
||||
require.NoError(t, err)
|
||||
|
||||
lh, err := newLogHandlerFunc(ctx)
|
||||
require.NoError(t, err)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/v1/log", lh)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
b, err := json.Marshal(&LogRequest{
|
||||
Message: "test message",
|
||||
Op: "test op",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/log", bytes.NewReader(b))
|
||||
mux.ServeHTTP(rec, req)
|
||||
assert.Equal(t, http.StatusNoContent, rec.Result().StatusCode)
|
||||
|
||||
sinkFileName := c.AllEvents.Name()
|
||||
defer func() { _ = os.WriteFile(sinkFileName, nil, 0o666) }()
|
||||
b, err = os.ReadFile(sinkFileName)
|
||||
require.NoError(t, err)
|
||||
gotEvent := &cloudevents.Event{}
|
||||
err = json.Unmarshal(b, gotEvent)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotData := gotEvent.Data.(map[string]any)["data"].(map[string]any)
|
||||
assert.Equal(t, "test message", gotData["msg"])
|
||||
assert.Equal(t, "test op", gotData["requester_op"])
|
||||
})
|
||||
|
||||
t.Run("success failed unmarshaling", func(t *testing.T) {
|
||||
c := event.TestEventerConfig(t, "TestLogHandler_success")
|
||||
testLock := &sync.Mutex{}
|
||||
testLogger := hclog.New(&hclog.LoggerOptions{
|
||||
Mutex: testLock,
|
||||
Name: "test",
|
||||
JSONFormat: true,
|
||||
})
|
||||
require.NoError(t, event.InitSysEventer(testLogger, testLock, "TestLogHandler_success", event.WithEventerConfig(&c.EventerConfig)))
|
||||
ctx, err := event.NewEventerContext(context.Background(), event.SysEventer())
|
||||
require.NoError(t, err)
|
||||
|
||||
lh, err := newLogHandlerFunc(ctx)
|
||||
require.NoError(t, err)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/v1/log", lh)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
b := []byte("not json")
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/log", bytes.NewReader(b))
|
||||
mux.ServeHTTP(rec, req)
|
||||
assert.Equal(t, http.StatusBadRequest, rec.Result().StatusCode)
|
||||
|
||||
sinkFileName := c.AllEvents.Name()
|
||||
defer func() { _ = os.WriteFile(sinkFileName, nil, 0o666) }()
|
||||
b, err = os.ReadFile(sinkFileName)
|
||||
require.NoError(t, err)
|
||||
gotEvent := &cloudevents.Event{}
|
||||
err = json.Unmarshal(b, gotEvent)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotData := gotEvent.Data.(map[string]any)
|
||||
assert.NotEmpty(t, gotData["error"])
|
||||
assert.Equal(t, "not json", gotData["info"].(map[string]any)["body"])
|
||||
})
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/version"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVersionEnforcement(t *testing.T) {
|
||||
var called bool
|
||||
calledHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
h := versionEnforcement(calledHandler)
|
||||
|
||||
t.Run("no version provided", func(t *testing.T) {
|
||||
called = false
|
||||
req := httptest.NewRequest("", "/test", nil)
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, req)
|
||||
assert.True(t, called)
|
||||
assert.Equal(t, http.StatusNoContent, w.Result().StatusCode)
|
||||
})
|
||||
|
||||
t.Run("bad version provided", func(t *testing.T) {
|
||||
called = false
|
||||
req := httptest.NewRequest("", "/test", nil)
|
||||
req.Header.Set(VersionHeaderKey, "badversion")
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, req)
|
||||
assert.False(t, called)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
|
||||
})
|
||||
|
||||
t.Run("correct version provided", func(t *testing.T) {
|
||||
called = false
|
||||
req := httptest.NewRequest("", "/test", nil)
|
||||
req.Header.Set(VersionHeaderKey, version.Get().VersionNumber())
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, req)
|
||||
assert.True(t, called)
|
||||
assert.Equal(t, http.StatusNoContent, w.Result().StatusCode)
|
||||
})
|
||||
}
|
||||
Loading…
Reference in new issue