Skip to content

Commit

Permalink
DigtalOcean backend: IPv6 support. (#633)
Browse files Browse the repository at this point in the history
  • Loading branch information
Drew Hess authored and domenkozar committed Mar 29, 2017
1 parent 78e964a commit 55ceaa9
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 10 deletions.
5 changes: 3 additions & 2 deletions doc/manual/overview.xml
Expand Up @@ -1400,8 +1400,8 @@ and destruction.</para>

<para><xref linkend="ex-trivial-digital-ocean.nix" /> shows how to run
a <literal>512m</literal> digital ocean instance in the
<literal>ams2</literal> region. We only support droplet creation and
destruction at the moment.
<literal>ams2</literal> region, with IPv6 support enabled. We only
support droplet creation and destruction at the moment.
</para>

<para>Note that we rely on a ssh key resource with the hard-coded name
Expand All @@ -1419,6 +1419,7 @@ destruction at the moment.
services.openssh.enable = true;

deployment.targetEnv = "digitalOcean";
deployment.digitalOcean.enableIpv6 = true;
deployment.digitalOcean.region = "ams2";
deployment.digitalOcean.size = "512mb";
};
Expand Down
1 change: 1 addition & 0 deletions examples/trivial-digital-ocean.nix
Expand Up @@ -6,6 +6,7 @@
services.openssh.enable = true;

deployment.targetEnv = "digitalOcean";
deployment.digitalOcean.enableIpv6 = true;
deployment.digitalOcean.region = "ams2";
deployment.digitalOcean.size = "512mb";
};
Expand Down
8 changes: 8 additions & 0 deletions nix/digital-ocean.nix
Expand Up @@ -41,6 +41,14 @@ in
https://developers.digitalocean.com/documentation/v2/#list-all-sizes
'';
};

deployment.digitalOcean.enableIpv6 = mkOption {
default = false;
type = types.bool;
description = ''
Whether to enable IPv6 support on the droplet.
'';
};
};

config = mkIf (config.deployment.targetEnv == "digitalOcean") {
Expand Down
38 changes: 30 additions & 8 deletions nixops/backends/digital_ocean.py
Expand Up @@ -41,6 +41,7 @@ def __init__(self, xml, config):
self.auth_token = config["digitalOcean"]["authToken"]
self.region = config["digitalOcean"]["region"]
self.size = config["digitalOcean"]["size"]
self.enable_ipv6 = config["digitalOcean"]["enableIpv6"]

def show_type(self):
return "{0} [{1}]".format(self.get_type(), self.region)
Expand All @@ -55,6 +56,9 @@ def get_type(cls):
public_ipv4 = nixops.util.attr_property("publicIpv4", None)
default_gateway = nixops.util.attr_property("defaultGateway", None)
netmask = nixops.util.attr_property("netmask", None)
enable_ipv6 = nixops.util.attr_property("digitalOcean.enableIpv6", False, bool)
public_ipv6 = nixops.util.attr_property("publicIpv6", {}, 'json')
default_gateway6 = nixops.util.attr_property("defaultGateway6", None)
region = nixops.util.attr_property("digitalOcean.region", None)
size = nixops.util.attr_property("digitalOcean.size", None)
auth_token = nixops.util.attr_property("digitalOcean.authToken", None)
Expand All @@ -77,17 +81,21 @@ def get_ssh_flags(self, *args, **kwargs):
]

def get_physical_spec(self):
prefixLength = bin(int(socket.inet_aton(self.netmask).encode('hex'), 16)).count('1')
def prefix_len(netmask):
return bin(int(socket.inet_aton(netmask).encode('hex'), 16)).count('1')
networking = {
'defaultGateway': self.default_gateway,
'nameservers': ['8.8.8.8'], # default provided by DO
('interfaces', 'enp0s3', 'ip4'): [{"address": self.public_ipv4, 'prefixLength': prefix_len(self.netmask)}],
}
if self.public_ipv6:
networking[('interfaces', 'enp0s3', 'ip6')] = [{'address': self.public_ipv6['address'], 'prefixLength': self.public_ipv6['prefixLength']}]
if self.default_gateway6:
networking['defaultGateway6'] = self.default_gateway6

return Function("{ ... }", {
'imports': [ RawValue('<nixpkgs/nixos/modules/profiles/qemu-guest.nix>') ],
'networking': {
'defaultGateway': self.default_gateway,
'nameservers': ['8.8.8.8'], # default provided by DO
('interfaces', 'enp0s3'): {
'ip4': [{"address": self.public_ipv4, 'prefixLength': prefixLength}],
},
},
'networking': networking,
('boot', 'loader', 'grub', 'device'): 'nodev', # keep ubuntu bootloader?
('fileSystems', '/'): { 'device': '/dev/vda1', 'fsType': 'ext4'},
('users', 'extraUsers', 'root', 'openssh', 'authorizedKeys', 'keys'): [self.depl.active_resources.get('ssh-key').public_key],
Expand Down Expand Up @@ -131,6 +139,7 @@ def create(self, defn, check, allow_reboot, allow_recreate):
token=self.get_auth_token(),
name=self.name,
region=defn.region,
ipv6=defn.enable_ipv6,
ssh_keys=[ssh_key.public_key],
image='ubuntu-16-04-x64', # only for lustration
size_slug=defn.size,
Expand Down Expand Up @@ -162,6 +171,19 @@ def create(self, defn, check, allow_reboot, allow_recreate):
self.default_gateway = droplet.networks['v4'][0]['gateway']
self.netmask = droplet.networks['v4'][0]['netmask']

first_ipv6 = {}
first_gw6 = None
if 'v6' in droplet.networks:
public_ipv6_networks = [n for n in droplet.networks['v6'] if n['type'] == 'public']
if len(public_ipv6_networks) > 0:
# The DigitalOcean API does not expose an explicit
# default interface or gateway, so assume this is it.
first_ipv6['address'] = public_ipv6_networks[0]['ip_address']
first_ipv6['prefixLength'] = public_ipv6_networks[0]['netmask']
first_gw6 = public_ipv6_networks[0]['gateway']
self.public_ipv6 = first_ipv6
self.default_gateway6 = first_gw6

# run modified nixos-infect
# - no reboot
# - predictable network interface naming (enp0s3 etc)
Expand Down

0 comments on commit 55ceaa9

Please sign in to comment.