Skip to content

Commit eb51bda

Browse files
committed
Support deploying from a flake
Example usage: $ nixops create -d hydra-ec2-demo --flake github:edolstra/hydra-ec2-demo $ nixops deploy to get an EC2 instance running Hydra. Evaluation currently doesn't use pure mode because we need access to NixOps's NixOS modules, and to physical.nix.
1 parent 4c7acbb commit eb51bda

File tree

4 files changed

+72
-12
lines changed

4 files changed

+72
-12
lines changed

nix/eval-machine-info.nix

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
{ system ? builtins.currentSystem
22
, networkExprs
3+
, flakeUri ? null
34
, checkConfigurationOptions ? true
45
, uuid
56
, deploymentName
67
, args
78
, pluginNixExprs
89
}:
910

11+
# FIXME: don't rely on <nixpkgs>.
1012
with import <nixpkgs> { inherit system; };
1113
with lib;
1214

@@ -31,14 +33,25 @@ rec {
3133
startSet = map exprToKey networkExprs;
3234
operator = { key }: map exprToKey ((getNetworkFromExpr key).require or []);
3335
};
34-
in map ({ key }: getNetworkFromExpr key) networkExprClosure;
36+
in
37+
map ({ key }: getNetworkFromExpr key) networkExprClosure
38+
++ optional (flakeUri != null)
39+
((call (builtins.getFlake flakeUri).outputs.nixopsConfigurations.default) // { _file = "<${flakeUri}>"; });
3540

3641
call = x: if builtins.isFunction x then x args else x;
3742

3843
network = zipAttrs networks;
3944

4045
defaults = network.defaults or [];
4146

47+
evalConfig =
48+
if flakeUri != null
49+
then
50+
if network ? nixpkgs
51+
then (head (network.nixpkgs)).lib.nixosSystem
52+
else throw "NixOps network must have a 'nixpkgs' attribute"
53+
else import "<nixpkgs/nixos/lib/eval-config.nix>";
54+
4255
# Compute the definitions of the machines.
4356
nodes =
4457
listToAttrs (map (machineName:
@@ -52,14 +65,14 @@ rec {
5265
networks;
5366
in
5467
{ name = machineName;
55-
value = import <nixpkgs/nixos/lib/eval-config.nix> {
68+
value = evalConfig {
5669
modules =
5770
modules ++
5871
defaults ++
5972
[ deploymentInfoModule ] ++
6073
[ { key = "nixops-stuff";
6174
# Make NixOps's deployment.* options available.
62-
imports = [ ./options.nix ./resource.nix pluginOptions ];
75+
imports = [ ./options.nix ./resource.nix pluginOptions ];
6376
# Provide a default hostname and deployment target equal
6477
# to the attribute name of the machine in the model.
6578
networking.hostName = mkOverride 900 machineName;
@@ -70,7 +83,7 @@ rec {
7083
extraArgs = { inherit nodes resources uuid deploymentName; name = machineName; };
7184
};
7285
}
73-
) (attrNames (removeAttrs network [ "network" "defaults" "resources" "require" "_file" ])));
86+
) (attrNames (removeAttrs network [ "network" "defaults" "resources" "require" "nixpkgs" "_file" ])));
7487

7588
# Compute the definitions of the non-machine resources.
7689
resourcesByType = zipAttrs (network.resources or []);

nixops/backends/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class MachineState(nixops.resources.ResourceState):
5858
# this machine.
5959
cur_toplevel = nixops.util.attr_property("toplevel", None)
6060

61+
# Immutable flake URI from which this machine was built.
62+
cur_flake_uri = nixops.util.attr_property("curFlakeUri", None)
63+
6164
# Time (in Unix epoch) the instance was started, if known.
6265
start_time = nixops.util.attr_property("startTime", None, int)
6366

nixops/deployment.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class Deployment(object):
4747
name = nixops.util.attr_property("name", None)
4848
nix_exprs = nixops.util.attr_property("nixExprs", [], 'json')
4949
nix_path = nixops.util.attr_property("nixPath", [], 'json')
50+
flake_uri = nixops.util.attr_property("flakeUri", None)
51+
cur_flake_uri = nixops.util.attr_property("curFlakeUri", None)
5052
args = nixops.util.attr_property("args", {}, 'json')
5153
description = nixops.util.attr_property("description", default_description)
5254
configs_path = nixops.util.attr_property("configsPath", None)
@@ -88,6 +90,8 @@ def __init__(self, statefile, uuid, log_file=sys.stderr):
8890

8991
self.definitions = None
9092

93+
self._cur_flake_uri = None
94+
9195

9296
@property
9397
def tempdir(self):
@@ -260,6 +264,16 @@ def _nix_path_flags(self):
260264
return flags
261265

262266

267+
def _get_cur_flake_uri(self):
268+
assert self.flake_uri is not None
269+
if self._cur_flake_uri is None:
270+
out = json.loads(subprocess.check_output(
271+
["nix", "flake", "info", "--json", "--", self.flake_uri],
272+
stderr=self.logger.log_file))
273+
self._cur_flake_uri = out['uri']
274+
return self._cur_flake_uri
275+
276+
263277
def _eval_flags(self, exprs):
264278
flags = self._nix_path_flags()
265279
args = {key: RawValue(val) for key, val in self.args.iteritems()}
@@ -275,7 +289,14 @@ def _eval_flags(self, exprs):
275289
"--argstr", "uuid", self.uuid,
276290
"--argstr", "deploymentName", self.name if self.name else "",
277291
"--arg", "pluginNixExprs", py2nix(extraexprs),
278-
"<nixops/eval-machine-info.nix>"])
292+
self.expr_path + "/eval-machine-info.nix"])
293+
294+
if self.flake_uri is not None:
295+
flags.extend(
296+
[#"--pure-eval", # FIXME
297+
"--argstr", "flakeUri", self._get_cur_flake_uri(),
298+
"--allowed-uris", self.expr_path])
299+
279300
return flags
280301

