Skip to content

Static IP, IPv6 and MAC addresses with Podman >=4.0.0 #359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions api/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,19 @@ type ContainerCgroupConfig struct {
CgroupParent string `json:"cgroup_parent,omitempty"`
}

// PerNetworkOptions allows you to set options per network the container is
// attached to.
type PerNetworkOptions struct {
// Aliases contains a list of names which the dns server should resolve to this container. Should only be set when DNSEnabled is true on the Network. If aliases are set but there is no dns support for this network the network interface implementation should ignore this and NOT error. Optional.
Aliases []string `json:"aliases,omitempty"`
// InterfaceName for this container. Required in the backend. Optional in the frontend. Will be filled with ethX (where X is a integer) when empty.
InterfaceName string `json:"interface_name,omitempty"`
// StaticIPs for this container. Optional.
StaticIPs []*net.IP `json:"static_ips,omitempty"`
// StaticMac for this container. Optional.
StaticMac *net.HardwareAddr `json:"static_mac,omitempty"`
}

// ContainerNetworkConfig contains information on a container's network
// configuration.
type ContainerNetworkConfig struct {
Expand Down Expand Up @@ -407,6 +420,13 @@ type ContainerNetworkConfig struct {
// Podman, and instead sourced from the image.
// Conflicts with HostAdd.
UseImageHosts bool `json:"use_image_hosts,omitempty"`
// Map of networks names or ids that the container should join. You can
// request additional settings for each network, you can set network
// aliases, static ips, static mac address and the network interface name
// for this container on the specific network. If the map is empty and the
// bridge network mode is set the container will be joined to the default
// network.
Networks map[string]PerNetworkOptions `json:"networks,omitempty"`
}

// ContainerResourceConfig contains information on container resource limits.
Expand Down
14 changes: 11 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,13 @@ var (
hclspec.NewAttr("image_pull_timeout", "string", false),
hclspec.NewLiteral(`"5m"`),
),
"init": hclspec.NewAttr("init", "bool", false),
"init_path": hclspec.NewAttr("init_path", "string", false),
"labels": hclspec.NewAttr("labels", "list(map(string))", false),
"init": hclspec.NewAttr("init", "bool", false),
"init_path": hclspec.NewAttr("init_path", "string", false),
"ipv4_address": hclspec.NewAttr("ipv4_address", "string", false),
"ipv6_address": hclspec.NewAttr("ipv6_address", "string", false),
"static_ips": hclspec.NewAttr("static_ips", "list(string)", false),
"static_macs": hclspec.NewAttr("static_macs", "list(string)", false),
"labels": hclspec.NewAttr("labels", "list(map(string))", false),
"logging": hclspec.NewBlock("logging", false, hclspec.NewObject(map[string]*hclspec.Spec{
"driver": hclspec.NewAttr("driver", "string", false),
"options": hclspec.NewAttr("options", "list(map(string))", false),
Expand Down Expand Up @@ -203,6 +207,10 @@ type TaskConfig struct {
Hostname string `codec:"hostname"`
Image string `codec:"image"`
ImagePullTimeout string `codec:"image_pull_timeout"`
IPv4Address string `codec:"ipv4_address"`
IPv6Address string `codec:"ipv6_address"`
StaticIPs []string `codec:"static_ips"`
StaticMAC string `codec:"static_mac"`
InitPath string `codec:"init_path"`
Logging TaskLoggingConfig `codec:"logging"`
Labels hclutils.MapStrStr `codec:"labels"`
Expand Down
76 changes: 76 additions & 0 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/containers/image/v5/pkg/shortnames"
"github.com/containers/image/v5/types"
"github.com/hashicorp/go-hclog"
version2 "github.com/hashicorp/go-version"
"github.com/hashicorp/nomad-driver-podman/api"
"github.com/hashicorp/nomad-driver-podman/registry"
"github.com/hashicorp/nomad-driver-podman/version"
Expand Down Expand Up @@ -688,6 +689,81 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
}
}

// Process static IP and MAC configuration. IPv4Address and IPv6Address are
// provided for compatibility with Docker, but the podman API v4 exposes a
// list of static addresses too. Add all three options to the API call for
// the default network
if driverConfig.StaticMAC != "" || driverConfig.IPv4Address != "" || driverConfig.IPv6Address != "" || len(driverConfig.StaticIPs) > 0 {
apiVersion, _ := d.podman.Ping(d.ctx)
versionValue, _ := version2.NewVersion(apiVersion)
versionCheck, _ := version2.NewConstraint(">=4.0.0")

if versionCheck.Check(versionValue) {
// Podman API v4 uses PerNetworkOptions. For now, we'll just use the
// default network.

netOpts := api.PerNetworkOptions{}
netOpts.StaticIPs = []*net.IP{}

if driverConfig.IPv4Address != "" {
parsedIP := net.ParseIP(driverConfig.IPv4Address)
if parsedIP != nil {
netOpts.StaticIPs = append(netOpts.StaticIPs, &parsedIP)
}
}

if driverConfig.IPv6Address != "" {
parsedIPv6 := net.ParseIP(driverConfig.IPv6Address)
if parsedIPv6 != nil {
netOpts.StaticIPs = append(netOpts.StaticIPs, &parsedIPv6)
}
}

if len(driverConfig.StaticIPs) > 0 {
for _, ip := range driverConfig.StaticIPs {
parsedIP := net.ParseIP(ip)
if parsedIP != nil {
netOpts.StaticIPs = append(netOpts.StaticIPs, &parsedIP)
}
}
}

// Process Static MAC configuration
if driverConfig.StaticMAC != "" {
parsedMAC, macErr := net.ParseMAC(driverConfig.StaticMAC)
if macErr == nil && parsedMAC != nil {
netOpts.StaticMac = &parsedMAC
}
}

createOpts.Networks = map[string]api.PerNetworkOptions{"default": netOpts}
} else {
// Before version 4, there were StaticIP, StaticIPv6 and StaticMAC properties

if driverConfig.IPv4Address != "" {
parsedIP := net.ParseIP(driverConfig.IPv4Address)
if parsedIP != nil {
createOpts.ContainerNetworkConfig.StaticIP = &parsedIP
}
}

if driverConfig.IPv6Address != "" {
parsedIPv6 := net.ParseIP(driverConfig.IPv6Address)
if parsedIPv6 != nil {
createOpts.ContainerNetworkConfig.StaticIPv6 = &parsedIPv6
}
}

// Process Static MAC configuration
if driverConfig.StaticMAC != "" {
parsedMAC, macErr := net.ParseMAC(driverConfig.StaticMAC)
if macErr == nil && parsedMAC != nil {
createOpts.ContainerNetworkConfig.StaticMAC = &parsedMAC
}
}
}
}

// carefully add extra hosts (--add-host)
if extraHostsErr := setExtraHosts(driverConfig.ExtraHosts, &createOpts); extraHostsErr != nil {
return nil, nil, extraHostsErr
Expand Down