1
0
mirror of https://github.com/rancher/os.git synced 2025-09-25 12:47:20 +00:00

Get RancherOS logging to throw debug logs to a remote syslog server when the kernel netconsole is configured

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
This commit is contained in:
Sven Dowideit
2017-08-12 03:09:47 +10:00
parent 69b54017a9
commit f793518aa6
18 changed files with 401 additions and 217 deletions

171
config/cmdline/cmdline.go Executable file
View File

@@ -0,0 +1,171 @@
package cmdline
import (
"io/ioutil"
"strings"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/rancher/os/util"
)
func Read(parseAll bool) (m map[interface{}]interface{}, err error) {
cmdLine, err := ioutil.ReadFile("/proc/cmdline")
if err != nil {
return nil, err
}
if len(cmdLine) == 0 {
return nil, nil
}
cmdLineObj := Parse(strings.TrimSpace(util.UnescapeKernelParams(string(cmdLine))), parseAll)
return cmdLineObj, nil
}
func GetCmdline(key string) interface{} {
parseAll := true
if strings.HasPrefix(key, "cc.") || strings.HasPrefix(key, "rancher.") {
// the normal case
parseAll = false
}
cmdline, _ := Read(parseAll)
v, _ := GetOrSetVal(key, cmdline, nil)
return v
}
func GetOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
parts := strings.Split(args, ".")
tData := data
if value != nil {
tData = util.MapCopy(data)
}
t := tData
for i, part := range parts {
val, ok := t[part]
last := i+1 == len(parts)
// Reached end, set the value
if last && value != nil {
if s, ok := value.(string); ok {
value = unmarshalOrReturnString(s)
}
t[part] = value
return value, tData
}
// Missing intermediate key, create key
if !last && value != nil && !ok {
newData := map[interface{}]interface{}{}
t[part] = newData
t = newData
continue
}
if !ok {
break
}
if last {
return val, tData
}
newData, ok := val.(map[interface{}]interface{})
if !ok {
break
}
t = newData
}
return "", tData
}
// Replace newlines, colons, and question marks with random strings
// This is done to avoid YAML treating these as special characters
var (
newlineMagicString = "9XsJcx6dR5EERYCC"
colonMagicString = "V0Rc21pIVknMm2rr"
questionMarkMagicString = "FoPL6JLMAaJqKMJT"
)
func reverseReplacement(result interface{}) interface{} {
switch val := result.(type) {
case map[interface{}]interface{}:
for k, v := range val {
val[k] = reverseReplacement(v)
}
return val
case []interface{}:
for i, item := range val {
val[i] = reverseReplacement(item)
}
return val
case string:
val = strings.Replace(val, newlineMagicString, "\n", -1)
val = strings.Replace(val, colonMagicString, ":", -1)
val = strings.Replace(val, questionMarkMagicString, "?", -1)
return val
}
return result
}
func unmarshalOrReturnString(value string) (result interface{}) {
value = strings.Replace(value, "\n", newlineMagicString, -1)
value = strings.Replace(value, ":", colonMagicString, -1)
value = strings.Replace(value, "?", questionMarkMagicString, -1)
if err := yaml.Unmarshal([]byte(value), &result); err != nil {
result = value
}
result = reverseReplacement(result)
return
}
func Parse(cmdLine string, parseAll bool) map[interface{}]interface{} {
result := make(map[interface{}]interface{})
outer:
for _, part := range strings.Split(cmdLine, " ") {
if strings.HasPrefix(part, "cc.") {
part = part[3:]
} else if !strings.HasPrefix(part, "rancher.") {
if !parseAll {
continue
}
}
var value string
kv := strings.SplitN(part, "=", 2)
if len(kv) == 1 {
value = "true"
} else {
value = kv[1]
}
current := result
keys := strings.Split(kv[0], ".")
for i, key := range keys {
if i == len(keys)-1 {
current[key] = unmarshalOrReturnString(value)
} else {
if obj, ok := current[key]; ok {
if newCurrent, ok := obj.(map[interface{}]interface{}); ok {
current = newCurrent
} else {
continue outer
}
} else {
newCurrent := make(map[interface{}]interface{})
current[key] = newCurrent
current = newCurrent
}
}
}
}
return result
}