281302

@@ -762,6 +783,7 @@ def worker(m):
762783
# configuration.
763784
m.cur_configs_path = configs_path
764785
m.cur_toplevel = m.new_toplevel
786+
m.cur_flake_uri = self._get_cur_flake_uri() if self.flake_uri is not None else None
765787

766788
except Exception as e:
767789
# This thread shouldn't throw an exception because
@@ -1023,6 +1045,8 @@ def worker(r):
10231045

10241046
if dry_activate: return
10251047

1048+
self.cur_flake_uri = self._get_cur_flake_uri() if self.flake_uri is not None else None
1049+
10261050
# Trigger cleanup of resources, e.g. disks that need to be detached etc. Needs to be
10271051
# done after activation to make sure they are not in use anymore.
10281052
def cleanup_worker(r):
@@ -1101,6 +1125,8 @@ def _rollback(self, generation, include=[], exclude=[], check=False,
11011125
sync=sync, always_activate=True,
11021126
dry_activate=False, max_concurrent_activate=max_concurrent_activate)
11031127

1128+
self.cur_flake_uri = None
1129+
11041130

11051131
def rollback(self, **kwargs):
11061132
with self._get_deployment_lock():

nixops/script_defs.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,23 @@ def set_name(depl, name):
110110
def modify_deployment(args, depl):
111111
nix_exprs = args.nix_exprs
112112
templates = args.templates or []
113-
for i in templates: nix_exprs.append("<nixops/templates/{0}.nix>".format(i))
114-
if len(nix_exprs) == 0:
115-
raise Exception("you must specify the path to a Nix expression and/or use ‘-t’")
116-
depl.nix_exprs = [os.path.abspath(x) if x[0:1] != '<' else x for x in nix_exprs]
117-
depl.nix_path = [nixops.util.abs_nix_path(x) for x in sum(args.nix_path or [], [])]
113+
if args.flake is None:
114+
for i in templates: nix_exprs.append("<nixops/templates/{0}.nix>".format(i))
115+
if len(nix_exprs) == 0:
116+
raise Exception("you must specify the path to a Nix expression and/or use ‘-t’")
117+
depl.nix_exprs = [os.path.abspath(x) if x[0:1] != '<' else x for x in nix_exprs]
118+
depl.nix_path = [nixops.util.abs_nix_path(x) for x in sum(args.nix_path or [], [])]
119+
else:
120+
if len(nix_exprs):
121+
raise Exception("you cannot specify a Nix expression in conjunction with '--flake'")
122+
if args.nix_path:
123+
raise Exception("you cannot specify a Nix search path ('-I') in conjunction with '--flake'")
124+
if len(templates) != 0:
125+
raise Exception("you cannot specify a template ('-t') in conjunction with '--flake'")
126+
# FIXME: should absolutize args.flake if it's a local path.
127+
depl.flake_uri = args.flake
128+
depl.nix_exprs = []
129+
depl.nix_path = []
118130

119131

120132
def op_create(args):
@@ -229,8 +241,13 @@ def name_to_key(name):
229241
print "Network name:", depl.name or "(none)"
230242
print "Network UUID:", depl.uuid
231243
print "Network description:", depl.description
232-
print "Nix expressions:", " ".join(depl.nix_exprs)
233-
if depl.nix_path != []: print "Nix path:", " ".join(map(lambda x: "-I " + x, depl.nix_path))
244+
if depl.flake_uri is None:
245+
print "Nix expressions:", " ".join(depl.nix_exprs)
246+
if depl.nix_path != []: print "Nix path:", " ".join(map(lambda x: "-I " + x, depl.nix_path))
247+
else:
248+
print "Flake URI:", depl.flake_uri
249+
if depl.cur_flake_uri is not None:
250+
print "Deployed flake URI:", depl.cur_flake_uri
234251
if depl.rollback_enabled: print "Nix profile:", depl.get_profile()
235252
if depl.args != {}: print "Nix arguments:", ", ".join([n + " = " + v for n, v in depl.args.iteritems()])
236253
print
@@ -777,6 +794,7 @@ def add_subparser(subparsers, name, help):
777794
def add_common_modify_options(subparser):
778795
subparser.add_argument('nix_exprs', nargs='*', metavar='NIX-FILE', help='Nix expression(s) defining the network')
779796
subparser.add_argument('--template', '-t', action="append", dest="templates", metavar='TEMPLATE', help='name of template to be used')
797+
subparser.add_argument('--flake', dest="flake", metavar='FLAKE_URI', help='URI of the flake that defines the network')
780798

781799

782800
def add_common_deployment_options(subparser):

0 commit comments

Comments
 (0)