Skip to content

Commit

Permalink
add support for network_mode, logging, and devices
Browse files Browse the repository at this point in the history
  • Loading branch information
aksiksi committed Nov 5, 2023
1 parent dd04259 commit cb1bc9f
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 60 deletions.
143 changes: 98 additions & 45 deletions compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,44 +55,6 @@ func portConfigsToPortStrings(portConfigs []types.ServicePortConfig) []string {
return ports
}

type Generator struct {
Project *Project
Runtime ContainerRuntime
Paths []string
EnvFiles []string
AutoStart bool
EnvFilesOnly bool

composeProject *types.Project
}

func (g *Generator) Run(ctx context.Context) (*NixContainerConfig, error) {
env, err := ReadEnvFiles(g.EnvFiles, !g.EnvFilesOnly)
if err != nil {
return nil, err
}
composeProject, err := loader.LoadWithContext(ctx, types.ConfigDetails{
ConfigFiles: types.ToConfigFiles(g.Paths),
Environment: types.NewMapping(env),
})
if err != nil {
return nil, err
}
g.composeProject = composeProject

containers := g.buildNixContainers()
networks := g.buildNixNetworks(containers)
volumes := g.buildNixVolumes(containers)

return &NixContainerConfig{
Project: g.Project,
Runtime: g.Runtime,
Containers: containers,
Networks: networks,
Volumes: volumes,
}, nil
}

func parseRestartPolicyAndSystemdLabels(service *types.ServiceConfig) (*NixContainerSystemdConfig, error) {
p := &NixContainerSystemdConfig{
Service: make(map[string]any),
Expand Down Expand Up @@ -178,6 +140,61 @@ func parseRestartPolicyAndSystemdLabels(service *types.ServiceConfig) (*NixConta
return p, nil
}

type Generator struct {
Project *Project
Runtime ContainerRuntime
Paths []string
EnvFiles []string
AutoStart bool
EnvFilesOnly bool

composeProject *types.Project
}

func (g *Generator) Run(ctx context.Context) (*NixContainerConfig, error) {
env, err := ReadEnvFiles(g.EnvFiles, !g.EnvFilesOnly)
if err != nil {
return nil, err
}
composeProject, err := loader.LoadWithContext(ctx, types.ConfigDetails{
ConfigFiles: types.ToConfigFiles(g.Paths),
Environment: types.NewMapping(env),
})
if err != nil {
return nil, err
}
g.composeProject = composeProject

containers := g.buildNixContainers()
networks := g.buildNixNetworks(containers)
volumes := g.buildNixVolumes(containers)

// Post-process any Compose settings that require full state.
g.postProcessContainers(containers)

return &NixContainerConfig{
Project: g.Project,
Runtime: g.Runtime,
Containers: containers,
Networks: networks,
Volumes: volumes,
}, nil
}

func (g *Generator) postProcessContainers(containers []NixContainer) {
serviceToContainer := make(map[string]*NixContainer)
for _, c := range containers {
serviceToContainer[c.service.Name] = &c
}
for _, c := range containers {
if networkMode := c.service.NetworkMode; strings.HasPrefix(networkMode, "service:") {
targetService := strings.Split(networkMode, ":")[1]
targetContainerName := serviceToContainer[targetService].Name
c.ExtraOptions = append(c.ExtraOptions, "--network=container:"+targetContainerName)
}
}
}

func (g *Generator) buildNixContainer(service types.ServiceConfig) NixContainer {
dependsOn := service.GetDependencies()
if g.Project != nil {
Expand Down Expand Up @@ -215,6 +232,7 @@ func (g *Generator) buildNixContainer(service types.ServiceConfig) NixContainer
SystemdConfig: systemdConfig,
DependsOn: dependsOn,
AutoStart: g.AutoStart,
service: &service,
}
slices.Sort(c.Networks)

Expand All @@ -231,17 +249,52 @@ func (g *Generator) buildNixContainer(service types.ServiceConfig) NixContainer
c.ExtraOptions = append(c.ExtraOptions, fmt.Sprintf("--network-alias=%s", service.Name))
}

// TODO(aksiksi): Handle the service's "network_mode"
// We can only parse the network mode at this point if it points to host or container.
// If it points to a service, we'll need to do a scan when we've finished parsing all
// containers.
// Compose: https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode
// Podman: https://docs.podman.io/en/latest/markdown/podman-run.1.html#network-mode-net

for _, v := range service.Volumes {
c.Volumes[v.Source] = v.String()
}

// https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode
// https://docs.podman.io/en/latest/markdown/podman-run.1.html#network-mode-net
switch networkMode := strings.TrimSpace(service.NetworkMode); {
case networkMode == "host":
c.ExtraOptions = append(c.ExtraOptions, "--network=host")
case strings.HasPrefix(networkMode, "container:"):
// container:[name] mode is supported by both Docker and Podman.
c.ExtraOptions = append(c.ExtraOptions, networkMode)
}

for _, ip := range service.DNS {
c.ExtraOptions = append(c.ExtraOptions, "--dns="+ip)
}

// https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
if service.Privileged {
c.ExtraOptions = append(c.ExtraOptions, "--privileged")
}
for _, cap := range service.CapAdd {
c.ExtraOptions = append(c.ExtraOptions, "--cap-add="+cap)
}
for _, cap := range service.CapDrop {
c.ExtraOptions = append(c.ExtraOptions, "--cap-drop="+cap)
}
for _, device := range service.Devices {
c.ExtraOptions = append(c.ExtraOptions, "--device="+device)
}

// https://docs.docker.com/config/containers/logging/configure/
// https://docs.podman.io/en/latest/markdown/podman-run.1.html#log-driver-driver
if service.LogDriver != "" {
c.ExtraOptions = append(c.ExtraOptions, "--log-driver="+service.LogDriver)
c.ExtraOptions = append(c.ExtraOptions, mapToRepeatedFlag("--log-opt", service.LogOpt)...)
}
if logging := service.Logging; logging != nil {
// https://docs.docker.com/compose/compose-file/compose-file-v3/#logging
if logging.Driver != "" {
c.ExtraOptions = append(c.ExtraOptions, "--log-driver="+logging.Driver)
c.ExtraOptions = append(c.ExtraOptions, mapToRepeatedFlag("--log-opt", logging.Options)...)
}
}

return c
}

Expand Down
5 changes: 5 additions & 0 deletions nixose.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strings"
"text/template"

"github.com/compose-spec/compose-go/types"
)