View File

@@ -5,6 +5,7 @@ import (
"strings"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/rancher/os/config/cmdline"
"github.com/rancher/os/util"
)
@@ -41,6 +42,13 @@ func Export(private, full bool) (string, error) {
bytes, err := yaml.Marshal(rawCfg)
return string(bytes), err
}
func filterPrivateKeys(data map[interface{}]interface{}) map[interface{}]interface{} {
for _, privateKey := range PrivateKeys {
_, data = filterKey(data, strings.Split(privateKey, "."))
}
return data
}
func Get(key string) (interface{}, error) {
cfg := LoadConfig()
@@ -50,23 +58,17 @@ func Get(key string) (interface{}, error) {
return nil, err
}
v, _ := getOrSetVal(key, data, nil)
v, _ := cmdline.GetOrSetVal(key, data, nil)
return v, nil
}
func GetCmdline(key string) interface{} {
cmdline := readCmdline()
v, _ := getOrSetVal(key, cmdline, nil)
return v
}
func Set(key string, value interface{}) error {
existing, err := readConfigs(nil, false, true, CloudConfigFile)
if err != nil {
return err
}
_, modified := getOrSetVal(key, existing, value)
_, modified := cmdline.GetOrSetVal(key, existing, value)
c := &CloudConfig{}
if err = util.Convert(modified, c); err != nil {

View File

@@ -5,6 +5,7 @@ import (
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/rancher/os/config/cmdline"
"github.com/rancher/os/util"
"github.com/stretchr/testify/require"
)
@@ -100,7 +101,7 @@ func TestUnmarshalOrReturnString(t *testing.T) {
assert.Equal([]interface{}{false, "a"}, unmarshalOrReturnString("[false,a]"))
}
func TestParseCmdline(t *testing.T) {
func TestCmdlineParse(t *testing.T) {
assert := require.New(t)
assert.Equal(map[interface{}]interface{}{
@@ -108,55 +109,55 @@ func TestParseCmdline(t *testing.T) {
"key1": "value1",
"key2": "value2",
},
}, parseCmdline("a b rancher.key1=value1 c rancher.key2=value2"))
}, cmdline.Parse("a b rancher.key1=value1 c rancher.key2=value2"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"key": "a,b",
},
}, parseCmdline("rancher.key=a,b"))
}, cmdline.Parse("rancher.key=a,b"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"key": "a\nb",
},
}, parseCmdline("rancher.key=a\nb"))
}, cmdline.Parse("rancher.key=a\nb"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"key": "a:b",
},
}, parseCmdline("rancher.key=a:b"))
}, cmdline.Parse("rancher.key=a:b"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"key": int64(5),
},
}, parseCmdline("rancher.key=5"))
}, cmdline.Parse("rancher.key=5"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"rescue": true,
},
}, parseCmdline("rancher.rescue"))
}, cmdline.Parse("rancher.rescue"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"keyArray": []interface{}{int64(1), int64(2)},
},
}, parseCmdline("rancher.keyArray=[1,2]"))
}, cmdline.Parse("rancher.keyArray=[1,2]"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"strArray": []interface{}{"url:http://192.168.1.100/cloud-config?a=b"},
},
}, parseCmdline("rancher.strArray=[\"url:http://192.168.1.100/cloud-config?a=b\"]"))
}, cmdline.Parse("rancher.strArray=[\"url:http://192.168.1.100/cloud-config?a=b\"]"), false)
assert.Equal(map[interface{}]interface{}{
"rancher": map[interface{}]interface{}{
"strArray": []interface{}{"url:http://192.168.1.100/cloud-config?a=b"},
},
}, parseCmdline("rancher.strArray=[url:http://192.168.1.100/cloud-config?a=b]"))
}, cmdline.Parse("rancher.strArray=[url:http://192.168.1.100/cloud-config?a=b]"), false)
}
func TestGet(t *testing.T) {
@@ -181,7 +182,7 @@ func TestGet(t *testing.T) {
}
for k, v := range tests {
val, _ := getOrSetVal(k, data, nil)
val, _ := cmdline.GeOrSetVal(k, data, nil)
assert.Equal(v, val)
}
}
@@ -225,8 +226,8 @@ func TestSet(t *testing.T) {
}
for k, v := range tests {
_, tData := getOrSetVal(k, data, v)
val, _ := getOrSetVal(k, tData, nil)
_, tData := cmdline.GetOrSetVal(k, data, v)
val, _ := cmdline.GetOrSetVal(k, tData, nil)
data = tData
assert.Equal(v, val)
}

