mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-18 14:54:19 +01:00
Merge pull request #4109 from pmores/drop-in-cfg-files-support
Drop in cfg files support
This commit is contained in:
@@ -10,8 +10,10 @@ package katautils
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
goruntime "runtime"
|
||||
"strings"
|
||||
|
||||
@@ -178,6 +180,20 @@ type agent struct {
|
||||
DialTimeout uint32 `toml:"dial_timeout"`
|
||||
}
|
||||
|
||||
func (orig *tomlConfig) Clone() tomlConfig {
|
||||
clone := *orig
|
||||
clone.Hypervisor = make(map[string]hypervisor)
|
||||
clone.Agent = make(map[string]agent)
|
||||
|
||||
for key, value := range orig.Hypervisor {
|
||||
clone.Hypervisor[key] = value
|
||||
}
|
||||
for key, value := range orig.Agent {
|
||||
clone.Agent[key] = value
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func (h hypervisor) path() (string, error) {
|
||||
p := h.Path
|
||||
|
||||
@@ -1322,9 +1338,179 @@ func decodeConfig(configPath string) (tomlConfig, string, error) {
|
||||
return tomlConf, resolved, err
|
||||
}
|
||||
|
||||
err = decodeDropIns(resolved, &tomlConf)
|
||||
if err != nil {
|
||||
return tomlConf, resolved, err
|
||||
}
|
||||
|
||||
return tomlConf, resolved, nil
|
||||
}
|
||||
|
||||
func decodeDropIns(mainConfigPath string, tomlConf *tomlConfig) error {
|
||||
configDir := filepath.Dir(mainConfigPath)
|
||||
dropInDir := filepath.Join(configDir, "config.d")
|
||||
|
||||
files, err := ioutil.ReadDir(dropInDir)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("error reading %q directory: %s", dropInDir, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
dropInFpath := filepath.Join(dropInDir, file.Name())
|
||||
|
||||
err = updateFromDropIn(dropInFpath, tomlConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateFromDropIn(dropInFpath string, tomlConf *tomlConfig) error {
|
||||
configData, err := os.ReadFile(dropInFpath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading file %q: %s", dropInFpath, err)
|
||||
}
|
||||
|
||||
// Ordinarily, BurntSushi only updates fields of tomlConfig that are
|
||||
// changed by the file and leaves the rest alone. This doesn't apply
|
||||
// though to tomlConfig substructures that are stored in maps. Their
|
||||
// previous contents are erased by toml.Decode() and only fields changed by
|
||||
// the file are set. To work around this, a bit of juggling is needed to
|
||||
// preserve the previous contents and merge them manually with the incoming
|
||||
// changes afterwards, using reflection.
|
||||
tomlConfOrig := tomlConf.Clone()
|
||||
|
||||
var md toml.MetaData
|
||||
md, err = toml.Decode(string(configData), &tomlConf)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding file %q: %s", dropInFpath, err)
|
||||
}
|
||||
|
||||
if len(md.Undecoded()) > 0 {
|
||||
msg := fmt.Sprintf("warning: undecoded keys in %q: %+v", dropInFpath, md.Undecoded())
|
||||
kataUtilsLogger.Warn(msg)
|
||||
}
|
||||
|
||||
for _, key := range md.Keys() {
|
||||
err = applyKey(*tomlConf, key, &tomlConfOrig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying key '%+v' from drop-in file %q: %s", key, dropInFpath, err)
|
||||
}
|
||||
}
|
||||
|
||||
tomlConf.Hypervisor = tomlConfOrig.Hypervisor
|
||||
tomlConf.Agent = tomlConfOrig.Agent
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyKey(sourceConf tomlConfig, key []string, targetConf *tomlConfig) error {
|
||||
// Any key that might need treatment provided by this function has to have
|
||||
// (at least) three components: [ map_name map_key_name field_toml_tag ],
|
||||
// e.g. [agent kata enable_tracing] or [hypervisor qemu confidential_guest].
|
||||
if len(key) < 3 {
|
||||
return nil
|
||||
}
|
||||
switch key[0] {
|
||||
case "agent":
|
||||
return applyAgentKey(sourceConf, key[1:], targetConf)
|
||||
case "hypervisor":
|
||||
return applyHypervisorKey(sourceConf, key[1:], targetConf)
|
||||
// The table the 'key' is in is not stored in a map so no special handling
|
||||
// is needed.
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Both of the following functions copy the value of a 'sourceConf' field
|
||||
// identified by the TOML tag in 'key' into the corresponding field in
|
||||
// 'targetConf'.
|
||||
func applyAgentKey(sourceConf tomlConfig, key []string, targetConf *tomlConfig) error {
|
||||
agentName := key[0]
|
||||
tomlKeyName := key[1]
|
||||
|
||||
sourceAgentConf := sourceConf.Agent[agentName]
|
||||
targetAgentConf := targetConf.Agent[agentName]
|
||||
|
||||
err := copyFieldValue(reflect.ValueOf(&sourceAgentConf).Elem(), tomlKeyName, reflect.ValueOf(&targetAgentConf).Elem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetConf.Agent[agentName] = targetAgentConf
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyHypervisorKey(sourceConf tomlConfig, key []string, targetConf *tomlConfig) error {
|
||||
hypervisorName := key[0]
|
||||
tomlKeyName := key[1]
|
||||
|
||||
sourceHypervisorConf := sourceConf.Hypervisor[hypervisorName]
|
||||
targetHypervisorConf := targetConf.Hypervisor[hypervisorName]
|
||||
|
||||
err := copyFieldValue(reflect.ValueOf(&sourceHypervisorConf).Elem(), tomlKeyName, reflect.ValueOf(&targetHypervisorConf).Elem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetConf.Hypervisor[hypervisorName] = targetHypervisorConf
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copies a TOML value of the source field identified by its TOML key to the
|
||||
// corresponding field of the target. Basically
|
||||
// 'target[tomlKeyName] = source[tomlKeyNmae]'.
|
||||
func copyFieldValue(source reflect.Value, tomlKeyName string, target reflect.Value) error {
|
||||
val, err := getValue(source, tomlKeyName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting key %q from a decoded drop-in conf file: %s", tomlKeyName, err)
|
||||
}
|
||||
err = setValue(target, tomlKeyName, val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting key %q to a new value '%v': %s", tomlKeyName, val.Interface(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The first argument is expected to be a reflect.Value of a tomlConfig
|
||||
// substructure (hypervisor, agent), the second argument is a TOML key
|
||||
// corresponding to the substructure field whose TOML value is queried.
|
||||
// Return value corresponds to 'tomlConfStruct[tomlKey]'.
|
||||
func getValue(tomlConfStruct reflect.Value, tomlKey string) (reflect.Value, error) {
|
||||
tomlConfStructType := tomlConfStruct.Type()
|
||||
for j := 0; j < tomlConfStruct.NumField(); j++ {
|
||||
fieldTomlTag := tomlConfStructType.Field(j).Tag.Get("toml")
|
||||
if fieldTomlTag == tomlKey {
|
||||
return tomlConfStruct.Field(j), nil
|
||||
}
|
||||
}
|
||||
return reflect.Value{}, fmt.Errorf("key %q not found", tomlKey)
|
||||
}
|
||||
|
||||
// The first argument is expected to be a reflect.Value of a tomlConfig
|
||||
// substructure (hypervisor, agent), the second argument is a TOML key
|
||||
// corresponding to the substructure field whose TOML value is to be changed,
|
||||
// the third argument is a reflect.Value representing the new TOML value.
|
||||
// An equivalent of 'tomlConfStruct[tomlKey] = newVal'.
|
||||
func setValue(tomlConfStruct reflect.Value, tomlKey string, newVal reflect.Value) error {
|
||||
tomlConfStructType := tomlConfStruct.Type()
|
||||
for j := 0; j < tomlConfStruct.NumField(); j++ {
|
||||
fieldTomlTag := tomlConfStructType.Field(j).Tag.Get("toml")
|
||||
if fieldTomlTag == tomlKey {
|
||||
tomlConfStruct.Field(j).Set(newVal)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("key %q not found", tomlKey)
|
||||
}
|
||||
|
||||
// checkConfig checks the validity of the specified config.
|
||||
func checkConfig(config oci.RuntimeConfig) error {
|
||||
if err := checkNetNsConfig(config); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user