Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement a log of events queriable from the metrics endpoint #306

Merged
merged 2 commits into from
Jul 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cmd/coordinator/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/edgelesssys/marblerun/coordinator/config"
"github.com/edgelesssys/marblerun/coordinator/core"
"github.com/edgelesssys/marblerun/coordinator/events"
"github.com/edgelesssys/marblerun/coordinator/quote"
"github.com/edgelesssys/marblerun/coordinator/recovery"
"github.com/edgelesssys/marblerun/coordinator/seal"
Expand Down Expand Up @@ -57,6 +58,7 @@ func run(validator quote.Validator, issuer quote.Issuer, sealDir string, sealer
promServerAddr := os.Getenv(config.PromAddr)

// Create Prometheus resources and start the Prometheus server.
var eventlog = events.NewLog()
var promRegistry *prometheus.Registry
var promFactoryPtr *promauto.Factory
if promServerAddr != "" {
Expand All @@ -72,15 +74,15 @@ func run(validator quote.Validator, issuer quote.Issuer, sealDir string, sealer
"commit": GitCommit,
},
})
go server.RunPrometheusServer(promServerAddr, zapLogger, promRegistry)
go server.RunPrometheusServer(promServerAddr, zapLogger, promRegistry, eventlog)
}

// creating core
zapLogger.Info("creating the Core object")
if err := os.MkdirAll(sealDir, 0o700); err != nil {
zapLogger.Fatal("Cannot create or access sealdir. Please check the permissions for the specified path.", zap.Error(err))
}
co, err := core.NewCore(dnsNames, validator, issuer, sealer, recovery, zapLogger, promFactoryPtr)
co, err := core.NewCore(dnsNames, validator, issuer, sealer, recovery, zapLogger, promFactoryPtr, eventlog)
if err != nil {
if _, ok := err.(core.QuoteError); !ok || !devMode {
zapLogger.Fatal("Cannot create Coordinator core", zap.Error(err))
Expand Down
7 changes: 5 additions & 2 deletions coordinator/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"sync"
"time"

"github.com/edgelesssys/marblerun/coordinator/events"
"github.com/edgelesssys/marblerun/coordinator/manifest"
"github.com/edgelesssys/marblerun/coordinator/quote"
"github.com/edgelesssys/marblerun/coordinator/recovery"
Expand Down Expand Up @@ -55,6 +56,7 @@ type Core struct {
updateLogger *updatelog.Logger
zaplogger *zap.Logger
metrics *coreMetrics
eventlog *events.Log
rpc.UnimplementedMarbleServer
}

Expand Down Expand Up @@ -112,7 +114,7 @@ func (c *Core) advanceState(newState state, tx store.Transaction) error {
}

// NewCore creates and initializes a new Core object.
func NewCore(dnsNames []string, qv quote.Validator, qi quote.Issuer, sealer seal.Sealer, recovery recovery.Recovery, zapLogger *zap.Logger, promFactory *promauto.Factory) (*Core, error) {
func NewCore(dnsNames []string, qv quote.Validator, qi quote.Issuer, sealer seal.Sealer, recovery recovery.Recovery, zapLogger *zap.Logger, promFactory *promauto.Factory, eventlog *events.Log) (*Core, error) {
aep marked this conversation as resolved.
Show resolved Hide resolved
stor := store.NewStdStore(sealer)
c := &Core{
qv: qv,
Expand All @@ -122,6 +124,7 @@ func NewCore(dnsNames []string, qv quote.Validator, qi quote.Issuer, sealer seal
data: storeWrapper{store: stor},
sealer: sealer,
zaplogger: zapLogger,
eventlog: eventlog,
}
c.metrics = newCoreMetrics(promFactory, c, "coordinator")

Expand Down Expand Up @@ -207,7 +210,7 @@ func NewCoreWithMocks() *Core {
issuer := quote.NewMockIssuer()
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()
core, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
core, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
if err != nil {
panic(err)
}
Expand Down
12 changes: 6 additions & 6 deletions coordinator/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestSeal(t *testing.T) {
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()

c, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
c, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)

// Set manifest. This will seal the state.
Expand All @@ -90,7 +90,7 @@ func TestSeal(t *testing.T) {
assert.NoError(err)

// Check sealing with a new core initialized with the sealed state.
c2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
c2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
c2State, err := c2.data.getState()
assert.NoError(err)
Expand Down Expand Up @@ -127,7 +127,7 @@ func TestRecover(t *testing.T) {
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()

c, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
c, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)

// new core does not allow recover
Expand All @@ -145,7 +145,7 @@ func TestRecover(t *testing.T) {

// Initialize new core and let unseal fail
sealer.UnsealError = seal.ErrEncryptionKey
c2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
c2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
sealer.UnsealError = nil
require.NoError(err)
c2State, err := c2.data.getState()
Expand Down Expand Up @@ -367,7 +367,7 @@ func TestUnsetRestart(t *testing.T) {
recovery := recovery.NewSinglePartyRecovery()

// create a new core, this seals the state with only certificate and keys
c1, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
c1, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
c1State, err := c1.data.getState()
assert.NoError(err)
Expand All @@ -376,7 +376,7 @@ func TestUnsetRestart(t *testing.T) {
assert.NoError(err)

// create a second core, this should overwrite the previously sealed certificate and keys since no manifest was set
c2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
c2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
c2State, err := c2.data.getState()
assert.NoError(err)
Expand Down
6 changes: 6 additions & 0 deletions coordinator/core/marbleapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ func (c *Core) Activate(ctx context.Context, req *rpc.ActivationReq) (*rpc.Activ

c.metrics.marbleAPI.activationSuccess.WithLabelValues(req.GetMarbleType(), req.GetUUID()).Inc()
c.zaplogger.Info("Successfully activated new Marble", zap.String("MarbleType", req.MarbleType), zap.String("UUID", marbleUUID.String()))

if c.eventlog != nil {
c.eventlog.Activation(req.GetMarbleType(), req.GetUUID(), req.GetQuote())
}


return resp, nil
}

Expand Down
8 changes: 4 additions & 4 deletions coordinator/core/marbleapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestActivate(t *testing.T) {
issuer := quote.NewMockIssuer()
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
require.NotNil(coreServer)

Expand Down Expand Up @@ -461,7 +461,7 @@ func TestSecurityLevelUpdate(t *testing.T) {
issuer := quote.NewMockIssuer()
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
require.NotNil(coreServer)

Expand Down Expand Up @@ -491,7 +491,7 @@ func TestSecurityLevelUpdate(t *testing.T) {
spawner.newMarble("frontend", "Azure", false)

// Use a new core and test if updated manifest persisted after restart
coreServer2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
coreServer2, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
coreServer2State, err := coreServer2.data.getState()
assert.NoError(err)
Expand Down Expand Up @@ -572,7 +572,7 @@ func TestActivateWithMissingParameters(t *testing.T) {
issuer := quote.NewMockIssuer()
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
require.NotNil(coreServer)

Expand Down
6 changes: 3 additions & 3 deletions coordinator/core/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestStoreWrapperMetrics(t *testing.T) {
//
reg := prometheus.NewRegistry()
fac := promauto.With(reg)
c, _ := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, &fac)
c, _ := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, &fac, nil)
assert.Equal(1, promtest.CollectAndCount(c.metrics.coordinatorState))
assert.Equal(float64(stateAcceptingManifest), promtest.ToFloat64(c.metrics.coordinatorState))

Expand All @@ -49,7 +49,7 @@ func TestStoreWrapperMetrics(t *testing.T) {
reg = prometheus.NewRegistry()
fac = promauto.With(reg)
sealer.UnsealError = seal.ErrEncryptionKey
c, err = NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, &fac)
c, err = NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, &fac, nil)
sealer.UnsealError = nil
require.NoError(err)
assert.Equal(1, promtest.CollectAndCount(c.metrics.coordinatorState))
Expand Down Expand Up @@ -84,7 +84,7 @@ func TestMarbleAPIMetrics(t *testing.T) {
recovery := recovery.NewSinglePartyRecovery()
promRegistry := prometheus.NewRegistry()
promFactory := promauto.With(promRegistry)
c, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, &promFactory)
c, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, &promFactory, nil)
require.NoError(err)
require.NotNil(c)

Expand Down
2 changes: 1 addition & 1 deletion coordinator/core/openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestOpenSSLVerify(t *testing.T) {
issuer := quote.NewMockIssuer()
sealer := &seal.MockSealer{}
recovery := recovery.NewSinglePartyRecovery()
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil)
coreServer, err := NewCore([]string{"localhost"}, validator, issuer, sealer, recovery, zapLogger, nil, nil)
require.NoError(err)
require.NotNil(coreServer)

Expand Down
54 changes: 54 additions & 0 deletions coordinator/events/eventlog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Edgeless Systems GmbH.
//
aep marked this conversation as resolved.
Show resolved Hide resolved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

// Package events implements a log of coordinator events.
package events

import (
"encoding/json"
"net/http"
"time"
)

// ActivationEvent is an event that is logged when a marble is activated.
type ActivationEvent struct {
MarbleType string `json:"marbleType"`
UUID string `json:"uuid"`
Quote []byte `json:"quote"`
}

// Event represents a single event in the event log.
type Event struct {
Timestamp time.Time `json:"time"`
Activation *ActivationEvent `json:"activation"`
}

// Log is a log of coordinator events.
type Log struct {
events []Event
}

// NewLog creates a new log.
func NewLog() *Log {
return &Log{}
}

// Activation adds an activation event to the log.
func (l *Log) Activation(marbleType string, uuid string, quote []byte) {
l.events = append(l.events, Event{
Timestamp: time.Now(),
Activation: &ActivationEvent{MarbleType: marbleType, UUID: uuid, Quote: quote},
})
}

// Handler returns a http.HandlerFunc which writes the log as JSON array.
func (l *Log) Handler() http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(l.events)
})
}
4 changes: 3 additions & 1 deletion coordinator/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"os"

"github.com/edgelesssys/marblerun/coordinator/core"
"github.com/edgelesssys/marblerun/coordinator/events"
"github.com/edgelesssys/marblerun/coordinator/rpc"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -112,9 +113,10 @@ func RunClientServer(mux http.Handler, address string, tlsConfig *tls.Config, za
}

// RunPrometheusServer runs a HTTP server handling the prometheus metrics endpoint.
func RunPrometheusServer(address string, zapLogger *zap.Logger, reg *prometheus.Registry) {
func RunPrometheusServer(address string, zapLogger *zap.Logger, reg *prometheus.Registry, eventlog *events.Log) {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.InstrumentMetricHandler(reg, promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg})))
mux.Handle("/events", eventlog.Handler())
zapLogger.Info("starting prometheus /metrics endpoint", zap.String("address", address))
err := http.ListenAndServe(address, mux)
zapLogger.Warn(err.Error())
Expand Down