<commit_before>package config

import (
	"io/ioutil"
	"os"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	yaml "github.com/cloudfoundry-incubator/candiedyaml"
	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/docker/libcompose/project"
	"github.com/rancher/os/util"
)

var osConfig *CloudConfig

func NewConfig() *CloudConfig {
	if osConfig == nil {
		osConfig, _ = ReadConfig(nil, true, OsConfigFile, OemConfigFile)
	}
	newCfg := *osConfig
	return &newCfg
}

func ReadConfig(bytes []byte, substituteMetadataVars bool, files ...string) (*CloudConfig, error) {
	if data, err := readConfig(bytes, substituteMetadataVars, files...); err == nil {
		c := &CloudConfig{}
		if err := util.Convert(data, c); err != nil {
			return nil, err
		}
		c, _ = amendNils(c)
		c, _ = amendContainerNames(c)
		return c, nil
	} else {
		return nil, err
	}
}

func LoadConfig() (*CloudConfig, error) {
	cfg, err := ChainCfgFuncs(NewConfig(),
		readFilesAndMetadata,
		readCmdline,
		amendNils,
		amendContainerNames)
	if err != nil {
		log.WithFields(log.Fields{"cfg": cfg, "err": err}).Error("Failed to load config")
		return nil, err
	}

	log.Debug("Merging cloud-config from meta-data and user-data")
	cfg = mergeMetadata(cfg, readMetadata())

	if cfg.Rancher.Debug {
		log.SetLevel(log.DebugLevel)
		if !util.Contains(cfg.Rancher.Docker.Args, "-D") {
			cfg.Rancher.Docker.Args = append(cfg.Rancher.Docker.Args, "-D")
		}
		if !util.Contains(cfg.Rancher.SystemDocker.Args, "-D") {
			cfg.Rancher.SystemDocker.Args = append(cfg.Rancher.SystemDocker.Args, "-D")
		}
	}

	return cfg, nil
}

func CloudConfigDirFiles() []string {
	files, err := util.DirLs(CloudConfigDir)
	if err != nil {
		if os.IsNotExist(err) {
			// do nothing
			log.Debugf("%s does not exist", CloudConfigDir)
		} else {
			log.Errorf("Failed to read %s: %v", CloudConfigDir, err)
		}
		return []string{}
	}

	files = util.Filter(files, func(x interface{}) bool {
		f := x.(os.FileInfo)
		if f.IsDir() || strings.HasPrefix(f.Name(), ".") {
			return false
		}
		return true
	})

	return util.ToStrings(util.Map(files, func(x interface{}) interface{} {
		return path.Join(CloudConfigDir, x.(os.FileInfo).Name())
	}))
}

// mergeMetadata merges certain options from md (meta-data from the datasource)
// onto cc (a CloudConfig derived from user-data), if they are not already set
// on cc (i.e. user-data always takes precedence)
func mergeMetadata(cc *CloudConfig, md datasource.Metadata) *CloudConfig {
	if cc == nil {
		return cc
	}
	out := cc
	dirty := false

	if md.Hostname != "" {
		if out.Hostname != "" {
			log.Debugf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname)
		} else {
			out = &(*cc)
			dirty = true
			out.Hostname = md.Hostname
		}
	}
	for _, key := range md.SSHPublicKeys {
		if !dirty {
			out = &(*cc)
			dirty = true
		}
		out.SSHAuthorizedKeys = append(out.SSHAuthorizedKeys, key)
	}
	return out
}

