Merge pull request #214 from vcaputo/socketactivation

stage1: generate socket unit for socket-activated ports
This commit is contained in:
Vito Caputo
2014-12-05 11:27:12 -08:00
2 changed files with 78 additions and 33 deletions
+50 -11
View File
@@ -63,7 +63,7 @@ func LoadContainer(root string) (*Container, error) {
return c, nil
}
// appToSystemd transforms the provided app manifest into a systemd service unit
// appToSystemd transforms the provided app manifest into systemd units
func (c *Container) appToSystemd(am *schema.AppManifest, id types.Hash) error {
name := am.Name.String()
execStart := strings.Join(am.Exec, " ")
@@ -101,17 +101,62 @@ func (c *Container) appToSystemd(am *schema.AppManifest, id types.Hash) error {
opts = append(opts, &unit.UnitOption{"Service", "Environment", ee})
}
file, err := os.OpenFile(ServiceFilePath(c.Root, id), os.O_WRONLY|os.O_CREATE, 0644)
saPorts := []types.Port{}
for _, p := range am.Ports {
if p.SocketActivated {
saPorts = append(saPorts, p)
}
}
if len(saPorts) > 0 {
sockopts := []*unit.UnitOption{
&unit.UnitOption{"Unit", "Description", name + " socket-activated ports"},
&unit.UnitOption{"Unit", "DefaultDependencies", "false"},
&unit.UnitOption{"Socket", "BindIPv6Only", "both"},
&unit.UnitOption{"Socket", "Service", ServiceUnitName(id)},
}
for _, sap := range saPorts {
var proto string
switch sap.Protocol {
case "tcp":
proto = "ListenStream"
case "udp":
proto = "ListenDatagram"
default:
return fmt.Errorf("unrecognized protocol: %v", sap.Protocol)
}
sockopts = append(sockopts, &unit.UnitOption{"Socket", proto, fmt.Sprintf("%v", sap.Port)})
}
file, err := os.OpenFile(SocketUnitPath(c.Root, id), os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return fmt.Errorf("failed to create socket file: %v", err)
}
defer file.Close()
if _, err = io.Copy(file, unit.Serialize(sockopts)); err != nil {
return fmt.Errorf("failed to write socket unit file: %v", err)
}
if err = os.Symlink(path.Join("..", SocketUnitName(id)), SocketWantPath(c.Root, id)); err != nil {
return fmt.Errorf("failed to link socket want: %v", err)
}
opts = append(opts, &unit.UnitOption{"Unit", "Requires", SocketUnitName(id)})
}
file, err := os.OpenFile(ServiceUnitPath(c.Root, id), os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return fmt.Errorf("failed to create service file: %v", err)
return fmt.Errorf("failed to create service unit file: %v", err)
}
defer file.Close()
if _, err = io.Copy(file, unit.Serialize(opts)); err != nil {
return fmt.Errorf("failed to write service file: %v", err)
return fmt.Errorf("failed to write service unit file: %v", err)
}
if err = os.Symlink(path.Join("..", ServiceName(id)), WantLinkPath(c.Root, id)); err != nil {
if err = os.Symlink(path.Join("..", ServiceUnitName(id)), ServiceWantPath(c.Root, id)); err != nil {
return fmt.Errorf("failed to link service want: %v", err)
}
@@ -121,12 +166,6 @@ func (c *Container) appToSystemd(am *schema.AppManifest, id types.Hash) error {
// ContainerToSystemd creates the appropriate systemd service unit files for
// all the constituent apps of the Container
func (c *Container) ContainerToSystemd() error {
if err := os.MkdirAll(ServicesPath(c.Root), 0640); err != nil {
return fmt.Errorf("failed to create services directory: %v", err)
}
if err := os.MkdirAll(WantsPath(c.Root), 0640); err != nil {
return fmt.Errorf("failed to create wants directory: %v", err)
}
for _, am := range c.Apps {
a := c.Manifest.Apps.Get(am.Name)
if a == nil {
+28 -22
View File
@@ -8,34 +8,40 @@ import (
)
const (
servicesDir = path.Stage1Dir + "/usr/lib/systemd/system"
wantsDir = servicesDir + "/default.target.wants"
unitsDir = path.Stage1Dir + "/usr/lib/systemd/system"
defaultWantsDir = unitsDir + "/default.target.wants"
socketsWantsDir = unitsDir + "/sockets.target.wants"
)
// ServiceName returns a sanitized (escaped) systemd service name
// for the given imageID
func ServiceName(imageID types.Hash) string {
// ServiceUnitName returns a systemd service unit name for the given imageID
func ServiceUnitName(imageID types.Hash) string {
return imageID.String() + ".service"
}
// WantsPath returns the systemd "wants" directory in root
func WantsPath(root string) string {
return filepath.Join(root, wantsDir)
// ServiceUnitPath returns the path to the systemd service file for the given
// imageID
func ServiceUnitPath(root string, imageID types.Hash) string {
return filepath.Join(root, unitsDir, ServiceUnitName(imageID))
}
// ServicesPath returns the systemd "services" directory in root
func ServicesPath(root string) string {
return filepath.Join(root, servicesDir)
}
// ServiceFilePath returns the path to the systemd service file
// path for the given imageID
func ServiceFilePath(root string, imageID types.Hash) string {
return filepath.Join(root, servicesDir, ServiceName(imageID))
}
// WantLinkPath returns the systemd "want" symlink path for the
// ServiceWantPath returns the systemd default.target want symlink path for the
// given imageID
func WantLinkPath(root string, imageID types.Hash) string {
return filepath.Join(root, wantsDir, ServiceName(imageID))
func ServiceWantPath(root string, imageID types.Hash) string {
return filepath.Join(filepath.Join(root, defaultWantsDir), ServiceUnitName(imageID))
}
// SocketUnitName returns a systemd socket unit name for the given imageID
func SocketUnitName(imageID types.Hash) string {
return imageID.String() + ".socket"
}
// SocketUnitPath returns the path to the systemd socket file for the given imageID
func SocketUnitPath(root string, imageID types.Hash) string {
return filepath.Join(root, unitsDir, SocketUnitName(imageID))
}
// SocketWantPath returns the systemd sockets.target.wants symlink path for the
// given imageID
func SocketWantPath(root string, imageID types.Hash) string {
return filepath.Join(filepath.Join(root, socketsWantsDir), SocketUnitName(imageID))
}