View File

@@ -1,11 +1,7 @@
package config
import (
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/rancher/os/log"
"strings"
"github.com/rancher/os/util"
)
@@ -14,6 +10,7 @@ type CfgFuncData struct {
Name string
Func CfgFunc
}
type CfgFuncs []CfgFuncData
func ChainCfgFuncs(cfg *CloudConfig, cfgFuncs CfgFuncs) (*CloudConfig, error) {
@@ -71,145 +68,3 @@ func filterKey(data map[interface{}]interface{}, key []string) (filtered, rest m
return
}
func filterPrivateKeys(data map[interface{}]interface{}) map[interface{}]interface{} {
for _, privateKey := range PrivateKeys {
_, data = filterKey(data, strings.Split(privateKey, "."))
}
return data
}
func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
parts := strings.Split(args, ".")
tData := data
if value != nil {
tData = util.MapCopy(data)
}
t := tData
for i, part := range parts {
val, ok := t[part]
last := i+1 == len(parts)
// Reached end, set the value
if last && value != nil {
if s, ok := value.(string); ok {
value = unmarshalOrReturnString(s)
}
t[part] = value
return value, tData
}
// Missing intermediate key, create key
if !last && value != nil && !ok {
newData := map[interface{}]interface{}{}
t[part] = newData
t = newData
continue
}
if !ok {
break
}
if last {
return val, tData
}
newData, ok := val.(map[interface{}]interface{})
if !ok {
break
}
t = newData
}
return "", tData
}
// Replace newlines, colons, and question marks with random strings
// This is done to avoid YAML treating these as special characters
var (
newlineMagicString = "9XsJcx6dR5EERYCC"
colonMagicString = "V0Rc21pIVknMm2rr"
questionMarkMagicString = "FoPL6JLMAaJqKMJT"
)
func reverseReplacement(result interface{}) interface{} {
switch val := result.(type) {
case map[interface{}]interface{}:
for k, v := range val {
val[k] = reverseReplacement(v)
}
return val
case []interface{}:
for i, item := range val {
val[i] = reverseReplacement(item)
}
return val
case string:
val = strings.Replace(val, newlineMagicString, "\n", -1)
val = strings.Replace(val, colonMagicString, ":", -1)
val = strings.Replace(val, questionMarkMagicString, "?", -1)
return val
}
return result
}
func unmarshalOrReturnString(value string) (result interface{}) {
value = strings.Replace(value, "\n", newlineMagicString, -1)
value = strings.Replace(value, ":", colonMagicString, -1)
value = strings.Replace(value, "?", questionMarkMagicString, -1)
if err := yaml.Unmarshal([]byte(value), &result); err != nil {
result = value
}
result = reverseReplacement(result)
return
}
func parseCmdline(cmdLine string) map[interface{}]interface{} {
result := make(map[interface{}]interface{})
outer:
for _, part := range strings.Split(cmdLine, " ") {
if strings.HasPrefix(part, "cc.") {
part = part[3:]
} else if !strings.HasPrefix(part, "rancher.") {
continue
}
var value string
kv := strings.SplitN(part, "=", 2)
if len(kv) == 1 {
value = "true"
} else {
value = kv[1]
}
current := result
keys := strings.Split(kv[0], ".")
for i, key := range keys {
if i == len(keys)-1 {
current[key] = unmarshalOrReturnString(value)
} else {
if obj, ok := current[key]; ok {
if newCurrent, ok := obj.(map[interface{}]interface{}); ok {
current = newCurrent
} else {
continue outer
}
} else {
newCurrent := make(map[interface{}]interface{})
current[key] = newCurrent
current = newCurrent
}
}
}
}
return result
}

