From e756a603731dfbfdf8c9c6f2bcb2395929cd14fb Mon Sep 17 00:00:00 2001 From: William Petit Date: Fri, 13 Oct 2023 12:30:52 +0200 Subject: [PATCH] feat: sentry integration --- Makefile | 1 + go.mod | 3 +- go.sum | 5 ++ internal/agent/agent.go | 17 +++-- internal/agent/controller/app/app_handler.go | 15 +++-- .../agent/controller/app/app_repository.go | 17 +++-- internal/agent/controller/app/controller.go | 17 +++-- internal/agent/controller/app/server.go | 5 +- internal/agent/controller/mdns/controller.go | 13 +++- .../openwrt/sysupgrade_controller.go | 5 +- .../controller/openwrt/uci_controller.go | 5 +- .../controller/persistence/controller.go | 9 ++- internal/agent/controller/proxy/controller.go | 13 +++- .../agent/controller/proxy/reverse_proxy.go | 9 ++- internal/agent/controller/spec/controller.go | 7 +- internal/command/main.go | 24 +++++++ internal/command/server/database/ping.go | 5 +- internal/config/config.go | 2 + internal/config/sentry.go | 18 +++++ internal/datastore/sqlite/agent_repository.go | 11 ++- internal/server/agent_api.go | 67 ++++++++++++++----- internal/server/authorization.go | 7 +- internal/server/spec_api.go | 26 +++++-- .../app.emissary.cadoles.com.json | 18 +++-- .../mdns.emissary.cadoles.com.json | 5 ++ .../proxy.emissary.cadoles.com.json | 8 +++ .../uci.emissary.cadoles.com.json | 7 +- 27 files changed, 272 insertions(+), 67 deletions(-) create mode 100644 internal/config/sentry.go diff --git a/Makefile b/Makefile index 942909f..aa7f8e1 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,7 @@ load-sample-specs: cat misc/spec-samples/app.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name app.emissary.cadoles.com cat misc/spec-samples/proxy.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name proxy.emissary.cadoles.com cat misc/spec-samples/mdns.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name mdns.emissary.cadoles.com + cat misc/spec-samples/uci.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name uci.emissary.cadoles.com version: .mktools @echo $(MKT_PROJECT_VERSION) diff --git a/go.mod b/go.mod index b87680b..a2ef054 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/barnybug/go-cast v0.0.0-20201201064555-a87ccbc26692 // indirect github.com/dop251/goja_nodejs v0.0.0-20230320130059-dcf93ba651dd // indirect github.com/gabriel-vasile/mimetype v1.4.1 // indirect + github.com/getsentry/sentry-go v0.25.0 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect @@ -105,7 +106,7 @@ require ( golang.org/x/text v0.9.0 // indirect golang.org/x/tools v0.8.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.29.1 // indirect gopkg.in/go-playground/validator.v9 v9.31.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect diff --git a/go.sum b/go.sum index 5345b4c..508e096 100644 --- a/go.sum +++ b/go.sum @@ -504,6 +504,8 @@ github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkF github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= @@ -1213,6 +1215,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1998,6 +2001,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/agent/agent.go b/internal/agent/agent.go index fcedc18..a78ab9b 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -8,6 +8,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/auth/agent" "forge.cadoles.com/Cadoles/emissary/internal/jwk" "forge.cadoles.com/Cadoles/emissary/pkg/client" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/api" "gitlab.com/wpetit/goweb/logger" @@ -43,13 +44,17 @@ func (a *Agent) Run(ctx context.Context) error { logger.Debug(ctx, "registering agent") if err := a.registerAgent(ctx, client, state); err != nil { - logger.Error(ctx, "could not register agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not register agent", logger.E(err)) + sentry.CaptureException(err) } logger.Debug(ctx, "state before reconciliation", logger.F("state", state)) if err := a.Reconcile(ctx, state); err != nil { - logger.Error(ctx, "could not reconcile node with state", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not reconcile node with state", logger.E(err)) + sentry.CaptureException(err) return } @@ -79,7 +84,9 @@ func (a *Agent) Reconcile(ctx context.Context, state *State) error { ) if err := ctrl.Reconcile(ctrlCtx, state); err != nil { - logger.Error(ctx, "could not reconcile", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not reconcile", logger.E(err)) + sentry.CaptureException(err) } } @@ -110,10 +117,12 @@ func (a *Agent) collectMetadata(ctx context.Context) (map[string]any, error) { for _, collector := range a.collectors { name, value, err := collector.Collect(ctx) if err != nil { + err = errors.WithStack(err) logger.Error( ctx, "could not collect metadata", - logger.E(errors.WithStack(err)), logger.F("name", name), + logger.E(err), logger.F("name", name), ) + sentry.CaptureException(err) continue } diff --git a/internal/agent/controller/app/app_handler.go b/internal/agent/controller/app/app_handler.go index b4df08c..a460e8f 100644 --- a/internal/agent/controller/app/app_handler.go +++ b/internal/agent/controller/app/app_handler.go @@ -23,6 +23,7 @@ import ( "forge.cadoles.com/arcad/edge/pkg/storage/driver" "forge.cadoles.com/arcad/edge/pkg/storage/share" "github.com/Masterminds/sprig/v3" + "github.com/getsentry/sentry-go" "github.com/go-chi/chi/v5" "github.com/lestrrat-go/jwx/v2/jwa" "github.com/pkg/errors" @@ -197,21 +198,25 @@ func createResolveAppURL(specs *spec.Spec) (ResolveAppURLFunc, error) { for ifaceName, ifaceTmpl := range ifaceMappings { iface, err := net.InterfaceByName(ifaceName) if err != nil { - logger.Error( + err = errors.WithStack(err) + logger.Warn( ctx, "could not find interface", - logger.E(errors.WithStack(err)), logger.F("iface", ifaceName), + logger.E(err), logger.F("iface", ifaceName), ) + sentry.CaptureException(err) continue } addresses, err := iface.Addrs() if err != nil { + err = errors.WithStack(err) logger.Error( ctx, "could not list interface addresses", - logger.E(errors.WithStack(err)), + logger.E(err), logger.F("iface", iface.Name), ) + sentry.CaptureException(err) continue } @@ -219,11 +224,13 @@ func createResolveAppURL(specs *spec.Spec) (ResolveAppURLFunc, error) { for _, addr := range addresses { ifaIP, network, err := net.ParseCIDR(addr.String()) if err != nil { + err = errors.WithStack(err) logger.Error( ctx, "could not parse interface ip", - logger.E(errors.WithStack(err)), + logger.E(err), logger.F("iface", iface.Name), ) + sentry.CaptureException(err) continue } diff --git a/internal/agent/controller/app/app_repository.go b/internal/agent/controller/app/app_repository.go index 884963f..53b2994 100644 --- a/internal/agent/controller/app/app_repository.go +++ b/internal/agent/controller/app/app_repository.go @@ -8,6 +8,7 @@ import ( "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bundle" appModule "forge.cadoles.com/arcad/edge/pkg/module/app" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) @@ -63,14 +64,18 @@ func (r *AppRepository) List(ctx context.Context) ([]*app.Manifest, error) { bundle, err := bundle.FromPath(path) if err != nil { - logger.Error(bundleCtx, "could not load bundle", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(bundleCtx, "could not load bundle", logger.E(err)) + sentry.CaptureException(err) continue } manifest, err := app.LoadManifest(bundle) if err != nil { - logger.Error(bundleCtx, "could not load manifest", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(bundleCtx, "could not load manifest", logger.E(err)) + sentry.CaptureException(err) continue } @@ -97,14 +102,18 @@ func (r *AppRepository) findManifest(ctx context.Context, id app.ID) (*app.Manif bundle, err := bundle.FromPath(path) if err != nil { - logger.Error(bundleCtx, "could not load bundle", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(bundleCtx, "could not load bundle", logger.E(err)) + sentry.CaptureException(err) continue } manifest, err := app.LoadManifest(bundle) if err != nil { - logger.Error(bundleCtx, "could not load manifest", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(bundleCtx, "could not load manifest", logger.E(err)) + sentry.CaptureException(err) continue } diff --git a/internal/agent/controller/app/controller.go b/internal/agent/controller/app/controller.go index 9247327..696c983 100644 --- a/internal/agent/controller/app/controller.go +++ b/internal/agent/controller/app/controller.go @@ -11,6 +11,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bundle" + "github.com/getsentry/sentry-go" "github.com/mitchellh/hashstructure/v2" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" @@ -66,11 +67,13 @@ func (c *Controller) stopAllApps(ctx context.Context, spec *spec.Spec) { logger.Info(ctx, "stopping app", logger.F("appID", appID)) if err := entry.Server.Stop(); err != nil { + err = errors.WithStack(err) logger.Error( ctx, "error while stopping app", logger.F("appID", appID), - logger.E(errors.WithStack(err)), + logger.E(err), ) + sentry.CaptureException(err) delete(c.servers, appID) } @@ -87,21 +90,25 @@ func (c *Controller) updateApps(ctx context.Context, specs *spec.Spec) { logger.Info(ctx, "stopping app", logger.F("appKey", appKey)) if err := server.Server.Stop(); err != nil { + err = errors.WithStack(err) logger.Error( ctx, "error while stopping app", logger.F("appKey", appKey), - logger.E(errors.WithStack(err)), + logger.E(err), ) + sentry.CaptureException(err) delete(c.servers, appKey) } } if err := c.updateAppRepository(ctx, specs); err != nil { + err = errors.WithStack(err) logger.Error( ctx, "could not update app repository", - logger.E(errors.WithStack(err)), + logger.E(err), ) + sentry.CaptureException(err) return } @@ -111,7 +118,9 @@ func (c *Controller) updateApps(ctx context.Context, specs *spec.Spec) { appCtx := logger.With(ctx, logger.F("appKey", appKey)) if err := c.updateApp(ctx, specs, appKey); err != nil { - logger.Error(appCtx, "could not update app", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(appCtx, "could not update app", logger.E(err)) + sentry.CaptureException(err) continue } } diff --git a/internal/agent/controller/app/server.go b/internal/agent/controller/app/server.go index 22d1cae..edd2c00 100644 --- a/internal/agent/controller/app/server.go +++ b/internal/agent/controller/app/server.go @@ -14,6 +14,7 @@ import ( "gitlab.com/wpetit/goweb/logger" "forge.cadoles.com/arcad/edge/pkg/bundle" + "github.com/getsentry/sentry-go" "github.com/go-chi/chi/middleware" "github.com/go-chi/chi/v5" "github.com/pkg/errors" @@ -72,7 +73,9 @@ func (s *Server) Start(ctx context.Context, addr string) (err error) { defer func() { if recovered := recover(); recovered != nil { if err, ok := recovered.(error); ok { - logger.Error(ctx, err.Error(), logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, err.Error(), logger.E(err)) + sentry.CaptureException(err) return } diff --git a/internal/agent/controller/mdns/controller.go b/internal/agent/controller/mdns/controller.go index c87305a..9b80719 100644 --- a/internal/agent/controller/mdns/controller.go +++ b/internal/agent/controller/mdns/controller.go @@ -8,6 +8,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent" mdns "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/mdns/spec" "github.com/brutella/dnssd" + "github.com/getsentry/sentry-go" "github.com/mitchellh/hashstructure/v2" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" @@ -117,7 +118,9 @@ func (c *Controller) updateResponder(ctx context.Context, spec *mdns.Spec) error service, err := dnssd.NewService(config) if err != nil { - logger.Error(ctx, "could not create mdns service", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not create mdns service", logger.E(err)) + sentry.CaptureException(err) continue } @@ -132,7 +135,9 @@ func (c *Controller) updateResponder(ctx context.Context, spec *mdns.Spec) error for _, service := range services { if _, err := responder.Add(service); err != nil { - logger.Error(ctx, "could not add mdns service", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not add mdns service", logger.E(err)) + sentry.CaptureException(err) continue } @@ -148,7 +153,9 @@ func (c *Controller) updateResponder(ctx context.Context, spec *mdns.Spec) error defer c.stopResponder(ctx) if err := responder.Respond(ctx); err != nil && !errors.Is(err, context.Canceled) { - logger.Error(ctx, "could not respond to mdns queries", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not respond to mdns queries", logger.E(err)) + sentry.CaptureException(err) } }() diff --git a/internal/agent/controller/openwrt/sysupgrade_controller.go b/internal/agent/controller/openwrt/sysupgrade_controller.go index ebb1001..75f0fc8 100644 --- a/internal/agent/controller/openwrt/sysupgrade_controller.go +++ b/internal/agent/controller/openwrt/sysupgrade_controller.go @@ -11,6 +11,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt/spec/sysupgrade" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) @@ -64,11 +65,13 @@ func (c *SysUpgradeController) Reconcile(ctx context.Context, state *agent.State defer func() { if err := os.RemoveAll(downloadDir); err != nil { + err = errors.WithStack(err) logger.Error( ctx, "could not remove download direction", - logger.E(errors.WithStack(err)), + logger.E(err), logger.F("downloadDir", downloadDir), ) + sentry.CaptureException(err) } }() diff --git a/internal/agent/controller/openwrt/uci_controller.go b/internal/agent/controller/openwrt/uci_controller.go index ea37059..ea31271 100644 --- a/internal/agent/controller/openwrt/uci_controller.go +++ b/internal/agent/controller/openwrt/uci_controller.go @@ -9,6 +9,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/openwrt/uci" ucispec "forge.cadoles.com/Cadoles/emissary/internal/spec/uci" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) @@ -46,7 +47,9 @@ func (c *UCIController) Reconcile(ctx context.Context, state *agent.State) error } if err := c.updateConfiguration(ctx, uciSpec); err != nil { - logger.Error(ctx, "could not update configuration", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not update configuration", logger.E(err)) + sentry.CaptureException(err) return nil } diff --git a/internal/agent/controller/persistence/controller.go b/internal/agent/controller/persistence/controller.go index ad595f8..a3796d5 100644 --- a/internal/agent/controller/persistence/controller.go +++ b/internal/agent/controller/persistence/controller.go @@ -10,6 +10,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/spec" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) @@ -145,7 +146,9 @@ func (c *Controller) writeState(ctx context.Context, state *agent.State) error { return } - logger.Error(ctx, "could not remove temporary file", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not remove temporary file", logger.E(err)) + sentry.CaptureException(err) } }() @@ -155,7 +158,9 @@ func (c *Controller) writeState(ctx context.Context, state *agent.State) error { return } - logger.Error(ctx, "could not close temporary file", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not close temporary file", logger.E(err)) + sentry.CaptureException(err) } }() diff --git a/internal/agent/controller/proxy/controller.go b/internal/agent/controller/proxy/controller.go index eb5cc95..8830d53 100644 --- a/internal/agent/controller/proxy/controller.go +++ b/internal/agent/controller/proxy/controller.go @@ -7,6 +7,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/proxy" spec "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy" + "github.com/getsentry/sentry-go" "github.com/mitchellh/hashstructure/v2" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" @@ -58,11 +59,13 @@ func (c *Controller) stopAllProxies(ctx context.Context) { logger.Info(ctx, "stopping proxy", logger.F("proxyID", proxyID)) if err := entry.Proxy.Stop(); err != nil { + err = errors.WithStack(err) logger.Error( ctx, "error while stopping proxy", logger.F("proxyID", proxyID), - logger.E(errors.WithStack(err)), + logger.E(err), ) + sentry.CaptureException(err) delete(c.proxies, proxyID) } @@ -79,11 +82,13 @@ func (c *Controller) updateProxies(ctx context.Context, spec *spec.Spec) { logger.Info(ctx, "stopping proxy", logger.F("proxyID", proxyID)) if err := entry.Proxy.Stop(); err != nil { + err = errors.WithStack(err) logger.Error( ctx, "error while stopping proxy", logger.F("proxyID", proxyID), - logger.E(errors.WithStack(err)), + logger.E(err), ) + sentry.CaptureException(err) delete(c.proxies, proxyID) } @@ -94,7 +99,9 @@ func (c *Controller) updateProxies(ctx context.Context, spec *spec.Spec) { proxyCtx := logger.With(ctx, logger.F("proxyID", proxyID)) if err := c.updateProxy(ctx, proxyID, proxySpec); err != nil { - logger.Error(proxyCtx, "could not update proxy", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(proxyCtx, "could not update proxy", logger.E(err)) + sentry.CaptureException(err) continue } } diff --git a/internal/agent/controller/proxy/reverse_proxy.go b/internal/agent/controller/proxy/reverse_proxy.go index 1aedaa6..9d38ab5 100644 --- a/internal/agent/controller/proxy/reverse_proxy.go +++ b/internal/agent/controller/proxy/reverse_proxy.go @@ -6,6 +6,7 @@ import ( "sync" "forge.cadoles.com/Cadoles/emissary/internal/proxy" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) @@ -38,12 +39,16 @@ func (p *ReverseProxy) Start(ctx context.Context, addr string, funcs ...proxy.Op go func() { defer func() { if err := p.Stop(); err != nil { - logger.Error(ctx, "error while stopping gateway", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "error while stopping gateway", logger.E(err)) + sentry.CaptureException(err) } }() if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - logger.Error(ctx, "error while listening", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "error while listening", logger.E(err)) + sentry.CaptureException(err) } }() diff --git a/internal/agent/controller/spec/controller.go b/internal/agent/controller/spec/controller.go index 87689b5..e729426 100644 --- a/internal/agent/controller/spec/controller.go +++ b/internal/agent/controller/spec/controller.go @@ -6,6 +6,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/datastore" "forge.cadoles.com/Cadoles/emissary/pkg/client" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) @@ -40,14 +41,16 @@ func (c *Controller) reconcileAgent(ctx context.Context, client *client.Client, ctx = logger.With(ctx, logger.F("agentID", agent.ID)) if agent.Status != datastore.AgentStatusAccepted { - logger.Error(ctx, "unexpected agent status", logger.F("status", agent.Status)) + logger.Warn(ctx, "unexpected agent status", logger.F("status", agent.Status)) return nil } specs, err := client.GetAgentSpecs(ctx, agent.ID) if err != nil { - logger.Error(ctx, "could not retrieve agent specs", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not retrieve agent specs", logger.E(err)) + sentry.CaptureException(err) return nil } diff --git a/internal/command/main.go b/internal/command/main.go index dea056a..131a3d3 100644 --- a/internal/command/main.go +++ b/internal/command/main.go @@ -7,8 +7,11 @@ import ( "sort" "time" + "forge.cadoles.com/Cadoles/emissary/internal/command/common" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "github.com/urfave/cli/v2" + "gitlab.com/wpetit/goweb/logger" ) func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands ...*cli.Command) { @@ -46,6 +49,23 @@ func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands return errors.WithStack(err) } + conf, err := common.LoadConfig(ctx) + if err != nil { + return errors.Wrap(err, "Could not load configuration") + } + + if conf.Sentry.DSN != "" { + err = sentry.Init(sentry.ClientOptions{ + Dsn: string(conf.Sentry.DSN), + Debug: ctx.Bool("debug"), + AttachStacktrace: true, + Environment: string(conf.Sentry.Environment), + }) + if err != nil { + logger.Error(ctx.Context, "could not initialize sentry", logger.E(errors.WithStack(err))) + } + } + return nil }, Flags: []cli.Flag{ @@ -84,11 +104,15 @@ func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands }, } + defer sentry.Flush(2 * time.Second) + app.ExitErrHandler = func(ctx *cli.Context, err error) { if err == nil { return } + sentry.CaptureException(err) + debug := ctx.Bool("debug") if !debug { diff --git a/internal/command/server/database/ping.go b/internal/command/server/database/ping.go index 683d700..772db1d 100644 --- a/internal/command/server/database/ping.go +++ b/internal/command/server/database/ping.go @@ -5,6 +5,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/command/common" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "github.com/urfave/cli/v2" "gitlab.com/wpetit/goweb/logger" @@ -32,7 +33,9 @@ func PingCommand() *cli.Command { defer func() { if err := db.Close(); err != nil { - logger.Error(ctx.Context, "error while closing database connection", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx.Context, "error while closing database connection", logger.E(err)) + sentry.CaptureException(err) } }() diff --git a/internal/config/config.go b/internal/config/config.go index 3f9ea7b..e3ec9aa 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,6 +11,7 @@ import ( // Config definition type Config struct { Logger LoggerConfig `yaml:"logger"` + Sentry SentryConfig `yaml:"sentry"` Server ServerConfig `yaml:"server"` Agent AgentConfig `yaml:"agent"` } @@ -44,6 +45,7 @@ func NewDefault() *Config { Logger: NewDefaultLoggerConfig(), Agent: NewDefaultAgentConfig(), Server: NewDefaultServerConfig(), + Sentry: NewDefaultSentryConfig(), } } diff --git a/internal/config/sentry.go b/internal/config/sentry.go new file mode 100644 index 0000000..960aee8 --- /dev/null +++ b/internal/config/sentry.go @@ -0,0 +1,18 @@ +package config + +import ( + "os" +) + +type SentryConfig struct { + DSN InterpolatedString `yaml:"dsn"` + Environment InterpolatedString `yaml:"environment"` +} + +func NewDefaultSentryConfig() SentryConfig { + hostname, _ := os.Hostname() + return SentryConfig{ + DSN: "", + Environment: InterpolatedString(hostname), + } +} diff --git a/internal/datastore/sqlite/agent_repository.go b/internal/datastore/sqlite/agent_repository.go index 7149b76..37b41c2 100644 --- a/internal/datastore/sqlite/agent_repository.go +++ b/internal/datastore/sqlite/agent_repository.go @@ -9,6 +9,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/datastore" + "github.com/getsentry/sentry-go" "github.com/lestrrat-go/jwx/v2/jwk" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" @@ -72,7 +73,9 @@ func (r *AgentRepository) GetSpecs(ctx context.Context, agentID datastore.AgentI defer func() { if err := rows.Close(); err != nil { - logger.Error(ctx, "could not close rows", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not close rows", logger.E(err)) + sentry.CaptureException(err) } }() @@ -226,7 +229,9 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer defer func() { if err := rows.Close(); err != nil { + err = errors.WithStack(err) logger.Error(ctx, "could not close rows", logger.E(errors.WithStack(err))) + sentry.CaptureException(err) } }() @@ -544,7 +549,9 @@ func (r *AgentRepository) withTx(ctx context.Context, fn func(*sql.Tx) error) er return } - logger.Error(ctx, "could not rollback transaction", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not rollback transaction", logger.E(err)) + sentry.CaptureException(err) } }() diff --git a/internal/server/agent_api.go b/internal/server/agent_api.go index 92f568c..eb16c68 100644 --- a/internal/server/agent_api.go +++ b/internal/server/agent_api.go @@ -9,6 +9,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/agent/metadata" "forge.cadoles.com/Cadoles/emissary/internal/datastore" "forge.cadoles.com/Cadoles/emissary/internal/jwk" + "github.com/getsentry/sentry-go" "github.com/go-chi/chi" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/api" @@ -39,7 +40,9 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { keySet, err := jwk.Parse(registerAgentReq.KeySet) if err != nil { - logger.Error(ctx, "could not parse key set", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not parse key set", logger.E(err)) + sentry.CaptureException(err) api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -51,14 +54,16 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { validSignature, err := jwk.Verify(keySet, registerAgentReq.Signature, registerAgentReq.Thumbprint, registerAgentReq.Metadata) if err != nil { - logger.Error(ctx, "could not validate signature", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not validate signature", logger.E(err)) + sentry.CaptureException(err) api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return } if !validSignature { - logger.Error(ctx, "conflicting signature", logger.F("signature", registerAgentReq.Signature)) + logger.Warn(ctx, "conflicting signature", logger.F("signature", registerAgentReq.Signature)) api.ErrorResponse(w, http.StatusConflict, ErrCodeConflict, nil) return @@ -74,7 +79,9 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { ) if err != nil { if !errors.Is(err, datastore.ErrAlreadyExist) { - logger.Error(ctx, "could not create agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not create agent", logger.E(err)) + sentry.CaptureException(err) api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -86,14 +93,18 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { datastore.WithAgentQueryLimit(1), ) if err != nil { - logger.Error(ctx, "could not retrieve agents", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not retrieve agents", logger.E(err)) + sentry.CaptureException(err) api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return } if len(agents) == 0 { - logger.Error(ctx, "could not retrieve matching agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not retrieve matching agent", logger.E(err)) + sentry.CaptureException(err) api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeNotFound, nil) @@ -104,10 +115,13 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { agent, err = s.agentRepo.Get(ctx, agentID) if err != nil { + err = errors.WithStack(err) logger.Error( ctx, "could not retrieve agent", - logger.E(errors.WithStack(err)), logger.F("agentID", agentID), + logger.E(err), logger.F("agentID", agentID), ) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -115,7 +129,9 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { validSignature, err = jwk.Verify(agent.KeySet.Set, registerAgentReq.Signature, registerAgentReq.Thumbprint, registerAgentReq.Metadata) if err != nil { - logger.Error(ctx, "could not validate signature using previous keyset", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not validate signature using previous keyset", logger.E(err)) + sentry.CaptureException(err) api.ErrorResponse(w, http.StatusConflict, ErrCodeConflict, nil) @@ -129,7 +145,10 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) { datastore.WithAgentUpdateThumbprint(registerAgentReq.Thumbprint), ) if err != nil { - logger.Error(ctx, "could not update agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not update agent", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -177,7 +196,10 @@ func (s *Server) updateAgent(w http.ResponseWriter, r *http.Request) { options..., ) if err != nil { - logger.Error(ctx, "could not update agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not update agent", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -258,7 +280,10 @@ func (s *Server) queryAgents(w http.ResponseWriter, r *http.Request) { options..., ) if err != nil { - logger.Error(ctx, "could not list agents", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not list agents", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -292,7 +317,10 @@ func (s *Server) deleteAgent(w http.ResponseWriter, r *http.Request) { return } - logger.Error(ctx, "could not delete agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not delete agent", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -324,7 +352,10 @@ func (s *Server) getAgent(w http.ResponseWriter, r *http.Request) { return } - logger.Error(ctx, "could not get agent", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not get agent", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -356,7 +387,10 @@ func getIntQueryParam(w http.ResponseWriter, r *http.Request, param string, defa if rawValue != "" { value, err := strconv.ParseInt(rawValue, 10, 64) if err != nil { - logger.Error(r.Context(), "could not parse int param", logger.F("param", param), logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(r.Context(), "could not parse int param", logger.F("param", param), logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil) return 0, false @@ -389,7 +423,10 @@ func getIntSliceValues(w http.ResponseWriter, r *http.Request, param string, def for _, rv := range rawValues { value, err := strconv.ParseInt(rv, 10, 64) if err != nil { - logger.Error(r.Context(), "could not parse int slice param", logger.F("param", param), logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(r.Context(), "could not parse int slice param", logger.F("param", param), logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil) return nil, false diff --git a/internal/server/authorization.go b/internal/server/authorization.go index 304da36..c5dcc99 100644 --- a/internal/server/authorization.go +++ b/internal/server/authorization.go @@ -8,6 +8,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/auth" "forge.cadoles.com/Cadoles/emissary/internal/auth/agent" "forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty" + "github.com/getsentry/sentry-go" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/api" "gitlab.com/wpetit/goweb/logger" @@ -124,7 +125,9 @@ func assertRequestUser(w http.ResponseWriter, r *http.Request) (auth.User, bool) ctx := r.Context() user, err := auth.CtxUser(ctx) if err != nil { - logger.Error(ctx, "could not retrieve user", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not retrieve user", logger.E(err)) + sentry.CaptureException(err) forbidden(w, r) @@ -147,7 +150,7 @@ func forbidden(w http.ResponseWriter, r *http.Request) { } func logUnexpectedUserType(ctx context.Context, user auth.User) { - logger.Error( + logger.Warn( ctx, "unexpected user type", logger.F("subject", user.Subject()), logger.F("type", fmt.Sprintf("%T", user)), diff --git a/internal/server/spec_api.go b/internal/server/spec_api.go index f7a0b26..bdb2b66 100644 --- a/internal/server/spec_api.go +++ b/internal/server/spec_api.go @@ -6,6 +6,7 @@ import ( "forge.cadoles.com/Cadoles/emissary/internal/datastore" "forge.cadoles.com/Cadoles/emissary/internal/spec" + "github.com/getsentry/sentry-go" "github.com/go-chi/chi" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/api" @@ -44,7 +45,10 @@ func (s *Server) updateSpec(w http.ResponseWriter, r *http.Request) { data.Message = validationErr.Error() } - logger.Error(ctx, "could not validate spec", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not validate spec", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, data) return @@ -70,7 +74,10 @@ func (s *Server) updateSpec(w http.ResponseWriter, r *http.Request) { return } - logger.Error(ctx, "could not update spec", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not update spec", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -99,7 +106,10 @@ func (s *Server) getAgentSpecs(w http.ResponseWriter, r *http.Request) { return } - logger.Error(ctx, "could not list specs", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not list specs", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -141,7 +151,10 @@ func (s *Server) deleteSpec(w http.ResponseWriter, r *http.Request) { return } - logger.Error(ctx, "could not delete spec", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(ctx, "could not delete spec", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil) return @@ -159,7 +172,10 @@ func getSpecID(w http.ResponseWriter, r *http.Request) (datastore.SpecID, bool) specID, err := strconv.ParseInt(rawSpecID, 10, 64) if err != nil { - logger.Error(r.Context(), "could not parse spec id", logger.E(errors.WithStack(err))) + err = errors.WithStack(err) + logger.Error(r.Context(), "could not parse spec id", logger.E(err)) + sentry.CaptureException(err) + api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil) return 0, false diff --git a/misc/spec-samples/app.emissary.cadoles.com.json b/misc/spec-samples/app.emissary.cadoles.com.json index 361509a..ff61c0e 100644 --- a/misc/spec-samples/app.emissary.cadoles.com.json +++ b/misc/spec-samples/app.emissary.cadoles.com.json @@ -12,8 +12,8 @@ } }, "edge.sdk.client.test": { - "url": "https://emissary.cadol.es/files/apps/edge.sdk.client.test_v2023.4.20-20c4189.zip", - "sha256sum": "1edeb4aa75c1675db49cf27367b1537234a04526848ea6657931ca63f26e5dae", + "url": "https://emissary.cadol.es/files/apps/edge.sdk.client.test_2023.10.11-stable.943.4d064de.zip", + "sha256sum": "5419054e09342f5b9834771c087385b0fb425ebdbca3d91aa69eab98d909ca57", "address": ":8084", "format": "zip", "storage": { @@ -21,13 +21,23 @@ "shareStoreDsn": "sqlite://apps/data/sharestore.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000", "documentStoreDsn": "sqlite://apps/data/edge.sdk.client.test/documentstore.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000" } + }, + "arcad.diffusion": { + "url": "https://emissary.cadol.es/files/apps/arcad.diffusion_v2023.10.12-cab0070.zip", + "sha256sum": "db58ec1ed463092fea2ee27cfd33dea5259739f1c6a4c9bf909ffd22392ee449", + "address": ":8085", + "format": "zip", + "storage": { + "blobStoreDsn": "sqlite://apps/data/arcad.diffusion/blobstore.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000", + "shareStoreDsn": "sqlite://apps/data/sharestore.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000", + "documentStoreDsn": "sqlite://apps/data/arcad.diffusion/documentstore.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000" + } } }, "config": { "appUrlResolving": { "ifaceMappings": { - "eth0": "http://{{ .DeviceIP }}:{{ .AppPort }}", - "eth1": "http://{{ .DeviceIP }}:{{ .AppPort }}" + "lo": "http://{{ .DeviceIP }}:{{ .AppPort }}" }, "defaultUrlTemplate": "http://{{ last ( splitList \".\" ( toString .Manifest.ID ) ) }}.localhost.arcad.lan:8080" }, diff --git a/misc/spec-samples/mdns.emissary.cadoles.com.json b/misc/spec-samples/mdns.emissary.cadoles.com.json index cf9fa9b..aca60f4 100644 --- a/misc/spec-samples/mdns.emissary.cadoles.com.json +++ b/misc/spec-samples/mdns.emissary.cadoles.com.json @@ -14,6 +14,11 @@ "type": "_http._tcp", "port": 8080, "host": "arcad-test" + }, + "diffusion": { + "type": "_http._tcp", + "port": 8080, + "host": "arcad-diffusion" } } } \ No newline at end of file diff --git a/misc/spec-samples/proxy.emissary.cadoles.com.json b/misc/spec-samples/proxy.emissary.cadoles.com.json index 15bc510..8dcf1c6 100644 --- a/misc/spec-samples/proxy.emissary.cadoles.com.json +++ b/misc/spec-samples/proxy.emissary.cadoles.com.json @@ -11,6 +11,10 @@ "hostPattern": "test.localhost.arcad.lan:*", "target": "http://localhost:8084" }, + { + "hostPattern": "diffusion.localhost.arcad.lan:*", + "target": "http://localhost:8085" + }, { "hostPattern": "arcad-hextris.local:*", "target": "http://localhost:8083" @@ -19,6 +23,10 @@ "hostPattern": "arcad-test.local:*", "target": "http://localhost:8084" }, + { + "hostPattern": "arcad-diffusion.local:*", + "target": "http://localhost:8085" + }, { "hostPattern": "*", "target": "http://localhost:8084" diff --git a/misc/spec-samples/uci.emissary.cadoles.com.json b/misc/spec-samples/uci.emissary.cadoles.com.json index b2cfa94..28b8ffa 100644 --- a/misc/spec-samples/uci.emissary.cadoles.com.json +++ b/misc/spec-samples/uci.emissary.cadoles.com.json @@ -2063,10 +2063,5 @@ } ] }, - "postImportCommands": [ - { - "args": [], - "command": "reload_config" - } - ] + "postImportCommands": [] } \ No newline at end of file