From b245f86e67f846c191cee33f52e5dbaacdaed483 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 4 Dec 2014 15:25:12 -0800 Subject: [PATCH] stage1: generate a socket unit for socket-activated ports * Generate a systemd socket unit for socket-activated ports * Add socket-related path generators and cleanup rest * Remove unnecessary systemd unit path mkdirs, just trust the stage1 rootfs provides them. --- stage1/container.go | 61 +++++++++++++++++++++++++++++++++++++-------- stage1/path.go | 50 +++++++++++++++++++++---------------- 2 files changed, 78 insertions(+), 33 deletions(-) diff --git a/stage1/container.go b/stage1/container.go index d7d9d8c..11c84df 100644 --- a/stage1/container.go +++ b/stage1/container.go @@ -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 { diff --git a/stage1/path.go b/stage1/path.go index ad76a82..45fbc77 100644 --- a/stage1/path.go +++ b/stage1/path.go @@ -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)) }