View File

@@ -14,6 +14,7 @@ import (
composeConfig "github.com/docker/libcompose/config"
"github.com/rancher/os/config/cloudinit/datasource"
"github.com/rancher/os/config/cloudinit/initialize"
"github.com/rancher/os/config/cmdline"
"github.com/rancher/os/log"
"github.com/rancher/os/util"
)
@@ -48,7 +49,11 @@ func loadRawDiskConfig(dirPrefix string, full bool) map[interface{}]interface{}
func loadRawConfig(dirPrefix string, full bool) map[interface{}]interface{} {
rawCfg := loadRawDiskConfig(dirPrefix, full)
rawCfg = util.Merge(rawCfg, readCmdline())
procCmdline, err := cmdline.Read(false)
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Failed to read kernel params")
}
rawCfg = util.Merge(rawCfg, procCmdline)
rawCfg = util.Merge(rawCfg, readElidedCmdline(rawCfg))
rawCfg = applyDebugFlags(rawCfg)
return mergeMetadata(rawCfg, readMetadata())
@@ -107,7 +112,7 @@ func Insert(m interface{}, args ...interface{}) interface{} {
}
func SaveInitCmdline(cmdLineArgs string) {
elidedCfg := parseCmdline(cmdLineArgs)
elidedCfg := cmdline.Parse(cmdLineArgs, false)
env := Insert(make(map[interface{}]interface{}), interface{}("EXTRA_CMDLINE"), interface{}(cmdLineArgs))
rancher := Insert(make(map[interface{}]interface{}), interface{}("environment"), env)
@@ -155,10 +160,10 @@ func applyDebugFlags(rawCfg map[interface{}]interface{}) map[interface{}]interfa
}
log.SetLevel(log.DebugLevel)
_, rawCfg = getOrSetVal("rancher.docker.debug", rawCfg, true)
_, rawCfg = getOrSetVal("rancher.system_docker.debug", rawCfg, true)
_, rawCfg = getOrSetVal("rancher.bootstrap_docker.debug", rawCfg, true)
_, rawCfg = getOrSetVal("rancher.log", rawCfg, true)
_, rawCfg = cmdline.GetOrSetVal("rancher.docker.debug", rawCfg, true)
_, rawCfg = cmdline.GetOrSetVal("rancher.system_docker.debug", rawCfg, true)
_, rawCfg = cmdline.GetOrSetVal("rancher.bootstrap_docker.debug", rawCfg, true)
_, rawCfg = cmdline.GetOrSetVal("rancher.log", rawCfg, true)
return rawCfg
}
@@ -216,7 +221,7 @@ func readElidedCmdline(rawCfg map[interface{}]interface{}) map[interface{}]inter
for k, v := range rawCfg {
if key, _ := k.(string); key == "EXTRA_CMDLINE" {
if val, ok := v.(string); ok {
cmdLineObj := parseCmdline(strings.TrimSpace(util.UnescapeKernelParams(string(val))))
cmdLineObj := cmdline.Parse(strings.TrimSpace(util.UnescapeKernelParams(string(val))), false)
return cmdLineObj
}
@@ -225,22 +230,6 @@ func readElidedCmdline(rawCfg map[interface{}]interface{}) map[interface{}]inter
return nil
}
func readCmdline() map[interface{}]interface{} {
cmdLine, err := ioutil.ReadFile("/proc/cmdline")
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Failed to read kernel params")
return nil
}
if len(cmdLine) == 0 {
return nil
}
cmdLineObj := parseCmdline(strings.TrimSpace(util.UnescapeKernelParams(string(cmdLine))))
return cmdLineObj
}
func amendNils(c *CloudConfig) *CloudConfig {
t := *c
if t.Rancher.Environment == nil {