Some checks are pending
Security Checks / security (push) Waiting to run
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
96 lines
2.4 KiB
Go
96 lines
2.4 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"sync/atomic"
|
|
)
|
|
|
|
var ErrRuntimeSnapshotUnavailable = errors.New("runtime configuration snapshot unavailable")
|
|
|
|
type SnapshotStore struct {
|
|
current atomic.Pointer[RuntimeConfig]
|
|
options RuntimeValidationOptions
|
|
}
|
|
|
|
func NewSnapshotStore(initial RuntimeConfig, options RuntimeValidationOptions) (*SnapshotStore, error) {
|
|
store := &SnapshotStore{options: options}
|
|
if err := store.Publish(initial); err != nil {
|
|
return nil, err
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
func (s *SnapshotStore) Validate(candidate RuntimeConfig) error {
|
|
if s == nil {
|
|
return ErrRuntimeSnapshotUnavailable
|
|
}
|
|
return ValidateRuntimeConfig(candidate, s.options)
|
|
}
|
|
|
|
func (s *SnapshotStore) Publish(candidate RuntimeConfig) error {
|
|
if s == nil {
|
|
return ErrRuntimeSnapshotUnavailable
|
|
}
|
|
candidate = cloneRuntimeConfig(candidate)
|
|
if err := s.Validate(candidate); err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
current := s.current.Load()
|
|
if current != nil && candidate.Generation < current.Generation {
|
|
return ErrGenerationConflict
|
|
}
|
|
if s.current.CompareAndSwap(current, &candidate) {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *SnapshotStore) Snapshot() (RuntimeConfig, error) {
|
|
if s == nil {
|
|
return RuntimeConfig{}, ErrRuntimeSnapshotUnavailable
|
|
}
|
|
current := s.current.Load()
|
|
if current == nil {
|
|
return RuntimeConfig{}, ErrRuntimeSnapshotUnavailable
|
|
}
|
|
return cloneRuntimeConfig(*current), nil
|
|
}
|
|
|
|
func (s *SnapshotStore) Generation() uint64 {
|
|
if s == nil {
|
|
return 0
|
|
}
|
|
current := s.current.Load()
|
|
if current == nil {
|
|
return 0
|
|
}
|
|
return current.Generation
|
|
}
|
|
|
|
func cloneRuntimeConfig(source RuntimeConfig) RuntimeConfig {
|
|
clone := source
|
|
clone.Root = cloneRootConfig(source.Root)
|
|
clone.SMTP = source.SMTP
|
|
clone.Email = source.Email
|
|
clone.SSL = source.SSL
|
|
clone.Plugins = clonePluginsSection(source.Plugins)
|
|
return clone
|
|
}
|
|
|
|
func clonePluginsSection(source PluginsSectionConfig) PluginsSectionConfig {
|
|
clone := source
|
|
if source.Plugins == nil {
|
|
return clone
|
|
}
|
|
clone.Plugins = make(map[string]PluginInstance, len(source.Plugins))
|
|
for logicalID, plugin := range source.Plugins {
|
|
clonedPlugin := plugin
|
|
clonedPlugin.Permissions.Capabilities = append([]string(nil), plugin.Permissions.Capabilities...)
|
|
clonedPlugin.Permissions.Network = append([]PluginNetworkTarget(nil), plugin.Permissions.Network...)
|
|
clonedPlugin.Settings = append([]byte(nil), plugin.Settings...)
|
|
clone.Plugins[logicalID] = clonedPlugin
|
|
}
|
|
return clone
|
|
}
|