1
0
mirror of https://github.com/rancher/os.git synced 2025-07-16 16:11:03 +00:00

Move in code from netconf and docker-from-scratch

This commit is contained in:
Josh Curl 2016-10-19 16:21:35 -07:00
parent 1f4d23bf50
commit 691f7cb42c
No known key found for this signature in database
GPG Key ID: 82B504B9BCCFA677
16 changed files with 1567 additions and 38 deletions

View File

@ -36,9 +36,9 @@ import (
"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
"github.com/coreos/coreos-cloudinit/datasource/url"
"github.com/coreos/coreos-cloudinit/pkg"
"github.com/rancher/netconf"
"github.com/rancher/os/cmd/cloudinitsave/gce"
rancherConfig "github.com/rancher/os/config"
"github.com/rancher/os/netconf"
"github.com/rancher/os/util"
)
@ -231,8 +231,8 @@ func getDatasources(cfg *rancherConfig.CloudConfig) []datasource.Datasource {
}
func enableDoLinkLocal() {
err := netconf.ApplyNetworkConfigs(&netconf.NetworkConfig{
Interfaces: map[string]netconf.InterfaceConfig{
err := netconf.ApplyNetworkConfigs(&rancherConfig.NetworkConfig{
Interfaces: map[string]rancherConfig.InterfaceConfig{
"eth0": {
IPV4LL: true,
},

View File

@ -12,11 +12,11 @@ import (
"github.com/Sirupsen/logrus"
"github.com/packethost/packngo/metadata"
"github.com/rancher/netconf"
rancherConfig "github.com/rancher/os/config"
"github.com/rancher/os/config"
"github.com/rancher/os/netconf"
)
func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
func enablePacketNetwork(cfg *config.RancherConfig) {
bootStrapped := false
for _, v := range cfg.Network.Interfaces {
if v.Address != "" {
@ -40,7 +40,7 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
return
}
bondCfg := netconf.InterfaceConfig{
bondCfg := config.InterfaceConfig{
Addresses: []string{},
BondOpts: map[string]string{
"lacp_rate": "1",
@ -51,11 +51,11 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
"mode": "4",
},
}
netCfg := netconf.NetworkConfig{
Interfaces: map[string]netconf.InterfaceConfig{},
netCfg := config.NetworkConfig{
Interfaces: map[string]config.InterfaceConfig{},
}
for _, iface := range m.Network.Interfaces {
netCfg.Interfaces["mac="+iface.Mac] = netconf.InterfaceConfig{
netCfg.Interfaces["mac="+iface.Mac] = config.InterfaceConfig{
Bond: "bond0",
}
}
@ -80,24 +80,24 @@ func enablePacketNetwork(cfg *rancherConfig.RancherConfig) {
b, _ := yaml.Marshal(netCfg)
logrus.Debugf("Generated network config: %s", string(b))
cc := rancherConfig.CloudConfig{
Rancher: rancherConfig.RancherConfig{
cc := config.CloudConfig{
Rancher: config.RancherConfig{
Network: netCfg,
},
}
// Post to phone home URL on first boot
if _, err = os.Stat(rancherConfig.CloudConfigNetworkFile); err != nil {
if _, err = os.Stat(config.CloudConfigNetworkFile); err != nil {
if _, err = http.Post(m.PhoneHomeURL, "application/json", bytes.NewReader([]byte{})); err != nil {
logrus.Errorf("Failed to post to Packet phone home URL: %v", err)
}
}
if err := os.MkdirAll(path.Dir(rancherConfig.CloudConfigNetworkFile), 0700); err != nil {
logrus.Errorf("Failed to create directory for file %s: %v", rancherConfig.CloudConfigNetworkFile, err)
if err := os.MkdirAll(path.Dir(config.CloudConfigNetworkFile), 0700); err != nil {
logrus.Errorf("Failed to create directory for file %s: %v", config.CloudConfigNetworkFile, err)
}
if err := rancherConfig.WriteToFile(cc, rancherConfig.CloudConfigNetworkFile); err != nil {
logrus.Errorf("Failed to save config file %s: %v", rancherConfig.CloudConfigNetworkFile, err)
if err := config.WriteToFile(cc, config.CloudConfigNetworkFile); err != nil {
logrus.Errorf("Failed to save config file %s: %v", config.CloudConfigNetworkFile, err)
}
}

View File

@ -9,10 +9,10 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/resolvconf"
"github.com/rancher/netconf"
"github.com/rancher/os/config"
"github.com/rancher/os/docker"
"github.com/rancher/os/hostname"
"github.com/rancher/os/netconf"
)
var (

View File

@ -7,7 +7,6 @@ import (
"github.com/coreos/coreos-cloudinit/config"
"github.com/docker/engine-api/types"
composeConfig "github.com/docker/libcompose/config"
"github.com/rancher/netconf"
)
const (
@ -113,8 +112,8 @@ type RancherConfig struct {
Disable []string `yaml:"disable,omitempty"`
ServicesInclude map[string]bool `yaml:"services_include,omitempty"`
Modules []string `yaml:"modules,omitempty"`
Network netconf.NetworkConfig `yaml:"network,omitempty"`
DefaultNetwork netconf.NetworkConfig `yaml:"default_network,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"`
DefaultNetwork NetworkConfig `yaml:"default_network,omitempty"`
Repositories Repositories `yaml:"repositories,omitempty"`
Ssh SshConfig `yaml:"ssh,omitempty"`
State StateConfig `yaml:"state,omitempty"`
@ -169,6 +168,39 @@ type DockerConfig struct {
Exec bool `yaml:"exec,omitempty"`
}
type NetworkConfig struct {
PreCmds []string `yaml:"pre_cmds,omitempty"`
Dns DnsConfig `yaml:"dns,omitempty"`
Interfaces map[string]InterfaceConfig `yaml:"interfaces,omitempty"`
PostCmds []string `yaml:"post_cmds,omitempty"`
HttpProxy string `yaml:"http_proxy,omitempty"`
HttpsProxy string `yaml:"https_proxy,omitempty"`
NoProxy string `yaml:"no_proxy,omitempty"`
}
type InterfaceConfig struct {
Match string `yaml:"match,omitempty"`
DHCP bool `yaml:"dhcp,omitempty"`
DHCPArgs string `yaml:"dhcp_args,omitempty"`
Address string `yaml:"address,omitempty"`
Addresses []string `yaml:"addresses,omitempty"`
IPV4LL bool `yaml:"ipv4ll,omitempty"`
Gateway string `yaml:"gateway,omitempty"`
GatewayIpv6 string `yaml:"gateway_ipv6,omitempty"`
MTU int `yaml:"mtu,omitempty"`
Bridge string `yaml:"bridge,omitempty"`
Bond string `yaml:"bond,omitempty"`
BondOpts map[string]string `yaml:"bond_opts,omitempty"`
PostUp []string `yaml:"post_up,omitempty"`
PreUp []string `yaml:"pre_up,omitempty"`
Vlans string `yaml:"vlans,omitempty"`
}
type DnsConfig struct {
Nameservers []string `yaml:"nameservers,flow,omitempty"`
Search []string `yaml:"search,flow,omitempty"`
}
type SshConfig struct {
Keys map[string]string `yaml:"keys,omitempty"`
}
@ -191,9 +223,9 @@ type CloudInit struct {
}
type Defaults struct {
Hostname string `yaml:"hostname,omitempty"`
Docker DockerConfig `yaml:"docker,omitempty"`
Network netconf.NetworkConfig `yaml:"network,omitempty"`
Hostname string `yaml:"hostname,omitempty"`
Docker DockerConfig `yaml:"docker,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"`
}
func (r Repositories) ToArray() []string {

24
dfs/one.go Normal file
View File

@ -0,0 +1,24 @@
// +build linux
package dfs
import (
"os"
"os/signal"
"syscall"
)
func PidOne() error {
c := make(chan os.Signal, 2048)
signal.Notify(c, syscall.SIGCHLD)
for range c {
for {
if pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil); err != nil || pid <= 0 {
break
}
}
}
return nil
}

727
dfs/scratch.go Normal file
View File

@ -0,0 +1,727 @@
package dfs
import (
"bufio"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"strconv"
"strings"
"syscall"
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/resolvconf"
"github.com/rancher/os/config"
"github.com/rancher/os/netconf"
"github.com/rancher/os/selinux"
"github.com/rancher/os/util"
)
const (
defaultPrefix = "/usr"
iptables = "/sbin/iptables"
modprobe = "/sbin/modprobe"
distSuffix = ".dist"
)
var (
mounts = [][]string{
{"devtmpfs", "/dev", "devtmpfs", ""},
{"none", "/dev/pts", "devpts", ""},
{"shm", "/dev/shm", "tmpfs", "rw,nosuid,nodev,noexec,relatime,size=65536k"},
{"mqueue", "/dev/mqueue", "mqueue", "rw,nosuid,nodev,noexec,relatime"},
{"none", "/proc", "proc", ""},
{"none", "/run", "tmpfs", ""},
{"none", "/sys", "sysfs", ""},
{"none", "/sys/fs/cgroup", "tmpfs", ""},
}
optionalMounts = [][]string{
{"none", "/sys/fs/selinux", "selinuxfs", ""},
}
)
type Config struct {
Fork bool
PidOne bool
CommandName string
DnsConfig config.DnsConfig
BridgeName string
BridgeAddress string
BridgeMtu int
CgroupHierarchy map[string]string
LogFile string
NoLog bool
NoFiles uint64
Environment []string
GraphDirectory string
DaemonConfig string
}
func createMounts(mounts ...[]string) error {
for _, mount := range mounts {
log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3])
err := util.Mount(mount[0], mount[1], mount[2], mount[3])
if err != nil {
return err
}
}
return nil
}
func createOptionalMounts(mounts ...[]string) {
for _, mount := range mounts {
log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3])
err := util.Mount(mount[0], mount[1], mount[2], mount[3])
if err != nil {
log.Debugf("Unable to mount %s %s %s %s: %s", mount[0], mount[1], mount[2], mount[3], err)
}
}
}
func createDirs(dirs ...string) error {
for _, dir := range dirs {
if _, err := os.Stat(dir); os.IsNotExist(err) {
log.Debugf("Creating %s", dir)
err = os.MkdirAll(dir, 0755)
if err != nil {
return err
}
}
}
return nil
}
func mountCgroups(hierarchyConfig map[string]string) error {
f, err := os.Open("/proc/cgroups")
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
hierarchies := make(map[string][]string)
for scanner.Scan() {
text := scanner.Text()
log.Debugf("/proc/cgroups: %s", text)
fields := strings.Split(text, "\t")
cgroup := fields[0]
if cgroup == "" || cgroup[0] == '#' || (len(fields) > 3 && fields[3] == "0") {
continue
}
hierarchy := hierarchyConfig[cgroup]
if hierarchy == "" {
hierarchy = fields[1]
}
if hierarchy == "0" {
hierarchy = cgroup
}
hierarchies[hierarchy] = append(hierarchies[hierarchy], cgroup)
}
for _, hierarchy := range hierarchies {
if err := mountCgroup(strings.Join(hierarchy, ",")); err != nil {
return err
}
}
if err = scanner.Err(); err != nil {
return err
}
log.Debug("Done mouting cgroupfs")
return nil
}
func CreateSymlinks(pathSets [][]string) error {
for _, paths := range pathSets {
if err := CreateSymlink(paths[0], paths[1]); err != nil {
return err
}
}
return nil
}
func CreateSymlink(src, dest string) error {
if _, err := os.Lstat(dest); os.IsNotExist(err) {
log.Debugf("Symlinking %s => %s", dest, src)
if err = os.Symlink(src, dest); err != nil {
return err
}
}
return nil
}
func mountCgroup(cgroup string) error {
if err := createDirs("/sys/fs/cgroup/" + cgroup); err != nil {
return err
}
if err := createMounts([][]string{{"none", "/sys/fs/cgroup/" + cgroup, "cgroup", cgroup}}...); err != nil {
return err
}
parts := strings.Split(cgroup, ",")
if len(parts) > 1 {
for _, part := range parts {
if err := CreateSymlink("/sys/fs/cgroup/"+cgroup, "/sys/fs/cgroup/"+part); err != nil {
return err
}
}
}
return nil
}
func execDocker(config *Config, docker, cmd string, args []string) (*exec.Cmd, error) {
if len(args) > 0 && args[0] == "docker" {
args = args[1:]
}
log.Debugf("Launching Docker %s %s %v", docker, cmd, args)
env := os.Environ()
if len(config.Environment) != 0 {
env = append(env, config.Environment...)
}
if config.Fork {
cmd := exec.Command(docker, args...)
if !config.NoLog {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
cmd.Env = env
err := cmd.Start()
if err != nil {
return cmd, err
}
if config.PidOne {
PidOne()
}
return cmd, err
} else {
err := syscall.Exec(expand(docker), append([]string{cmd}, args...), env)
return nil, err
}
}
func copyDefault(folder, name string) error {
defaultFile := path.Join(defaultPrefix, folder, name)
if err := CopyFile(defaultFile, folder, name); err != nil {
return err
}
return nil
}
func copyDefaultFolder(folder string) error {
log.Debugf("Copying folder %s", folder)
defaultFolder := path.Join(defaultPrefix, folder)
files, _ := ioutil.ReadDir(defaultFolder)
for _, file := range files {
var err error
if file.IsDir() {
err = copyDefaultFolder(path.Join(folder, file.Name()))
} else {
err = copyDefault(folder, file.Name())
}
if err != nil {
return err
}
}
return nil
}
func defaultFiles(files ...string) error {
for _, file := range files {
dir := path.Dir(file)
name := path.Base(file)
if err := copyDefault(dir, name); err != nil {
return err
}
}
return nil
}
func defaultFolders(folders ...string) error {
for _, folder := range folders {
if err := copyDefaultFolder(folder); err != nil {
return err
}
}
return nil
}
func CopyFile(src, folder, name string) error {
if _, err := os.Lstat(src); os.IsNotExist(err) {
log.Debugf("Not copying %s, does not exists", src)
return nil
}
dst := path.Join(folder, name)
if _, err := os.Lstat(dst); err == nil {
log.Debugf("Not copying %s => %s already exists", src, dst)
return nil
}
if err := createDirs(folder); err != nil {
return err
}
stat, err := os.Lstat(src)
if err != nil {
return err
}
if stat.Mode()&os.ModeSymlink != 0 {
symDst, err := os.Readlink(src)
if err != nil {
log.Errorf("Failed to readlink: %v", err)
return err
}
// file is a symlink
log.Debugf("Symlinking %s => %s", dst, symDst)
return os.Symlink(symDst, dst)
}
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
log.Debugf("Copying %s => %s", src, dst)
_, err = io.Copy(dstFile, srcFile)
return err
}
func tryCreateFile(name, content string) error {
if _, err := os.Stat(name); err == nil {
return nil
}
if err := createDirs(path.Dir(name)); err != nil {
return err
}
return ioutil.WriteFile(name, []byte(content), 0644)
}
func createPasswd() error {
return tryCreateFile("/etc/passwd", "root:x:0:0:root:/root:/bin/sh\n")
}
func createGroup() error {
return tryCreateFile("/etc/group", "root:x:0:\n")
}
func setupNetworking(cfg *Config) error {
if cfg == nil {
return nil
}
hostname, err := os.Hostname()
if err != nil {
return err
}
tryCreateFile("/etc/hosts", `127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.1.1 `+hostname)
if len(cfg.DnsConfig.Nameservers) != 0 {
if _, err := resolvconf.Build("/etc/resolv.conf", cfg.DnsConfig.Nameservers, cfg.DnsConfig.Search, nil); err != nil {
return err
}
}
if cfg.BridgeName != "" && cfg.BridgeName != "none" {
log.Debugf("Creating bridge %s (%s)", cfg.BridgeName, cfg.BridgeAddress)
if err := netconf.ApplyNetworkConfigs(&config.NetworkConfig{
Interfaces: map[string]config.InterfaceConfig{
cfg.BridgeName: {
Address: cfg.BridgeAddress,
MTU: cfg.BridgeMtu,
Bridge: "true",
},
},
}); err != nil {
return err
}
}
return nil
}
func GetValue(index int, args []string) string {
val := args[index]
parts := strings.SplitN(val, "=", 2)
if len(parts) == 1 {
if len(args) > index+1 {
return args[index+1]
} else {
return ""
}
} else {
return parts[1]
}
}
func ParseConfig(config *Config, args ...string) []string {
for i, arg := range args {
if strings.HasPrefix(arg, "--bip") {
config.BridgeAddress = GetValue(i, args)
} else if strings.HasPrefix(arg, "--fixed-cidr") {
config.BridgeAddress = GetValue(i, args)
} else if strings.HasPrefix(arg, "-b") || strings.HasPrefix(arg, "--bridge") {
config.BridgeName = GetValue(i, args)
} else if strings.HasPrefix(arg, "--config-file") {
config.DaemonConfig = GetValue(i, args)
} else if strings.HasPrefix(arg, "--mtu") {
mtu, err := strconv.Atoi(GetValue(i, args))
if err != nil {
config.BridgeMtu = mtu
}
} else if strings.HasPrefix(arg, "-g") || strings.HasPrefix(arg, "--graph") {
config.GraphDirectory = GetValue(i, args)
}
}
if config.BridgeName != "" && config.BridgeAddress != "" {
newArgs := []string{}
skip := false
for _, arg := range args {
if skip {
skip = false
continue
}
if arg == "--bip" {
skip = true
continue
} else if strings.HasPrefix(arg, "--bip=") {
continue
}
newArgs = append(newArgs, arg)
}
args = newArgs
}
return args
}
func PrepareFs(config *Config) error {
if err := createMounts(mounts...); err != nil {
return err
}
createOptionalMounts(optionalMounts...)
if err := mountCgroups(config.CgroupHierarchy); err != nil {
return err
}
if err := createLayout(config); err != nil {
return err
}
if err := firstPrepare(); err != nil {
return err
}
return nil
}
func touchSocket(path string) error {
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
return err
}
return ioutil.WriteFile(path, []byte{}, 0700)
}
func touchSockets(args ...string) error {
touched := false
for i, arg := range args {
if strings.HasPrefix(arg, "-H") {
val := GetValue(i, args)
if strings.HasPrefix(val, "unix://") {
val = val[len("unix://"):]
log.Debugf("Creating temp file at %s", val)
if err := touchSocket(val); err != nil {
return err
}
touched = true
}
}
}
if !touched {
return touchSocket("/var/run/docker.sock")
}
return nil
}
func createDaemonConfig(config *Config) error {
if config.DaemonConfig == "" {
return nil
}
if _, err := os.Stat(config.DaemonConfig); os.IsNotExist(err) {
if err := os.MkdirAll(path.Dir(config.DaemonConfig), 0755); err != nil {
return err
}
return ioutil.WriteFile(config.DaemonConfig, []byte("{}"), 0600)
}
return nil
}
func cleanupFiles(graphDirectory string) {
zeroFiles := []string{
"/etc/docker/key.json",
"/etc/docker/daemon.json",
"/etc/docker/system-daemon.json",
path.Join(graphDirectory, "image/overlay/repositories.json"),
}
for _, file := range zeroFiles {
if stat, err := os.Stat(file); err == nil {
if stat.Size() < 2 {
log.Warnf("Deleting invalid json file: %s", file)
os.Remove(file)
}
}
}
}
func createLayout(config *Config) error {
if err := createDirs("/tmp", "/root/.ssh", "/var", "/usr/lib"); err != nil {
return err
}
graphDirectory := config.GraphDirectory
if config.GraphDirectory == "" {
graphDirectory = "/var/lib/docker"
}
if err := createDirs(graphDirectory); err != nil {
return err
}
if err := createDaemonConfig(config); err != nil {
return err
}
cleanupFiles(graphDirectory)
selinux.SetFileContext(graphDirectory, "system_u:object_r:var_lib_t:s0")
return CreateSymlinks([][]string{
{"usr/lib", "/lib"},
{"usr/sbin", "/sbin"},
{"../run", "/var/run"},
})
}
func firstPrepare() error {
os.Setenv("PATH", "/sbin:/usr/sbin:/usr/bin")
if err := defaultFiles(
"/etc/ssl/certs/ca-certificates.crt",
"/etc/passwd",
"/etc/group",
); err != nil {
return err
}
if err := defaultFolders(
"/etc/docker",
"/etc/selinux",
"/etc/selinux/ros",
"/etc/selinux/ros/policy",
"/etc/selinux/ros/contexts",
"/var/lib/cni",
); err != nil {
return err
}
if err := createPasswd(); err != nil {
return err
}
if err := createGroup(); err != nil {
return err
}
return nil
}
func secondPrepare(config *Config, docker string, args ...string) error {
if err := setupNetworking(config); err != nil {
return err
}
if err := touchSockets(args...); err != nil {
return err
}
if err := setupLogging(config); err != nil {
return err
}
for _, i := range []string{docker, iptables, modprobe} {
if err := setupBin(config, i); err != nil {
return err
}
}
if err := setUlimit(config); err != nil {
return err
}
ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1"), 0655)
return nil
}
func expand(bin string) string {
expanded, err := exec.LookPath(bin)
if err == nil {
return expanded
}
return bin
}
func setupBin(config *Config, bin string) error {
expanded, err := exec.LookPath(bin)
if err == nil {
return nil
}
expanded, err = exec.LookPath(bin + distSuffix)
if err != nil {
// Purposely not returning error
return nil
}
return CreateSymlink(expanded, expanded[:len(expanded)-len(distSuffix)])
}
func setupLogging(config *Config) error {
if config.LogFile == "" {
return nil
}
if err := createDirs(path.Dir(config.LogFile)); err != nil {
return err
}
output, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return err
}
syscall.Dup3(int(output.Fd()), int(os.Stdout.Fd()), 0)
syscall.Dup3(int(output.Fd()), int(os.Stderr.Fd()), 0)
return nil
}
func setUlimit(cfg *Config) error {
var rLimit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
return err
}
if cfg.NoFiles == 0 {
rLimit.Max = 1000000
} else {
rLimit.Max = cfg.NoFiles
}
rLimit.Cur = rLimit.Max
return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
}
func runOrExec(config *Config, docker string, args ...string) (*exec.Cmd, error) {
if err := secondPrepare(config, docker, args...); err != nil {
return nil, err
}
cmd := path.Base(docker)
if config != nil && config.CommandName != "" {
cmd = config.CommandName
}
if cmd == "dockerd" && len(args) > 1 && args[0] == "daemon" {
args = args[1:]
}
return execDocker(config, docker, cmd, args)
}
func LaunchDocker(config *Config, docker string, args ...string) (*exec.Cmd, error) {
if err := PrepareFs(config); err != nil {
return nil, err
}
return runOrExec(config, docker, args...)
}
func Main() {
if os.Getenv("DOCKER_LAUNCH_DEBUG") == "true" {
log.SetLevel(log.DebugLevel)
}
if len(os.Args) < 2 {
log.Fatalf("Usage Example: %s /usr/bin/docker -d -D", os.Args[0])
}
args := []string{}
if len(os.Args) > 1 {
args = os.Args[2:]
}
var config Config
args = ParseConfig(&config, args...)
if os.Getenv("DOCKER_LAUNCH_REAP") == "true" {
config.Fork = true
config.PidOne = true
}
log.Debugf("Launch config %#v", config)
_, err := LaunchDocker(&config, os.Args[1], args...)
if err != nil {
log.Fatal(err)
}
}

View File

@ -6,9 +6,9 @@ import (
"strings"
log "github.com/Sirupsen/logrus"
"github.com/rancher/docker-from-scratch"
"github.com/rancher/os/compose"
"github.com/rancher/os/config"
"github.com/rancher/os/dfs"
"github.com/rancher/os/util"
)
@ -36,7 +36,7 @@ func startDocker(cfg *config.CloudConfig) (chan interface{}, error) {
launchConfig.LogFile = ""
launchConfig.NoLog = true
cmd, err := dockerlaunch.LaunchDocker(launchConfig, config.SYSTEM_DOCKER_BIN, args...)
cmd, err := dfs.LaunchDocker(launchConfig, config.SYSTEM_DOCKER_BIN, args...)
if err != nil {
return nil, err
}

View File

@ -12,8 +12,8 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/mount"
"github.com/rancher/docker-from-scratch"
"github.com/rancher/os/config"
"github.com/rancher/os/dfs"
"github.com/rancher/os/util"
"github.com/rancher/os/util/network"
)
@ -27,7 +27,7 @@ const (
)
var (
mountConfig = dockerlaunch.Config{
mountConfig = dfs.Config{
CgroupHierarchy: map[string]string{
"cpu": "cpu",
"cpuacct": "cpu",
@ -162,10 +162,10 @@ func tryMountAndBootstrap(cfg *config.CloudConfig) (*config.CloudConfig, error)
return mountOem(cfg)
}
func getLaunchConfig(cfg *config.CloudConfig, dockerCfg *config.DockerConfig) (*dockerlaunch.Config, []string) {
var launchConfig dockerlaunch.Config
func getLaunchConfig(cfg *config.CloudConfig, dockerCfg *config.DockerConfig) (*dfs.Config, []string) {
var launchConfig dfs.Config
args := dockerlaunch.ParseConfig(&launchConfig, dockerCfg.FullArgs()...)
args := dfs.ParseConfig(&launchConfig, dockerCfg.FullArgs()...)
launchConfig.DnsConfig.Nameservers = cfg.Rancher.Defaults.Network.Dns.Nameservers
launchConfig.DnsConfig.Search = cfg.Rancher.Defaults.Network.Dns.Search
@ -220,7 +220,7 @@ func RunInit() error {
boot2DockerEnvironment := false
initFuncs := []config.CfgFunc{
func(c *config.CloudConfig) (*config.CloudConfig, error) {
return c, dockerlaunch.PrepareFs(&mountConfig)
return c, dfs.PrepareFs(&mountConfig)
},
mountOem,
func(_ *config.CloudConfig) (*config.CloudConfig, error) {
@ -280,7 +280,7 @@ func RunInit() error {
},
loadModules,
func(c *config.CloudConfig) (*config.CloudConfig, error) {
return c, dockerlaunch.PrepareFs(&mountConfig)
return c, dfs.PrepareFs(&mountConfig)
},
func(c *config.CloudConfig) (*config.CloudConfig, error) {
network.SetProxyEnvironmentVariables(c)
@ -300,7 +300,7 @@ func RunInit() error {
launchConfig.Fork = !cfg.Rancher.SystemDocker.Exec
log.Info("Launching System Docker")
_, err = dockerlaunch.LaunchDocker(launchConfig, config.SYSTEM_DOCKER_BIN, args...)
_, err = dfs.LaunchDocker(launchConfig, config.SYSTEM_DOCKER_BIN, args...)
if err != nil {
return err
}

View File

@ -10,8 +10,8 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/archive"
"github.com/rancher/docker-from-scratch"
"github.com/rancher/os/config"
"github.com/rancher/os/dfs"
)
func cleanupTarget(rootfs, targetUsr, usr, usrVer, tmpDir string) (bool, error) {
@ -21,7 +21,7 @@ func cleanupTarget(rootfs, targetUsr, usr, usrVer, tmpDir string) (bool, error)
return false, err
}
if err := dockerlaunch.CreateSymlink(usrVer, path.Join(rootfs, "usr")); err != nil {
if err := dfs.CreateSymlink(usrVer, path.Join(rootfs, "usr")); err != nil {
return false, err
}

View File

@ -6,7 +6,6 @@ import (
"github.com/docker/docker/docker"
"github.com/docker/docker/pkg/reexec"
"github.com/rancher/cniglue"
"github.com/rancher/docker-from-scratch"
"github.com/rancher/os/cmd/cloudinitexecute"
"github.com/rancher/os/cmd/cloudinitsave"
"github.com/rancher/os/cmd/console"
@ -20,6 +19,7 @@ import (
"github.com/rancher/os/cmd/systemdocker"
"github.com/rancher/os/cmd/userdocker"
"github.com/rancher/os/cmd/wait"
"github.com/rancher/os/dfs"
osInit "github.com/rancher/os/init"
)
@ -30,7 +30,7 @@ var entrypoints = map[string]func(){
"console.sh": console.Main,
"docker": docker.Main,
"docker-init": dockerinit.Main,
"dockerlaunch": dockerlaunch.Main,
"dockerlaunch": dfs.Main,
"halt": power.Halt,
"init": osInit.MainInit,
"netconf": network.Main,

143
netconf/bonding.go Normal file
View File

@ -0,0 +1,143 @@
package netconf
import (
"io/ioutil"
"os"
"os/exec"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/vishvananda/netlink"
)
const (
base = "/sys/class/net/"
bondingMasters = "/sys/class/net/bonding_masters"
)
type Bonding struct {
name string
}
func (b *Bonding) init() error {
_, err := os.Stat(bondingMasters)
if os.IsNotExist(err) {
logrus.Info("Loading bonding kernel module")
cmd := exec.Command("modprobe", "bonding")
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdin
err = cmd.Run()
if err != nil {
for i := 0; i < 30; i++ {
if _, err = os.Stat(bondingMasters); err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
}
}
_, err = os.Stat(bondingMasters)
return err
}
func contains(file, word string) (bool, error) {
words, err := ioutil.ReadFile(file)
if err != nil {
return false, err
}
for _, s := range strings.Split(strings.TrimSpace(string(words)), " ") {
if s == strings.TrimSpace(word) {
return true, nil
}
}
return false, nil
}
func (b *Bonding) linkDown() error {
link, err := netlink.LinkByName(b.name)
if err != nil {
return err
}
return netlink.LinkSetDown(link)
}
func (b *Bonding) ListSlaves() ([]string, error) {
file := base + b.name + "/bonding/slaves"
words, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
result := []string{}
for _, s := range strings.Split(strings.TrimSpace(string(words)), " ") {
if s != "" {
result = append(result, s)
}
}
return result, nil
}
func (b *Bonding) RemoveSlave(slave string) error {
if ok, err := contains(base+b.name+"/bonding/slaves", slave); err != nil {
return err
} else if !ok {
return nil
}
p := base + b.name + "/bonding/slaves"
logrus.Infof("Removing slave %s from master %s", slave, b.name)
return ioutil.WriteFile(p, []byte("-"+slave), 0644)
}
func (b *Bonding) AddSlave(slave string) error {
if ok, err := contains(base+b.name+"/bonding/slaves", slave); err != nil {
return err
} else if ok {
return nil
}
p := base + b.name + "/bonding/slaves"
logrus.Infof("Adding slave %s to master %s", slave, b.name)
return ioutil.WriteFile(p, []byte("+"+slave), 0644)
}
func (b *Bonding) Opt(key, value string) error {
if key == "mode" {
// Don't care about errors here
b.linkDown()
slaves, _ := b.ListSlaves()
for _, slave := range slaves {
b.RemoveSlave(slave)
}
}
p := base + b.name + "/bonding/" + key
if err := ioutil.WriteFile(p, []byte(value), 0644); err != nil {
logrus.Errorf("Failed to set %s=%s on %s: %v", key, value, b.name, err)
return err
} else {
logrus.Infof("Set %s=%s on %s", key, value, b.name)
}
return nil
}
func Bond(name string) (*Bonding, error) {
b := &Bonding{name: name}
if err := b.init(); err != nil {
return nil, err
}
if ok, err := contains(bondingMasters, name); err != nil {
return nil, err
} else if ok {
return b, nil
}
logrus.Infof("Creating bond %s", name)
return b, ioutil.WriteFile(bondingMasters, []byte("+"+name), 0644)
}

48
netconf/bridge.go Normal file
View File

@ -0,0 +1,48 @@
package netconf
import (
"fmt"
"github.com/vishvananda/netlink"
)
type Bridge struct {
name string
}
func NewBridge(name string) (*Bridge, error) {
b := &Bridge{name: name}
return b, b.init()
}
func (b *Bridge) init() error {
link, err := netlink.LinkByName(b.name)
if err == nil {
if _, ok := link.(*netlink.Bridge); !ok {
return fmt.Errorf("%s is not a bridge device", b.name)
}
return nil
}
bridge := netlink.Bridge{}
bridge.LinkAttrs.Name = b.name
return netlink.LinkAdd(&bridge)
}
func (b *Bridge) AddLink(link netlink.Link) error {
existing, err := netlink.LinkByName(b.name)
if err != nil {
return err
}
if bridge, ok := existing.(*netlink.Bridge); ok {
if link.Attrs().MasterIndex != bridge.Index {
return netlink.LinkSetMaster(link, bridge)
}
} else {
return fmt.Errorf("%s is not a bridge", b.name)
}
return nil
}

75
netconf/ipv4ll_linux.go Normal file
View File

@ -0,0 +1,75 @@
package netconf
import (
"encoding/binary"
"fmt"
"math/rand"
"net"
log "github.com/Sirupsen/logrus"
"github.com/j-keck/arping"
"github.com/vishvananda/netlink"
)
func AssignLinkLocalIP(link netlink.Link) error {
ifaceName := link.Attrs().Name
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
log.Error("could not get information about interface")
return err
}
addrs, err := iface.Addrs()
if err != nil {
log.Error("Error fetching existing ip on interface")
}
for _, addr := range addrs {
if addr.String()[:7] == "169.254" {
log.Info("Link Local IP already set on interface")
return nil
}
}
randSource, err := getPseudoRandomGenerator(link.Attrs().HardwareAddr)
if err != nil {
return err
}
// try a random address upto 10 times
for i := 0; i < 10; i++ {
randGenerator := rand.New(*randSource)
randomNum := randGenerator.Uint32()
dstIP := getNewIPV4LLAddr(randomNum)
if dstIP[2] == 0 || dstIP[2] == 255 {
i--
continue
}
_, _, err := arping.PingOverIfaceByName(dstIP, ifaceName)
if err != nil {
// this ip is not being used
addr, err := netlink.ParseAddr(dstIP.String() + "/16")
if err != nil {
log.Errorf("error while parsing ipv4ll addr, err = %v", err)
return err
}
if err := netlink.AddrAdd(link, addr); err != nil {
log.Error("ipv4ll addr add failed")
return err
}
log.Infof("Set %s on %s", dstIP.String(), link.Attrs().Name)
return nil
}
}
log.Error("Could not find a suitable ipv4ll")
return fmt.Errorf("Could not find a suitable ipv4ll")
}
func getNewIPV4LLAddr(randomNum uint32) net.IP {
byte1 := randomNum & 255 // use least significant 8 bits
byte2 := randomNum >> 24 // use most significant 8 bits
return []byte{169, 254, byte(byte1), byte(byte2)}
}
func getPseudoRandomGenerator(haAddr []byte) (*rand.Source, error) {
seed, _ := binary.Varint(haAddr)
src := rand.NewSource(seed)
return &src, nil
}

396
netconf/netconf_linux.go Normal file
View File

@ -0,0 +1,396 @@
package netconf
import (
"bytes"
"errors"
"net"
"os"
"os/exec"
"strings"
"sync"
"syscall"
log "github.com/Sirupsen/logrus"
"github.com/flynn/go-shlex"
"github.com/rancher/os/config"
"github.com/ryanuber/go-glob"
"github.com/vishvananda/netlink"
)
const (
CONF = "/var/lib/rancher/conf"
MODE = "mode"
)
var (
defaultDhcpArgs = []string{"dhcpcd", "-MA4"}
)
func createInterfaces(netCfg *config.NetworkConfig) {
configured := map[string]bool{}
for name, iface := range netCfg.Interfaces {
if iface.Bridge == "true" {
if _, err := NewBridge(name); err != nil {
log.Errorf("Failed to create bridge %s: %v", name, err)
}
} else if iface.Bridge != "" {
if _, err := NewBridge(iface.Bridge); err != nil {
log.Errorf("Failed to create bridge %s: %v", iface.Bridge, err)
}
} else if iface.Bond != "" {
bond, err := Bond(iface.Bond)
if err != nil {
log.Errorf("Failed to create bond %s: %v", iface.Bond, err)
continue
}
if !configured[iface.Bond] {
if bondIface, ok := netCfg.Interfaces[iface.Bond]; ok {
// Other settings depends on mode, so set it first
if v, ok := bondIface.BondOpts[MODE]; ok {
bond.Opt(MODE, v)
}
for k, v := range bondIface.BondOpts {
if k != MODE {
bond.Opt(k, v)
}
}
configured[iface.Bond] = true
}
}
}
}
}
func createSlaveInterfaces(netCfg *config.NetworkConfig) {
links, err := netlink.LinkList()
if err != nil {
log.Errorf("Failed to list links: %v", err)
return
}
for _, link := range links {
match, ok := findMatch(link, netCfg)
if !ok {
continue
}
vlanDefs, err := ParseVlanDefinitions(match.Vlans)
if err != nil {
log.Errorf("Failed to create vlans on device %s: %v", link.Attrs().Name, err)
continue
}
for _, vlanDef := range vlanDefs {
if _, err = NewVlan(link, vlanDef.Name, vlanDef.Id); err != nil {
log.Errorf("Failed to create vlans on device %s, id %d: %v", link.Attrs().Name, vlanDef.Id, err)
}
}
}
}
func findMatch(link netlink.Link, netCfg *config.NetworkConfig) (config.InterfaceConfig, bool) {
linkName := link.Attrs().Name
var match config.InterfaceConfig
exactMatch := false
found := false
for key, netConf := range netCfg.Interfaces {
if netConf.Match == "" {
netConf.Match = key
}
if netConf.Match == "" {
continue
}
if strings.HasPrefix(netConf.Match, "mac") {
haAddr, err := net.ParseMAC(netConf.Match[4:])
if err != nil {
log.Errorf("Failed to parse mac %s: %v", netConf.Match[4:], err)
continue
}
// Don't match mac address of the bond because it is the same as the slave
if bytes.Compare(haAddr, link.Attrs().HardwareAddr) == 0 && link.Attrs().Name != netConf.Bond {
// MAC address match is used over all other matches
return netConf, true
}
}
if !exactMatch && glob.Glob(netConf.Match, linkName) {
match = netConf
found = true
}
if netConf.Match == linkName {
// Found exact match, use it over wildcard match
match = netConf
exactMatch = true
}
}
return match, exactMatch || found
}
func populateDefault(netCfg *config.NetworkConfig) {
if netCfg.Interfaces == nil {
netCfg.Interfaces = map[string]config.InterfaceConfig{}
}
if len(netCfg.Interfaces) == 0 {
netCfg.Interfaces["eth*"] = config.InterfaceConfig{
DHCP: true,
}
}
if _, ok := netCfg.Interfaces["lo"]; !ok {
netCfg.Interfaces["lo"] = config.InterfaceConfig{
Address: "127.0.0.1/8",
}
}
}
func ApplyNetworkConfigs(netCfg *config.NetworkConfig) error {
populateDefault(netCfg)
log.Debugf("Config: %#v", netCfg)
runCmds(netCfg.PreCmds, "")
createInterfaces(netCfg)
createSlaveInterfaces(netCfg)
links, err := netlink.LinkList()
if err != nil {
return err
}
//apply network config
for _, link := range links {
linkName := link.Attrs().Name
if match, ok := findMatch(link, netCfg); ok && !match.DHCP {
if err := applyInterfaceConfig(link, match); err != nil {
log.Errorf("Failed to apply settings to %s : %v", linkName, err)
}
}
}
runCmds(netCfg.PostCmds, "")
return err
}
func RunDhcp(netCfg *config.NetworkConfig, setHostname, setDns bool) error {
populateDefault(netCfg)
links, err := netlink.LinkList()
if err != nil {
return err
}
dhcpLinks := map[string]string{}
for _, link := range links {
if match, ok := findMatch(link, netCfg); ok && match.DHCP {
dhcpLinks[link.Attrs().Name] = match.DHCPArgs
}
}
//run dhcp
wg := sync.WaitGroup{}
for iface, args := range dhcpLinks {
wg.Add(1)
go func(iface, args string) {
runDhcp(netCfg, iface, args, setHostname, setDns)
wg.Done()
}(iface, args)
}
wg.Wait()
return err
}
func runDhcp(netCfg *config.NetworkConfig, iface string, argstr string, setHostname, setDns bool) {
log.Infof("Running DHCP on %s", iface)
args := []string{}
if argstr != "" {
var err error
args, err = shlex.Split(argstr)
if err != nil {
log.Errorf("Failed to parse [%s]: %v", argstr, err)
}
}
if len(args) == 0 {
args = defaultDhcpArgs
}
if setHostname {
args = append(args, "-e", "force_hostname=true")
}
if !setDns {
args = append(args, "--nohook", "resolv.conf")
}
args = append(args, iface)
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Error(err)
}
}
func linkUp(link netlink.Link, netConf config.InterfaceConfig) error {
if err := netlink.LinkSetUp(link); err != nil {
log.Errorf("failed to setup link: %v", err)
return err
}
return nil
}
func applyAddress(address string, link netlink.Link, netConf config.InterfaceConfig) error {
addr, err := netlink.ParseAddr(address)
if err != nil {
return err
}
if err := netlink.AddrAdd(link, addr); err == syscall.EEXIST {
//Ignore this error
} else if err != nil {
log.Errorf("addr add failed: %v", err)
} else {
log.Infof("Set %s on %s", netConf.Address, link.Attrs().Name)
}
return nil
}
func setGateway(gateway string) error {
if gateway == "" {
return nil
}
gatewayIp := net.ParseIP(gateway)
if gatewayIp == nil {
return errors.New("Invalid gateway address " + gateway)
}
route := netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
Gw: gatewayIp,
}
if err := netlink.RouteAdd(&route); err == syscall.EEXIST {
//Ignore this error
} else if err != nil {
log.Errorf("gateway set failed: %v", err)
return err
}
log.Infof("Set default gateway %s", gateway)
return nil
}
func applyInterfaceConfig(link netlink.Link, netConf config.InterfaceConfig) error {
if netConf.Bond != "" {
if err := netlink.LinkSetDown(link); err != nil {
return err
}
b, err := Bond(netConf.Bond)
if err != nil {
return err
}
if err := b.AddSlave(link.Attrs().Name); err != nil {
return err
}
return nil
}
if netConf.Bridge != "" && netConf.Bridge != "true" {
b, err := NewBridge(netConf.Bridge)
if err != nil {
return err
}
if err := b.AddLink(link); err != nil {
return err
}
return linkUp(link, netConf)
}
if netConf.IPV4LL {
if err := AssignLinkLocalIP(link); err != nil {
log.Errorf("IPV4LL set failed: %v", err)
return err
}
} else {
addresses := []string{}
if netConf.Address != "" {
addresses = append(addresses, netConf.Address)
}
if len(netConf.Addresses) > 0 {
addresses = append(addresses, netConf.Addresses...)
}
for _, address := range addresses {
err := applyAddress(address, link, netConf)
if err != nil {
log.Errorf("Failed to apply address %s to %s: %v", address, link.Attrs().Name, err)
}
}
}
if netConf.MTU > 0 {
if err := netlink.LinkSetMTU(link, netConf.MTU); err != nil {
log.Errorf("set MTU Failed: %v", err)
return err
}
}
runCmds(netConf.PreUp, link.Attrs().Name)
if err := linkUp(link, netConf); err != nil {
return err
}
if err := setGateway(netConf.Gateway); err != nil {
log.Errorf("Fail to set gateway %s", netConf.Gateway)
}
if err := setGateway(netConf.GatewayIpv6); err != nil {
log.Errorf("Fail to set gateway %s", netConf.GatewayIpv6)
}
runCmds(netConf.PostUp, link.Attrs().Name)
return nil
}
func runCmds(cmds []string, iface string) {
for _, cmd := range cmds {
cmd = strings.TrimSpace(cmd)
if cmd == "" {
continue
}
args, err := shlex.Split(strings.Replace(cmd, "$iface", iface, -1))
if err != nil {
log.Errorf("Failed to parse command [%s]: %v", cmd, err)
continue
}
log.Infof("Running command %s %v", args[0], args[1:])
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Errorf("Failed to run command [%v]: %v", cmd, err)
continue
}
}
}

79
netconf/vlan.go Normal file
View File

@ -0,0 +1,79 @@
package netconf
import (
"fmt"
"strconv"
"strings"
"github.com/vishvananda/netlink"
)
type VlanDefinition struct {
Id int
Name string
}
type Vlan struct {
name string
link netlink.Link
id int
}
func NewVlan(link netlink.Link, name string, id int) (*Vlan, error) {
if name == "" {
name = fmt.Sprintf("%s.%d", link.Attrs().Name, id)
}
v := &Vlan{
name: name,
link: link,
id: id,
}
return v, v.init()
}
func (v *Vlan) init() error {
link, err := netlink.LinkByName(v.name)
if err == nil {
if _, ok := link.(*netlink.Vlan); !ok {
return fmt.Errorf("%s is not a VLAN device", v.name)
}
return nil
}
vlan := netlink.Vlan{}
vlan.ParentIndex = v.link.Attrs().Index
vlan.Name = v.name
vlan.VlanId = v.id
return netlink.LinkAdd(&vlan)
}
func ParseVlanDefinitions(vlans string) ([]VlanDefinition, error) {
vlans = strings.TrimSpace(vlans)
if vlans == "" {
return nil, nil
}
result := []VlanDefinition{}
for _, vlan := range strings.Split(vlans, ",") {
idName := strings.SplitN(strings.TrimSpace(vlan), ":", 2)
id, err := strconv.Atoi(idName[0])
if err != nil {
return nil, fmt.Errorf("Invalid format in %s: %v", vlans, err)
}
def := VlanDefinition{
Id: id,
}
if len(idName) > 1 {
def.Name = idName[1]
}
result = append(result, def)
}
return result, nil
}

View File

@ -9,3 +9,8 @@ func InitializeSelinux() (int, error) {
ret, err := C.selinux_init_load_policy(&enforce)
return int(ret), err
}
func SetFileContext(path string, context string) (int, error) {
ret, err := C.setfilecon(C.CString(path), C.CString(context))
return int(ret), err
}