api/server: get features from a callback fn

Passing around a bare pointer to the map of configured features in order
to propagate to consumers changes to the configuration across reloads is
dangerous. Map operations are not atomic, so concurrently reading from
the map while it is being updated is a data race as there is no
synchronization. Use a getter function to retrieve the current features
map so the features can be retrieved race-free.

Remove the unused features argument from the build router.

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider 2023-05-24 14:03:37 -04:00
parent c679da9ae1
commit 982e4fb448
5 changed files with 21 additions and 17 deletions

View file

@ -12,15 +12,13 @@ type buildRouter struct {
backend Backend
daemon experimentalProvider
routes []router.Route
features *map[string]bool
}
// NewRouter initializes a new build router
func NewRouter(b Backend, d experimentalProvider, features *map[string]bool) router.Router {
func NewRouter(b Backend, d experimentalProvider) router.Router {
r := &buildRouter{
backend: b,
daemon: d,
features: features,
}
r.initRoutes()
return r

View file

@ -12,11 +12,11 @@ type systemRouter struct {
cluster ClusterBackend
routes []router.Route
builder *buildkit.Builder
features *map[string]bool
features func() map[string]bool
}
// NewRouter initializes a new system router
func NewRouter(b Backend, c ClusterBackend, builder *buildkit.Builder, features *map[string]bool) router.Router {
func NewRouter(b Backend, c ClusterBackend, builder *buildkit.Builder, features func() map[string]bool) router.Router {
r := &systemRouter{
backend: b,
cluster: c,

View file

@ -31,7 +31,7 @@ func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Add("Pragma", "no-cache")
builderVersion := build.BuilderVersion(*s.features)
builderVersion := build.BuilderVersion(s.features())
if bv := builderVersion; bv != "" {
w.Header().Set("Builder-Version", string(bv))
}

View file

@ -337,7 +337,7 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
type routerOptions struct {
sessionManager *session.Manager
buildBackend *buildbackend.Backend
features *map[string]bool
features func() map[string]bool
buildkit *buildkit.Builder
daemon *daemon.Daemon
cluster *cluster.Cluster
@ -357,7 +357,7 @@ func newRouterOptions(ctx context.Context, config *config.Config, d *daemon.Daem
cgroupParent := newCgroupParent(config)
ro := routerOptions{
sessionManager: sm,
features: d.Features(),
features: d.Features,
daemon: d,
}
@ -593,9 +593,9 @@ func (opts routerOptions) Build() []router.Router {
opts.daemon.ImageService().DistributionServices().ImageStore,
opts.daemon.ImageService().DistributionServices().LayerStore,
),
systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildkit, opts.features),
systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildkit, opts.daemon.Features),
volume.NewRouter(opts.daemon.VolumesService(), opts.cluster),
build.NewRouter(opts.buildBackend, opts.daemon, opts.features),
build.NewRouter(opts.buildBackend, opts.daemon),
sessionrouter.NewRouter(opts.sessionManager),
swarmrouter.NewRouter(opts.cluster),
pluginrouter.NewRouter(opts.daemon.PluginManager()),

View file

@ -154,8 +154,14 @@ func (daemon *Daemon) HasExperimental() bool {
}
// Features returns the features map from configStore
func (daemon *Daemon) Features() *map[string]bool {
return &daemon.configStore.Features
func (daemon *Daemon) Features() map[string]bool {
daemon.configStore.Lock()
defer daemon.configStore.Unlock()
f := make(map[string]bool, len(daemon.configStore.Features))
for k, v := range daemon.configStore.Features {
f[k] = v
}
return f
}
// UsesSnapshotter returns true if feature flag to use containerd snapshotter is enabled