func readMetadata() datasource.Metadata {
	metadata := datasource.Metadata{}
	if metaDataBytes, err := ioutil.ReadFile(MetaDataFile); err == nil {
		yaml.Unmarshal(metaDataBytes, &metadata)
	}<commit_msg>Sort SSH keys from metadata so that diff functions will be consistent<commit_after>package config

import (
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	yaml "github.com/cloudfoundry-incubator/candiedyaml"
	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/docker/libcompose/project"
	"github.com/rancher/os/util"
)

var osConfig *CloudConfig

func NewConfig() *CloudConfig {
	if osConfig == nil {
		osConfig, _ = ReadConfig(nil, true, OsConfigFile, OemConfigFile)
	}
	newCfg := *osConfig
	return &newCfg
}

func ReadConfig(bytes []byte, substituteMetadataVars bool, files ...string) (*CloudConfig, error) {
	if data, err := readConfig(bytes, substituteMetadataVars, files...); err == nil {
		c := &CloudConfig{}
		if err := util.Convert(data, c); err != nil {
			return nil, err
		}
		c, _ = amendNils(c)
		c, _ = amendContainerNames(c)
		return c, nil
	} else {
		return nil, err
	}
}

func LoadConfig() (*CloudConfig, error) {
	cfg, err := ChainCfgFuncs(NewConfig(),
		readFilesAndMetadata,
		readCmdline,
		amendNils,
		amendContainerNames)
	if err != nil {
		log.WithFields(log.Fields{"cfg": cfg, "err": err}).Error("Failed to load config")
		return nil, err
	}

	log.Debug("Merging cloud-config from meta-data and user-data")
	cfg = mergeMetadata(cfg, readMetadata())

	if cfg.Rancher.Debug {
		log.SetLevel(log.DebugLevel)
		if !util.Contains(cfg.Rancher.Docker.Args, "-D") {
			cfg.Rancher.Docker.Args = append(cfg.Rancher.Docker.Args, "-D")
		}
		if !util.Contains(cfg.Rancher.SystemDocker.Args, "-D") {
			cfg.Rancher.SystemDocker.Args = append(cfg.Rancher.SystemDocker.Args, "-D")
		}
	}

	return cfg, nil
}

func CloudConfigDirFiles() []string {
	files, err := util.DirLs(CloudConfigDir)
	if err != nil {
		if os.IsNotExist(err) {
			// do nothing
			log.Debugf("%s does not exist", CloudConfigDir)
		} else {
			log.Errorf("Failed to read %s: %v", CloudConfigDir, err)
		}
		return []string{}
	}

	files = util.Filter(files, func(x interface{}) bool {
		f := x.(os.FileInfo)
		if f.IsDir() || strings.HasPrefix(f.Name(), ".") {
			return false
		}
		return true
	})

	return util.ToStrings(util.Map(files, func(x interface{}) interface{} {
		return path.Join(CloudConfigDir, x.(os.FileInfo).Name())
	}))
}

// mergeMetadata merges certain options from md (meta-data from the datasource)
// onto cc (a CloudConfig derived from user-data), if they are not already set
// on cc (i.e. user-data always takes precedence)
func mergeMetadata(cc *CloudConfig, md datasource.Metadata) *CloudConfig {
	if cc == nil {
		return cc
	}
	out := cc
	dirty := false

	if md.Hostname != "" {
		if out.Hostname != "" {
			log.Debugf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname)
		} else {
			out = &(*cc)
			dirty = true
			out.Hostname = md.Hostname
		}
	}

	// Sort SSH keys by key name
	keys := []string{}
	for k := range md.SSHPublicKeys {
		keys = append(keys, k)
	}

	sort.Sort(sort.StringSlice(keys))

	for _, k := range keys {
		if !dirty {
			out = &(*cc)
			dirty = true
		}
		out.SSHAuthorizedKeys = append(out.SSHAuthorizedKeys, md.SSHPublicKeys[k])
	}

	return out
}

func readMetadata() datasource.Metadata {
	metadata := datasource.Metadata{}
	if metaDataBytes, err := ioutil.ReadFile(MetaDataFile); err == nil {
		yaml.Unmarshal(metaDataBytes, &metadata)
	}
{'fraction_non_alphanumeric': 0.1482769044740024, 'fraction_numerical': 0.0, 'mean_word_length': 3.879793510324484, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2303219', 'n_tokens_mistral': 2539, 'n_tokens_neox': 2380, 'n_words': 562}