fix: fail fast when the announcement store is unreachable

Replace reva's cache.NewNatsKeyValue (which retries with backoff and can hang
the web service startup) with a direct nats.Connect + jetstream setup like the
graph service, so an unreachable or misconfigured store surfaces as a clear
startup error instead of a hang.
This commit is contained in:
Dominik Schmidt
2026-07-29 11:53:58 +02:00
parent d27b7ef40a
commit 6d2d6c38ec

View File

@@ -1,16 +1,20 @@
package http
import (
"errors"
"fmt"
"path"
"strings"
chimiddleware "github.com/go-chi/chi/v5/middleware"
"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/jetstream"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"github.com/opencloud-eu/reva/v2/pkg/storage/cache"
"go-micro.dev/v4"
"github.com/opencloud-eu/opencloud/pkg/cors"
"github.com/opencloud-eu/opencloud/pkg/middleware"
natspkg "github.com/opencloud-eu/opencloud/pkg/nats"
"github.com/opencloud-eu/opencloud/pkg/registry"
"github.com/opencloud-eu/opencloud/pkg/service/http"
"github.com/opencloud-eu/opencloud/pkg/version"
@@ -78,18 +82,29 @@ func Server(opts ...Option) (http.Service, error) {
fsx.NewBasePathFs(fsx.FromIOFS(web.Assets), "assets/themes"),
)
// NATS JetStream key-value store for runtime managed web settings, e.g. the announcement banner
kv, err := cache.NewNatsKeyValue(cache.Config{
Nodes: options.Config.Store.Nodes,
Database: options.Config.Store.Database,
AuthUsername: options.Config.Store.AuthUsername,
AuthPassword: options.Config.Store.AuthPassword,
TLSEnabled: options.Config.Store.EnableTLS,
TLSInsecure: options.Config.Store.TLSInsecure,
TLSRootCACertificate: options.Config.Store.TLSRootCACertificate,
}, &options.Logger.Logger)
// NATS JetStream key-value store for runtime managed web settings, e.g. the announcement banner.
// Connect eagerly and fail fast: an unreachable store means the feature would be broken, so a
// clear startup error is preferable to silently degrading.
natsConn, err := nats.Connect(
strings.Join(options.Config.Store.Nodes, ","),
natspkg.Secure(options.Config.Store.EnableTLS, options.Config.Store.TLSInsecure, options.Config.Store.TLSRootCACertificate),
nats.UserInfo(options.Config.Store.AuthUsername, options.Config.Store.AuthPassword),
)
if err != nil {
return http.Service{}, fmt.Errorf("could not initialize announcement store: %w", err)
return http.Service{}, fmt.Errorf("could not connect to nats for the announcement store: %w", err)
}
js, err := jetstream.New(natsConn)
if err != nil {
return http.Service{}, fmt.Errorf("could not create jetstream context for the announcement store: %w", err)
}
kv, err := js.KeyValue(options.Context, options.Config.Store.Database)
if err != nil {
if !errors.Is(err, jetstream.ErrBucketNotFound) {
return http.Service{}, fmt.Errorf("could not open the announcement store bucket %q: %w", options.Config.Store.Database, err)
}
if kv, err = js.CreateKeyValue(options.Context, jetstream.KeyValueConfig{Bucket: options.Config.Store.Database}); err != nil {
return http.Service{}, fmt.Errorf("could not create the announcement store bucket %q: %w", options.Config.Store.Database, err)
}
}
announcementStore := announcement.NewStore(kv)