1
1
Fork 0

호스트사이드 네트워크 설정기 제작

This commit is contained in:
Sangbum Kim 2017-01-09 12:20:23 +09:00
parent 107ef036de
commit 7746285212
6 changed files with 841 additions and 128 deletions

View File

@ -9,15 +9,14 @@ DefaultDependencies=no
After=systemd-udevd.service dbus.service network-pre.target systemd-sysusers.service systemd-sysctl.service
Before=network.target multi-user.target shutdown.target
Conflicts=shutdown.target
Wants=network.target
Wants=network-online.target
[Service]
# the VT is cleared by TTYVTDisallocate
RemainAfterExit=yes
#Environment=IP_ADDR= GATEWAY_ADDR=
EnvironmentFile=/proc/1/environ
ExecStart=/connet
ExecStart=/usr/bin/connet
Type=oneshot
[Install]
WantedBy=multi-user.target

BIN
gp/gp

Binary file not shown.

View File

@ -1,123 +1,419 @@
package main
import (
"github.com/vishvananda/netlink"
"io/ioutil"
"strings"
"net"
// "github.com/ttacon/libphonenumber"
"bytes"
"errors"
"fmt"
"net"
"os"
"regexp"
"os/exec"
"path/filepath"
"strings"
"syscall"
"io/ioutil"
kingpin "gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/yaml.v2"
"strconv"
errs "github.com/pkg/errors"
"github.com/vishvananda/netlink"
)
var env map[string]string=make(map[string]string)
var (
app = kingpin.New("plugin", "nspawn Bootstrapper.").Author("Sangbum Kim<sangbumkim@amuz.es>")
verbosePtr = app.Flag("verbose", "Enable verbose mode.").Short('v').Bool()
clearPtr = app.Flag("clear", "clear os environment.").Short('p').Bool()
nspawnPathPtr = app.Flag("nspawn-path", "systemd-nspawn location.").Short('s').String()
containerHomePtr = app.Flag("container-home", "container residence path").Default("/container").Short('d').String()
configFileNamePtr = app.Flag("config-file", "config file name").Default("settings.yml").HintOptions("FILENAME").Short('c').String()
nodeNamePtr = app.Arg("nodeName", "nodename to spawn.").Required().String()
bindIfNamePtr = app.Arg("bindInterface", "interface name to bind.").Required().String()
)
func main(){
readInitEnviron()
macAddr,_ := net.ParseMAC(getEnv("IF_ADDR"))
// fpAddr,_ := netlink.ParseAddr(getEnv("IP_ADDR"))
// gatewayAddr := net.ParseIP(getEnv("GATEWAY_ADDR"))
linkInterfaceType:=getEnvWithDefault("IF_TYPE","macvlan")
link:=findFirstInterface(linkInterfaceType)
renameInterface(link,"eth0")
renameMacAddress(link,macAddr)
/*
#netlink.AddrAdd(link,ipAddr)
#netlink.LinkSetUp(link)
#addRoute(gatewayAddr,link)
*/
type mountPoint struct {
Name string `yaml:"name"`
Map string `yaml:"map"`
Readonly bool `yaml:"readonly"`
}
func readInitEnviron(){
envFilePath:="/proc/1/environ"
content, err := ioutil.ReadFile(envFilePath)
kvParseRegex,_:=regexp.Compile("^([^=]+)=(.+)$")
func (mp mountPoint) Option(nodePath string) (option string, err error) {
var mountPointPath string
if filepath.IsAbs(mp.Name){
mountPointPath= mp.Name
}else{
mountPointPath= filepath.Clean(filepath.Join(nodePath, "volume", mp.Name))
}
if _, err = os.Stat(mountPointPath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("mount point %s is not exist", mountPointPath))
return
}
var buffer bytes.Buffer
if mp.Readonly {
buffer.WriteString("--bind-ro=")
} else {
buffer.WriteString("--bind=")
}
buffer.WriteString(mountPointPath)
buffer.WriteRune(':')
buffer.WriteString(mp.Map)
option = buffer.String()
return
}
func (mp mountPoint) String() string {
var buffer bytes.Buffer
buffer.WriteString(mp.Name)
if mp.Readonly {
buffer.WriteString("(readonly)")
}
buffer.WriteString("->")
buffer.WriteString(mp.Map)
return buffer.String()
}
type config struct {
MoundPoint []mountPoint `yaml:"mount-point"`
}
func (c config) String() string {
var buffer bytes.Buffer
buffer.WriteString("config:\n")
buffer.WriteString(" mount-point:\n")
for _, entry := range c.MoundPoint {
buffer.WriteString(" - ")
buffer.WriteString(entry.String())
buffer.WriteRune('\n')
}
return buffer.String()
}
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
func getIP(host string) (ip net.IP, err error) {
var addrs []net.IP
addrs, err = net.LookupIP(host)
if err != nil {
panic(fmt.Sprintf("environ file %s not existed!",envFilePath))
return
}
return addrs[0], nil
}
func getOutboundIPInfo(verbose bool) (gw net.IP, cidr uint64, linkname string, err error) {
// netlink.RouteList
var links []netlink.Link
links, err = netlink.LinkList()
if err != nil {
return
}
if verbose {
fmt.Println("scan system network interfaces:")
}
for _, link := range links {
attrs := link.Attrs()
if verbose {
fmt.Printf(" interface %s : ", attrs.Name)
}
if (attrs.Flags & net.FlagUp) == 0 {
if verbose {
fmt.Printf(" not up -- abandon!\n")
}
lines := strings.Split(string(content), "\x00")
for _,element := range lines {
result_slice := kvParseRegex.FindStringSubmatch(element)
if len(result_slice) < 2 {
continue
} else {
env[result_slice[1]]=result_slice[2]
if verbose {
fmt.Printf(" up!\n")
}
}
if verbose {
fmt.Println(" getting routing tables ")
}
func renameMacAddress(link netlink.Link,new_name net.HardwareAddr){
err:=netlink.LinkSetHardwareAddr(link,new_name)
if(err != nil){
panic(fmt.Sprintf("cannot rename mac address %s -> %s",link.Attrs().HardwareAddr,new_name))
var routes []netlink.Route
routes, err = netlink.RouteList(link, netlink.FAMILY_V4)
if err != nil {
continue
}
var (
matchedGw net.IP
matchedCidr uint64
cidrOk = false
)
for _, route := range routes {
if verbose {
fmt.Printf(" routing table %s\n", route.String())
}
if route.Dst == nil {
if verbose {
fmt.Printf(" - default gateway\n")
}
matchedGw = route.Gw
break
} else if verbose {
fmt.Printf(" - no default gateway\n")
}
}
if matchedGw == nil {
if verbose {
fmt.Println(" interface have not a default routing rule.")
}
continue
} else if verbose {
fmt.Printf(" interface have a default routing rule. gateway: %s", matchedGw.String())
}
if verbose {
fmt.Println(" getting networks ")
}
var networks []netlink.Addr
networks, err = netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil {
fmt.Println(" - cannot getting networks ")
continue
}
for _, network := range networks {
if verbose {
fmt.Printf(" network %s\n", network.String())
}
if network.Contains(matchedGw) {
if verbose {
fmt.Printf(" - default network\n")
}
netSlice := strings.Split(network.IPNet.String(), "/")
if len(netSlice) < 2 {
continue
}
if cidrData, err := strconv.ParseUint(netSlice[len(netSlice)-1], 10, 64); err == nil {
matchedCidr = cidrData
cidrOk = true
break
}
} else if verbose {
fmt.Printf(" - no default network\n")
}
}
if cidrOk {
gw, cidr, linkname = matchedGw, matchedCidr, attrs.Name
if verbose {
fmt.Println(" ok! matched ")
}
break
} else if verbose {
fmt.Println(" abandon this interface ")
}
}
if gw == nil {
err = errors.New("default gateway or cidr not found!")
}
return
}
func checkInterfaceValid(bindIfName string) (err error) {
var link netlink.Link
link, err = netlink.LinkByName(bindIfName)
if err != nil {
return
}
attrs := link.Attrs()
if (attrs.Flags & net.FlagUp) == 0 {
err = errors.New("bind interface had been deactivated")
}
return
}
func checkNodeBasicDirectory(containerHomePath string, nodeName string, configFileName string) (nodePath string, nodeConfigPath string, err error) {
var containerHomeInfo os.FileInfo
if containerHomeInfo, err = os.Stat(containerHomePath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("container home path %s is not exist", containerHomePath))
return
} else if !containerHomeInfo.IsDir() {
err = errors.New(fmt.Sprintf("container home path %s is not a directory", containerHomePath))
return
}
nodePath = filepath.Join(containerHomePath, nodeName)
var nodePathInfo os.FileInfo
if nodePathInfo, err = os.Stat(nodePath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("node path %s is not exist", nodePath))
return
} else if !nodePathInfo.IsDir() {
err = errors.New(fmt.Sprintf("node path %s is not a directory", nodePath))
return
} else {
fmt.Printf("rename mac address %s -> %s\n",link.Attrs().HardwareAddr,new_name)
subDirNames := [...]string{"merge", "work", "delta"}
for _, subDirName := range subDirNames {
subPath := filepath.Join(nodePath, subDirName)
var subPathInfo os.FileInfo
if subPathInfo, err = os.Stat(subPath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("sub path %s is not exist", subPath))
return
} else if !subPathInfo.IsDir() {
err = errors.New(fmt.Sprintf("sub path %s is not a directory", subPath))
return
}
}
}
nodeConfigPath = filepath.Join(containerHomePath, nodeName, configFileName)
var nodeConfigPathInfo os.FileInfo
if nodeConfigPathInfo, err = os.Stat(nodeConfigPath); os.IsNotExist(err) || nodeConfigPathInfo.IsDir() {
nodeConfigPath = ""
}
return
}
func renameInterface(link netlink.Link,new_name string) {
err:=netlink.LinkSetName(link,new_name)
if(err != nil){
panic(fmt.Sprintf("cannot rename interface %s -> %s",link.Attrs().Name,new_name))
func LoadConfig(path string) (config config, err error) {
var (
source []byte
)
if path == "" {
} else if source, err = ioutil.ReadFile(path); err != nil {
} else if err = yaml.Unmarshal(source, &config); err != nil {
}
return
}
func main() {
_, err := app.Parse(os.Args[1:])
var (
verbose = *verbosePtr
clear = *clearPtr
nodeName = *nodeNamePtr
bindIfName = *bindIfNamePtr
configFileName = *configFileNamePtr
nspawnPath string
progress = -1
)
defer func() {
if r := recover(); r != nil {
fmt.Fprintln(os.Stderr, r)
os.Exit(progress)
}
}()
if err != nil {
panic(err)
}
progress = 1
containerHomePath, err := filepath.Abs(*containerHomePtr)
if err != nil {
panic(err)
}
progress++
nodePath, nodeConfigPath, err := checkNodeBasicDirectory(containerHomePath, nodeName, configFileName)
if err != nil {
panic(err)
} else if verbose {
var nodeConfigPathDesc = "not used"
if nodeConfigPath != "" {
nodeConfigPathDesc = nodeConfigPath
}
fmt.Printf(`sane container environment:
- container home : %s
- node home : %s
- node config : %s
`, containerHomePath, nodePath, nodeConfigPathDesc)
}
config, err := LoadConfig(nodeConfigPath)
if err != nil {
panic(err)
} else if verbose {
fmt.Printf(config.String())
}
progress++
if nspawnPathPtr != nil && *nspawnPathPtr != "" {
nspawnPath = *nspawnPathPtr
} else if nspawnPathData, err := exec.LookPath("systemd-nspawn"); err != nil {
panic(errs.Wrap(err, "cannot find systemd-nspawn"))
} else {
fmt.Printf("rename interface %s -> %s\n",link.Attrs().Name,new_name)
nspawnPath = nspawnPathData
if verbose {
fmt.Printf("systemd-nspawn found : %s\n", nspawnPath)
}
}
func findFirstInterface(if_type string) netlink.Link {
links,_:=netlink.LinkList()
for _,element := range links {
if(element.Type() == if_type){
fmt.Println(element.Attrs().Name)
return element
progress++
err = checkInterfaceValid(bindIfName)
if err != nil {
panic(errs.Wrap(err, "error in checkInterfaceValid"))
}
}
panic(fmt.Sprintf("cannot get type: %s interface",if_type))
progress++
nodeAddr, err := getIP(nodeName)
if err != nil {
panic(errs.Wrap(err, "error in getIP"))
}
func getEnv(variable string) string {
envValue,ok:=env[variable]
if ok {
return envValue
progress++
gw, cidr, linkname, err := getOutboundIPInfo(verbose)
if err != nil {
panic(errs.Wrap(err, "error in getOutboundIPInfo"))
}
envValue=os.Getenv(variable)
if(envValue == ""){
panic(fmt.Sprintf("cannot get ${%s}",variable))
if verbose {
fmt.Println("network configuration:")
fmt.Printf(" - bind interface{%s} : valid\n", bindIfName)
fmt.Printf(" - default routing info : ip %s/%d link %s\n", gw.String(), cidr, linkname)
fmt.Printf(" - node{%s} IP address : %s\n", nodeName, nodeAddr.String())
}
option := []string{
"--quiet",
"--boot",
"--link-journal=try-host",
"--notify-ready=true"}
option = append(option, fmt.Sprintf("--network-ipvlan=%s", bindIfName))
option = append(option, fmt.Sprintf("--setenv=IP_ADDR=%s/%d", nodeAddr.String(), cidr))
option = append(option, fmt.Sprintf("--setenv=GATEWAY_ADDR=%s", gw.String()))
option = append(option, fmt.Sprintf("--machine=%s", nodeName))
option = append(option, fmt.Sprintf("--directory=%s", filepath.Join(nodePath, "merge")))
for _, mountPoint := range config.MoundPoint {
if mountOption, err := mountPoint.Option(nodePath); err != nil {
panic(errs.Wrap(err, "error in mountOption"))
} else {
return envValue
option = append(option, mountOption)
}
}
func getEnvWithDefault(variable string, ret_is_empty string) string {
envValue,ok:=env[variable]
if ok {
return envValue
}
envValue=os.Getenv(variable)
if(envValue == ""){
return ret_is_empty
var env []string
if clear {
env = []string{}
} else {
return envValue
env = os.Environ()
if verbose {
fmt.Println("os environment:")
for _, segment := range env {
fmt.Println(" ", segment)
}
}
}
if verbose {
fmt.Println("systemd-nspawn spawning:")
fmt.Println(nspawnPath, " \\")
for _, segment := range option {
fmt.Println(" ", segment, " \\")
}
fmt.Println(" ;")
fmt.Println("bye!")
}
if err := syscall.Exec(nspawnPath, option, env); err != nil {
panic(errs.Wrap(err, "error in systemd-nspawn execution"))
}
}
func addRoute(gateway net.IP,link netlink.Link){
default_network := &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.CIDRMask(0, 32),
}
route := &netlink.Route{LinkIndex: link.Attrs().Index, Dst: default_network, Gw:gateway}
netlink.RouteAdd(route)
}

@ -1 +1 @@
Subproject commit 814b43a0c71f01a137581a3fadf37619e71751b8
Subproject commit 13fb20a9787c14bfc4c7996ce27d7224aa815812

23
main.go
View File

@ -15,25 +15,24 @@ var env map[string]string=make(map[string]string)
func main(){
readInitEnviron()
macAddr,_ := net.ParseMAC(getEnv("IF_ADDR"))
// fpAddr,_ := netlink.ParseAddr(getEnv("IP_ADDR"))
// gatewayAddr := net.ParseIP(getEnv("GATEWAY_ADDR"))
linkInterfaceType:=getEnvWithDefault("IF_TYPE","macvlan")
linkInterfaceType:=getEnvWithDefault("IF_TYPE","ipvlan")
link:=findFirstInterface(linkInterfaceType)
renameInterface(link,"eth0")
switch linkInterfaceType {
case "macvlan":
macAddr,_ := net.ParseMAC(getEnv("IF_ADDR"))
renameMacAddress(link,macAddr)
/*
#netlink.AddrAdd(link,ipAddr)
#netlink.LinkSetUp(link)
#addRoute(gatewayAddr,link)
*/
case "ipvlan":
ipAddr,_ := netlink.ParseAddr(getEnv("IP_ADDR"))
gatewayAddr := net.ParseIP(getEnv("GATEWAY_ADDR"))
netlink.AddrAdd(link,ipAddr)
netlink.LinkSetUp(link)
addRoute(gatewayAddr,link)
}
}
func readInitEnviron(){

419
plugin.go Normal file
View File

@ -0,0 +1,419 @@
package main
import (
// "github.com/ttacon/libphonenumber"
"bytes"
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"io/ioutil"
kingpin "gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/yaml.v2"
"strconv"
errs "github.com/pkg/errors"
"github.com/vishvananda/netlink"
)
var (
app = kingpin.New("plugin", "nspawn Bootstrapper.").Author("Sangbum Kim<sangbumkim@amuz.es>")
verbosePtr = app.Flag("verbose", "Enable verbose mode.").Short('v').Bool()
clearPtr = app.Flag("clear", "clear os environment.").Short('p').Bool()
nspawnPathPtr = app.Flag("nspawn-path", "systemd-nspawn location.").Short('s').String()
containerHomePtr = app.Flag("container-home", "container residence path").Default("/container").Short('d').String()
configFileNamePtr = app.Flag("config-file", "config file name").Default("settings.yml").HintOptions("FILENAME").Short('c').String()
nodeNamePtr = app.Arg("nodeName", "nodename to spawn.").Required().String()
bindIfNamePtr = app.Arg("bindInterface", "interface name to bind.").Required().String()
)
type mountPoint struct {
Name string `yaml:"name"`
Map string `yaml:"map"`
Readonly bool `yaml:"readonly"`
}
func (mp mountPoint) Option(nodePath string) (option string, err error) {
var mountPointPath string
if filepath.IsAbs(mp.Name){
mountPointPath= mp.Name
}else{
mountPointPath= filepath.Clean(filepath.Join(nodePath, "volume", mp.Name))
}
if _, err = os.Stat(mountPointPath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("mount point %s is not exist", mountPointPath))
return
}
var buffer bytes.Buffer
if mp.Readonly {
buffer.WriteString("--bind-ro=")
} else {
buffer.WriteString("--bind=")
}
buffer.WriteString(mountPointPath)
buffer.WriteRune(':')
buffer.WriteString(mp.Map)
option = buffer.String()
return
}
func (mp mountPoint) String() string {
var buffer bytes.Buffer
buffer.WriteString(mp.Name)
if mp.Readonly {
buffer.WriteString("(readonly)")
}
buffer.WriteString("->")
buffer.WriteString(mp.Map)
return buffer.String()
}
type config struct {
MoundPoint []mountPoint `yaml:"mount-point"`
}
func (c config) String() string {
var buffer bytes.Buffer
buffer.WriteString("config:\n")
buffer.WriteString(" mount-point:\n")
for _, entry := range c.MoundPoint {
buffer.WriteString(" - ")
buffer.WriteString(entry.String())
buffer.WriteRune('\n')
}
return buffer.String()
}
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
func getIP(host string) (ip net.IP, err error) {
var addrs []net.IP
addrs, err = net.LookupIP(host)
if err != nil {
return
}
return addrs[0], nil
}
func getOutboundIPInfo(verbose bool) (gw net.IP, cidr uint64, linkname string, err error) {
// netlink.RouteList
var links []netlink.Link
links, err = netlink.LinkList()
if err != nil {
return
}
if verbose {
fmt.Println("scan system network interfaces:")
}
for _, link := range links {
attrs := link.Attrs()
if verbose {
fmt.Printf(" interface %s : ", attrs.Name)
}
if (attrs.Flags & net.FlagUp) == 0 {
if verbose {
fmt.Printf(" not up -- abandon!\n")
}
continue
} else {
if verbose {
fmt.Printf(" up!\n")
}
}
if verbose {
fmt.Println(" getting routing tables ")
}
var routes []netlink.Route
routes, err = netlink.RouteList(link, netlink.FAMILY_V4)
if err != nil {
continue
}
var (
matchedGw net.IP
matchedCidr uint64
cidrOk = false
)
for _, route := range routes {
if verbose {
fmt.Printf(" routing table %s\n", route.String())
}
if route.Dst == nil {
if verbose {
fmt.Printf(" - default gateway\n")
}
matchedGw = route.Gw
break
} else if verbose {
fmt.Printf(" - no default gateway\n")
}
}
if matchedGw == nil {
if verbose {
fmt.Println(" interface have not a default routing rule.")
}
continue
} else if verbose {
fmt.Printf(" interface have a default routing rule. gateway: %s", matchedGw.String())
}
if verbose {
fmt.Println(" getting networks ")
}
var networks []netlink.Addr
networks, err = netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil {
fmt.Println(" - cannot getting networks ")
continue
}
for _, network := range networks {
if verbose {
fmt.Printf(" network %s\n", network.String())
}
if network.Contains(matchedGw) {
if verbose {
fmt.Printf(" - default network\n")
}
netSlice := strings.Split(network.IPNet.String(), "/")
if len(netSlice) < 2 {
continue
}
if cidrData, err := strconv.ParseUint(netSlice[len(netSlice)-1], 10, 64); err == nil {
matchedCidr = cidrData
cidrOk = true
break
}
} else if verbose {
fmt.Printf(" - no default network\n")
}
}
if cidrOk {
gw, cidr, linkname = matchedGw, matchedCidr, attrs.Name
if verbose {
fmt.Println(" ok! matched ")
}
break
} else if verbose {
fmt.Println(" abandon this interface ")
}
}
if gw == nil {
err = errors.New("default gateway or cidr not found!")
}
return
}
func checkInterfaceValid(bindIfName string) (err error) {
var link netlink.Link
link, err = netlink.LinkByName(bindIfName)
if err != nil {
return
}
attrs := link.Attrs()
if (attrs.Flags & net.FlagUp) == 0 {
err = errors.New("bind interface had been deactivated")
}
return
}
func checkNodeBasicDirectory(containerHomePath string, nodeName string, configFileName string) (nodePath string, nodeConfigPath string, err error) {
var containerHomeInfo os.FileInfo
if containerHomeInfo, err = os.Stat(containerHomePath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("container home path %s is not exist", containerHomePath))
return
} else if !containerHomeInfo.IsDir() {
err = errors.New(fmt.Sprintf("container home path %s is not a directory", containerHomePath))
return
}
nodePath = filepath.Join(containerHomePath, nodeName)
var nodePathInfo os.FileInfo
if nodePathInfo, err = os.Stat(nodePath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("node path %s is not exist", nodePath))
return
} else if !nodePathInfo.IsDir() {
err = errors.New(fmt.Sprintf("node path %s is not a directory", nodePath))
return
} else {
subDirNames := [...]string{"merge", "work", "delta"}
for _, subDirName := range subDirNames {
subPath := filepath.Join(nodePath, subDirName)
var subPathInfo os.FileInfo
if subPathInfo, err = os.Stat(subPath); os.IsNotExist(err) {
err = errors.New(fmt.Sprintf("sub path %s is not exist", subPath))
return
} else if !subPathInfo.IsDir() {
err = errors.New(fmt.Sprintf("sub path %s is not a directory", subPath))
return
}
}
}
nodeConfigPath = filepath.Join(containerHomePath, nodeName, configFileName)
var nodeConfigPathInfo os.FileInfo
if nodeConfigPathInfo, err = os.Stat(nodeConfigPath); os.IsNotExist(err) || nodeConfigPathInfo.IsDir() {
nodeConfigPath = ""
}
return
}
func LoadConfig(path string) (config config, err error) {
var (
source []byte
)
if path == "" {
} else if source, err = ioutil.ReadFile(path); err != nil {
} else if err = yaml.Unmarshal(source, &config); err != nil {
}
return
}
func main() {
_, err := app.Parse(os.Args[1:])
var (
verbose = *verbosePtr
clear = *clearPtr
nodeName = *nodeNamePtr
bindIfName = *bindIfNamePtr
configFileName = *configFileNamePtr
nspawnPath string
progress = -1
)
defer func() {
if r := recover(); r != nil {
fmt.Fprintln(os.Stderr, r)
os.Exit(progress)
}
}()
if err != nil {
panic(err)
}
progress = 1
containerHomePath, err := filepath.Abs(*containerHomePtr)
if err != nil {
panic(err)
}
progress++
nodePath, nodeConfigPath, err := checkNodeBasicDirectory(containerHomePath, nodeName, configFileName)
if err != nil {
panic(err)
} else if verbose {
var nodeConfigPathDesc = "not used"
if nodeConfigPath != "" {
nodeConfigPathDesc = nodeConfigPath
}
fmt.Printf(`sane container environment:
- container home : %s
- node home : %s
- node config : %s
`, containerHomePath, nodePath, nodeConfigPathDesc)
}
config, err := LoadConfig(nodeConfigPath)
if err != nil {
panic(err)
} else if verbose {
fmt.Printf(config.String())
}
progress++
if nspawnPathPtr != nil && *nspawnPathPtr != "" {
nspawnPath = *nspawnPathPtr
} else if nspawnPathData, err := exec.LookPath("systemd-nspawn"); err != nil {
panic(errs.Wrap(err, "cannot find systemd-nspawn"))
} else {
nspawnPath = nspawnPathData
if verbose {
fmt.Printf("systemd-nspawn found : %s\n", nspawnPath)
}
}
progress++
err = checkInterfaceValid(bindIfName)
if err != nil {
panic(errs.Wrap(err, "error in checkInterfaceValid"))
}
progress++
nodeAddr, err := getIP(nodeName)
if err != nil {
panic(errs.Wrap(err, "error in getIP"))
}
progress++
gw, cidr, linkname, err := getOutboundIPInfo(verbose)
if err != nil {
panic(errs.Wrap(err, "error in getOutboundIPInfo"))
}
if verbose {
fmt.Println("network configuration:")
fmt.Printf(" - bind interface{%s} : valid\n", bindIfName)
fmt.Printf(" - default routing info : ip %s/%d link %s\n", gw.String(), cidr, linkname)
fmt.Printf(" - node{%s} IP address : %s\n", nodeName, nodeAddr.String())
}
option := []string{
"--quiet",
"--boot",
"--link-journal=try-host",
"--notify-ready=true"}
option = append(option, fmt.Sprintf("--network-ipvlan=%s", bindIfName))
option = append(option, fmt.Sprintf("--setenv=IP_ADDR=%s/%d", nodeAddr.String(), cidr))
option = append(option, fmt.Sprintf("--setenv=GATEWAY_ADDR=%s", gw.String()))
option = append(option, fmt.Sprintf("--machine=%s", nodeName))
option = append(option, fmt.Sprintf("--directory=%s", filepath.Join(nodePath, "merge")))
for _, mountPoint := range config.MoundPoint {
if mountOption, err := mountPoint.Option(nodePath); err != nil {
panic(errs.Wrap(err, "error in mountOption"))
} else {
option = append(option, mountOption)
}
}
var env []string
if clear {
env = []string{}
} else {
env = os.Environ()
if verbose {
fmt.Println("os environment:")
for _, segment := range env {
fmt.Println(" ", segment)
}
}
}
if verbose {
fmt.Println("systemd-nspawn spawning:")
fmt.Println(nspawnPath, " \\")
for _, segment := range option {
fmt.Println(" ", segment, " \\")
}
fmt.Println(" ;")
fmt.Println("bye!")
}
if err := syscall.Exec(nspawnPath, option, env); err != nil {
panic(errs.Wrap(err, "error in systemd-nspawn execution"))
}
}