144 lines
3.3 KiB
Go
144 lines
3.3 KiB
Go
// Package confmigrate provides a way to upgrade the YAML configuration file.
|
|
package confmigrate
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/AdguardTeam/golibs/log"
|
|
yaml "gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// CurrentSchemaVersion is the current schema version.
|
|
const CurrentSchemaVersion = 26
|
|
|
|
// These aliases are provided for convenience.
|
|
type (
|
|
yarr = []any
|
|
yobj = map[string]any
|
|
)
|
|
|
|
// Config is a the configuration for initializing a [Migrator].
|
|
type Config struct {
|
|
// WorkingDir is an absolute path to the working directory of AdGuardHome.
|
|
WorkingDir string
|
|
}
|
|
|
|
// Migrator performs the YAML configuration file migrations.
|
|
type Migrator struct {
|
|
// workingDir is an absolute path to the working directory of AdGuardHome.
|
|
workingDir string
|
|
}
|
|
|
|
// New creates a new Migrator.
|
|
func New(cfg *Config) (m *Migrator) {
|
|
return &Migrator{
|
|
workingDir: cfg.WorkingDir,
|
|
}
|
|
}
|
|
|
|
// Migrate does necessary upgrade operations if needed. It returns the new
|
|
// configuration file body, and a boolean indicating whether the configuration
|
|
// file was actually upgraded.
|
|
func (m *Migrator) Migrate(body []byte) (newBody []byte, upgraded bool, err error) {
|
|
// read a config file into an interface map, so we can manipulate values without losing any
|
|
diskConf := yobj{}
|
|
err = yaml.Unmarshal(body, &diskConf)
|
|
if err != nil {
|
|
log.Printf("parsing config file for upgrade: %s", err)
|
|
|
|
return nil, false, err
|
|
}
|
|
|
|
schemaVersionVal, ok := diskConf["schema_version"]
|
|
log.Tracef("got schema version %v", schemaVersionVal)
|
|
if !ok {
|
|
// no schema version, set it to 0
|
|
schemaVersionVal = 0
|
|
}
|
|
|
|
schemaVersion, ok := schemaVersionVal.(int)
|
|
if !ok {
|
|
err = fmt.Errorf("configuration file contains non-integer schema_version, abort")
|
|
log.Println(err)
|
|
|
|
return nil, false, err
|
|
}
|
|
|
|
if schemaVersion == CurrentSchemaVersion {
|
|
// do nothing
|
|
return body, false, nil
|
|
}
|
|
|
|
err = m.upgradeConfigSchema(schemaVersion, diskConf)
|
|
if err != nil {
|
|
log.Printf("upgrading configuration file: %s", err)
|
|
|
|
return nil, false, err
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
enc := yaml.NewEncoder(buf)
|
|
enc.SetIndent(2)
|
|
|
|
err = enc.Encode(diskConf)
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("generating new config: %w", err)
|
|
}
|
|
|
|
return buf.Bytes(), true, nil
|
|
}
|
|
|
|
// upgradeFunc is a function that upgrades a config and returns an error.
|
|
type upgradeFunc = func(diskConf yobj) (err error)
|
|
|
|
// Upgrade from oldVersion to newVersion
|
|
func (m *Migrator) upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
|
|
upgrades := []upgradeFunc{
|
|
m.upgradeSchema0to1,
|
|
m.upgradeSchema1to2,
|
|
upgradeSchema2to3,
|
|
upgradeSchema3to4,
|
|
upgradeSchema4to5,
|
|
upgradeSchema5to6,
|
|
upgradeSchema6to7,
|
|
upgradeSchema7to8,
|
|
upgradeSchema8to9,
|
|
upgradeSchema9to10,
|
|
upgradeSchema10to11,
|
|
upgradeSchema11to12,
|
|
upgradeSchema12to13,
|
|
upgradeSchema13to14,
|
|
upgradeSchema14to15,
|
|
upgradeSchema15to16,
|
|
upgradeSchema16to17,
|
|
upgradeSchema17to18,
|
|
upgradeSchema18to19,
|
|
upgradeSchema19to20,
|
|
upgradeSchema20to21,
|
|
upgradeSchema21to22,
|
|
upgradeSchema22to23,
|
|
upgradeSchema23to24,
|
|
upgradeSchema24to25,
|
|
upgradeSchema25to26,
|
|
}
|
|
|
|
n := 0
|
|
for i, u := range upgrades {
|
|
if i >= oldVersion {
|
|
err = u(diskConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
n++
|
|
}
|
|
}
|
|
|
|
if n == 0 {
|
|
return fmt.Errorf("unknown configuration schema version %d", oldVersion)
|
|
}
|
|
|
|
return nil
|
|
}
|