const DefaultProjectSeparator = "-"
Expand Down Expand Up @@ -101,6 +103,9 @@ type NixContainer struct {
SystemdConfig *NixContainerSystemdConfig
User string
AutoStart bool

// Original Docker Compose service.
service *types.ServiceConfig
}

type NixContainerConfig struct {
Expand Down
15 changes: 13 additions & 2 deletions nixose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nixose

import (
"context"
"flag"
"fmt"
"os"
"path"
Expand All @@ -10,6 +11,8 @@ import (
"github.com/google/go-cmp/cmp"
)

var update = flag.Bool("update", false, "update golden files")

func getPaths(t *testing.T) (string, string, string) {
outFileName := fmt.Sprintf("%s_out.nix", t.Name())
composePath := path.Join("testdata", "docker-compose.yml")
Expand All @@ -35,7 +38,11 @@ func TestDocker(t *testing.T) {
t.Fatal(err)
}
got, want := c.String(), string(wantOutput)
if diff := cmp.Diff(want, got); diff != "" {
if *update {
if err := os.WriteFile(outFilePath, []byte(got), 0644); err != nil {
t.Fatal(err)
}
} else if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("output diff: %s\n", diff)
}
}
Expand All @@ -57,7 +64,11 @@ func TestPodman(t *testing.T) {
t.Fatal(err)
}
got, want := c.String(), string(wantOutput)
if diff := cmp.Diff(want, got); diff != "" {
if *update {
if err := os.WriteFile(outFilePath, []byte(got), 0644); err != nil {
t.Fatal(err)
}
} else if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("output diff: %s\n", diff)
}
}
20 changes: 9 additions & 11 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ import (
var templateFS embed.FS
var nixTemplates = template.New("nix").Funcs(sprig.FuncMap()).Funcs(funcMap)

func labelMapToLabelFlags(l map[string]string) []string {
// https://docs.docker.com/engine/reference/commandline/run/#label
// https://docs.podman.io/en/latest/markdown/podman-run.1.html#label-l-key-value
labels := mapToKeyValArray(l)
for i, label := range labels {
labels[i] = fmt.Sprintf("--label=%s", label)
func mapToRepeatedFlag(flagName string, m map[string]string) []string {
arr := mapToKeyValArray(m)
for i, v := range arr {
arr[i] = fmt.Sprintf("%s=%s", flagName, v)
}
return labels
return arr
}

func execTemplate(t *template.Template) func(string, any) (string, error) {
Expand All @@ -45,8 +43,8 @@ func toNixValue(v any) any {
}

var funcMap template.FuncMap = template.FuncMap{
"derefInt": derefInt,
"labelMapToLabelFlags": labelMapToLabelFlags,
"mapToKeyValArray": mapToKeyValArray,
"toNixValue": toNixValue,
"derefInt": derefInt,
"mapToRepeatedFlag": mapToRepeatedFlag,
"mapToKeyValArray": mapToKeyValArray,
"toNixValue": toNixValue,
}
2 changes: 1 addition & 1 deletion templates/container.nix.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{- $name := .Project.With .Name -}}
{{- $runtime := .Runtime | printf "%s" -}}
{{- $labels := labelMapToLabelFlags .Labels -}}
{{- $labels := mapToRepeatedFlag "--label" .Labels -}}
virtualisation.oci-containers.containers."{{$name}}" = {
image = "{{.Image}}";

Expand Down
2 changes: 1 addition & 1 deletion templates/network.nix.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{- $name := .Project.With .Name -}}
{{- $runtime := .Runtime | printf "%s" -}}
{{- $labels := labelMapToLabelFlags .Labels -}}
{{- $labels := mapToRepeatedFlag "--label" .Labels -}}
systemd.services."create-{{$runtime}}-network-{{$name}}" = {
serviceConfig.Type = "oneshot";
path = [ pkgs.{{$runtime}} ];
Expand Down
26 changes: 26 additions & 0 deletions testdata/TestDocker_out.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
extraOptions = [
"--network=default"
"--network-alias=jellyseerr"
"--dns=1.1.1.1"
"--log-driver=json-file"
"--log-opt=compress=true"
"--log-opt=max-file=3"
"--log-opt=max-size=10m"
];
autoStart = false;
};
Expand All @@ -52,6 +57,10 @@
extraOptions = [
"--network=default"
"--network-alias=photoprism-mariadb"
"--log-driver=json-file"
"--log-opt=compress=true"
"--log-opt=max-file=3"
"--log-opt=max-size=10m"
];
user = "1000:1000";
autoStart = false;
Expand Down Expand Up @@ -85,6 +94,10 @@
extraOptions = [
"--network=default"
"--network-alias=sabnzbd"
"--log-driver=json-file"
"--log-opt=compress=true"
"--log-opt=max-file=3"
"--log-opt=max-size=10m"
];
autoStart = false;
};
Expand Down Expand Up @@ -119,6 +132,10 @@
extraOptions = [
"--network=default"
"--network-alias=traefik"
"--log-driver=json-file"
"--log-opt=compress=true"
"--log-opt=max-file=3"
"--log-opt=max-size=10m"
];
autoStart = false;
};
Expand Down Expand Up @@ -164,6 +181,15 @@
extraOptions = [
"--network=default"
"--network-alias=transmission"
"--dns=8.8.8.8"
"--dns=8.8.4.4"
"--privileged"
"--cap-add=NET_ADMIN"
"--device=/dev/net/tun:/dev/net/tun"
"--log-driver=json-file"
"--log-opt=compress=true"
"--log-opt=max-file=3"
"--log-opt=max-size=10m"
];
autoStart = false;
};
Expand Down
Loading

0 comments on commit cb1bc9f

Please sign in to comment.