nixops icon indicating copy to clipboard operation
nixops copied to clipboard

eval-machine-info.nix: ported to modules

Open pasqui23 opened this issue 3 years ago • 64 comments

Fixes https://github.com/NixOS/nixops/issues/1486 Done:

  • [X] Made requires an error.
  • [X] Implemented the nodes option.
  • [X] Made a fallback for the old way of writing nodes to the
  • [X] Added network.nodesExtraArgs To do:
  • [ ] Implemented all resources options
  • [x] Implemented all network options
  • [x] Tested it
  • [ ] Wire up the NixOS assertions and warnings
  • [ ] Declare lock and storage with plugin (assigned @roberth)

pasqui23 avatar Feb 20 '22 23:02 pasqui23

Heads up about the resources; just a merged a PR that touches that.

roberth avatar Feb 21 '22 20:02 roberth

what pr @roberth ?

pasqui23 avatar Feb 21 '22 21:02 pasqui23

https://github.com/NixOS/nixops/pull/1506

roberth avatar Feb 21 '22 22:02 roberth

So do I have to rebase on master?

pasqui23 avatar Feb 21 '22 22:02 pasqui23

Ok I did rebase on master

pasqui23 avatar Feb 21 '22 22:02 pasqui23

Ok I've tried and failed to build a network, it goes in infinite recursion while evaluating scrubOptionValues in info.machines

pasqui23 avatar Feb 22 '22 20:02 pasqui23

It still goes in infinite recursion, I need to know what nixops expects exactly from this file and what can be safely removed.

pasqui23 avatar Feb 22 '22 23:02 pasqui23

it goes in infinite recursion

Maybe the attrsOfs need to be lazyAttrsOfs. attrsOf can only return its attrset by inspecting the children for mkIf false values and filtering out those attributes. That makes it more strict than lazyAttrsOf, which will return the spine of the attribute set (attr names + thunks) without inspecting those thunks.

roberth avatar Feb 23 '22 11:02 roberth

I need to know what nixops expects exactly

I don't expect this to be documented anywhere.

roberth avatar Feb 23 '22 11:02 roberth

I need to know what nixops expects exactly

I don't expect this to be documented anywhere.

I don't need to be fully documented,only to tell me what the nixops cli and plugins take from the eval-machine-info.nix file right now. If, after merging this, you go and change that I would not care abut it.

pasqui23 avatar Feb 23 '22 12:02 pasqui23

it goes in infinite recursion

Maybe the attrsOfs need to be lazyAttrsOfs. attrsOf can only return its attrset by inspecting the children for mkIf false values and filtering out those attributes. That makes it more strict than lazyAttrsOf, which will return the spine of the attribute set (attr names + thunks) without inspecting those thunks.

Tried with lazyAttrsOf but it still gives me the same problems.

pasqui23 avatar Feb 23 '22 17:02 pasqui23

Ok it was an error on my part, I forgot network.nodesExtraArgs = { inherit inputs; }; in my config. 🤦🤦🤦🤦

pasqui23 avatar Feb 23 '22 18:02 pasqui23

Ok now the error I get is

; nixops list
error: The option `resources' is used but not defined.

Which I don't know where even to begin

pasqui23 avatar Feb 23 '22 18:02 pasqui23

; nixops list
error: The option `resources' is used but not defined.

Just add a default = { }; to the resources option.

It's some silly ux :/

roberth avatar Feb 23 '22 18:02 roberth

Ok latest error is definetly on the python side

; nixops list                       
def-matcher: command not found
Traceback (most recent call last):
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/bin/.nixops-wrapped", line 9, in <module>
    sys.exit(main())
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/__main__.py", line 56, in main
    args.op(args)
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/script_defs.py", line 200, in op_list_deployments
    depl.evaluate()
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 431, in evaluate
    self.evaluate_network()
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 423, in evaluate_network
    self.description = config.get("description", self.default_description)
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/util.py", line 493, in set
    self._set_attr(name, x)
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 250, in _set_attr
    self._set_attrs({name: value})
  File "/nix/store/nbrhfv3wcdsiish3y3k64jgs9826k2xc-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 243, in _set_attrs
    c.execute(
sqlite3.OperationalError: attempt to write a readonly database

pasqui23 avatar Feb 23 '22 20:02 pasqui23

sqlite3.OperationalError: attempt to write a readonly database

This is a known issue. https://github.com/NixOS/nixops/issues/1490

At this time, nixops deploy is more suitable for testing, as this command does request write access to the db.

roberth avatar Mar 03 '22 12:03 roberth

; nixops deploy                  
def-matcher: command not found
flatwoody> generating new SSH key pair... done
Password: 
flatwoody> setting state version to 22.05
flatwoody> waiting for SSH...
building all machine configurations...
trace: warning: Please use the actual nodes.* option instead of assigning machines to the config's top level
error: 'flatwoody' at /tmp/nixops-tmpnvee6p43/physical.nix:2:15 called without required argument 'pkgs'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:199:25:

          198|                 file = def.file;
          199|                 value = def.value arg;
             |                         ^
          200|               }) defs);

       … while evaluating the attribute 'value'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:199:17:

          198|                 file = def.file;
          199|                 value = def.value arg;
             |                 ^
          200|               }) defs);

       … while evaluating 'getType'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:171:21:

          170|         let
          171|           getType = value:
             |                     ^
          172|             if isAttrs value && isCoercibleToString value

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:179:16:

          178|           commonType = foldl' (type: def:
          179|             if getType def.value == type
             |                ^
          180|             then type

       … while evaluating anonymous lambda

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:178:38:

          177|           # don't have the same type
          178|           commonType = foldl' (type: def:
             |                                      ^
          179|             if getType def.value == type

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:178:24:

          177|           # don't have the same type
          178|           commonType = foldl' (type: def:
             |                        ^
          179|             if getType def.value == type

       … while evaluating 'merge'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:169:20:

          168|       check = value: true;
          169|       merge = loc: defs:
             |                    ^
          170|         let

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:195:38:

          194|             stringCoercibleSet = mergeOneOption;
          195|             lambda = loc: defs: arg: anything.merge
             |                                      ^
          196|               (loc ++ [ "<function body>" ])

       … while evaluating 'lambda'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:195:33:

          194|             stringCoercibleSet = mergeOneOption;
          195|             lambda = loc: defs: arg: anything.merge
             |                                 ^
          196|               (loc ++ [ "<function body>" ])

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:530:46:

          529|         coerce = unify: value: if isFunction value
          530|           then setFunctionArgs (args: unify (value args)) (functionArgs value)
             |                                              ^
          531|           else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);

       … while evaluating 'unifyModuleSyntax'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:343:34:

          342|      of ‘options’, ‘config’ and ‘imports’ attributes. */
          343|   unifyModuleSyntax = file: key: m:
             |                                  ^
          344|     let

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:530:39:

          529|         coerce = unify: value: if isFunction value
          530|           then setFunctionArgs (args: unify (value args)) (functionArgs value)
             |                                       ^
          531|           else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);

       … while evaluating anonymous lambda

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:530:33:

          529|         coerce = unify: value: if isFunction value
          530|           then setFunctionArgs (args: unify (value args)) (functionArgs value)
             |                                 ^
          531|           else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:397:8:

          396|       # works.
          397|     in f (args // extraArgs)
             |        ^
          398|   else

       … while evaluating 'applyIfFunction'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:373:29:

          372|
          373|   applyIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
             |                             ^
          374|     let

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:272:55:

          271|         if isFunction m || isAttrs m then
          272|           unifyModuleSyntax fallbackFile fallbackKey (applyIfFunction fallbackKey m args)
             |                                                       ^
          273|         else if isList m then

       … while evaluating 'unifyModuleSyntax'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:343:34:

          342|      of ‘options’, ‘config’ and ‘imports’ attributes. */
          343|   unifyModuleSyntax = file: key: m:
             |                                  ^
          344|     let

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:272:11:

          271|         if isFunction m || isAttrs m then
          272|           unifyModuleSyntax fallbackFile fallbackKey (applyIfFunction fallbackKey m args)
             |           ^
          273|         else if isList m then

       … while evaluating 'loadModule'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:270:53:

          269|       # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
          270|       loadModule = args: fallbackFile: fallbackKey: m:
             |                                                     ^
          271|         if isFunction m || isAttrs m then

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:311:22:

          310|           let
          311|             module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
             |                      ^
          312|             collectedImports = collectStructuredModules module._file module.key module.imports args;

       … while evaluating anonymous lambda

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:328:31:

          327|           disabledKeys = map moduleKey disabled;
          328|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          329|         in map (attrs: attrs.module) (builtins.genericClosure {

       … from call site

       … while evaluating 'filterModules'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:324:36:

          323|       # modules recursively. It returns the final list of unique-by-key modules
          324|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          325|         let

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:335:7:

          334|     in modulesPath: initialModules: args:
          335|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
             |       ^
          336|

       … while evaluating anonymous lambda

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:334:37:

          333|
          334|     in modulesPath: initialModules: args:
             |                                     ^
          335|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:180:25:

          179|       merged =
          180|         let collected = collectModules
             |                         ^
          181|           (specialArgs.modulesPath or "")

       … while evaluating 'reverseList'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/lists.nix:393:17:

          392|   */
          393|   reverseList = xs:
             |                 ^
          394|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:184:33:

          183|           ({ inherit lib options config specialArgs; } // specialArgs);
          184|         in mergeModules prefix (reverseList collected);
             |                                 ^
          185|

       … while evaluating 'byName'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:449:25:

          448|       */
          449|       byName = attr: f: modules:
             |                         ^
          450|         zipAttrsWith (n: concatLists)

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:466:21:

          465|       # an attrset 'name' => list of submodules that declare ‘name’.
          466|       declsByName = byName "options" (module: option:
             |                     ^
          467|           [{ inherit (module) _file; options = option; }]

       … while evaluating the attribute 'matchedOptions'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:510:14:

          509|     in {
          510|       inherit matchedOptions;
             |              ^
          511|

       … while evaluating 'mapAttrsRecursiveCond'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/attrsets.nix:296:36:

          295|   */
          296|   mapAttrsRecursiveCond = cond: f: set:
             |                                    ^
          297|     let

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:192:28:

          191|           # For definitions that have an associated option
          192|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                            ^
          193|

       … while evaluating the attribute 'config'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:257:9:

          256|         options = checked options;
          257|         config = checked (removeAttrs config [ "_module" ]);
             |         ^
          258|         _module = checked (config._module);

       … while evaluating 'merge'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:569:22:

          568|         check = x: isAttrs x || isFunction x || path.check x;
          569|         merge = loc: defs:
             |                      ^
          570|           (base.extendModules {

       … from call site

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:649:59:

          648|       if isDefined then
          649|         if all (def: type.check def.value) defsFinal then type.merge loc defsFinal
             |                                                           ^
          650|         else let allInvalid = filter (def: ! type.check def.value) defsFinal;

       … while evaluating the attribute 'value'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/modules.nix:660:27:

          659|     optionalValue =
          660|       if isDefined then { value = mergedValue; }
             |                           ^
          661|       else {};

       … while evaluating anonymous lambda

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/lib/types.nix:413:22:

          412|       merge = loc: defs:
          413|         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
             |                      ^
          414|             (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue

       … from call site

       … while evaluating the attribute 'config.system.build.toplevel'

       at /nix/store/kxlj5h6jss1szi1rs0lkccggr18g0lza-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nix/eval-machine-info.nix:161:32:

          160|   #TODO: take options and auter modules outputs for each node
          161|   nodes = lib.mapAttrs (n: v: {config = v;}) net.config.nodes;
             |                                ^
          162|

       … while evaluating anonymous lambda

       at /nix/store/kxlj5h6jss1szi1rs0lkccggr18g0lza-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nix/eval-machine-info.nix:309:54:

          308|         mkdir -p $out
          309|         ${toString (lib.attrValues (lib.mapAttrs (n: v: ''
             |                                                      ^
          310|           ln -s ${v.config.system.build.toplevel} $out/${n}

       … from call site

       … while evaluating the attribute 'buildCommand' of the derivation 'nixops-machines'

       at /nix/store/531v6sf2db04x3xqphfkh7zg2dgw135k-source/pkgs/stdenv/generic/make-derivation.nix:205:7:

          204|     // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
          205|       name =
             |       ^
          206|         let
error: evaluation of the deployment specification failed

pasqui23 avatar Mar 06 '22 01:03 pasqui23

Where it would ask for a pkgs param?

pasqui23 avatar Mar 10 '22 08:03 pasqui23

The error seems to originate from the "physical" expression that's generated by nixops. You can see that expression with nixops show-physical.

roberth avatar Mar 10 '22 10:03 roberth

; nixops show-physical
def-matcher: command not found
Traceback (most recent call last):
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/bin/.nixops-wrapped", line 9, in <module>
    sys.exit(main())
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/__main__.py", line 56, in main
    args.op(args)
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/script_defs.py", line 813, in op_show_physical
    depl.evaluate()
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 431, in evaluate
    self.evaluate_network()
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 423, in evaluate_network
    self.description = config.get("description", self.default_description)
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/util.py", line 493, in set
    self._set_attr(name, x)
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 250, in _set_attr
    self._set_attrs({name: value})
  File "/nix/store/3y06h6hsw56r0bszn9pl7cdpz91qjbh1-python3.8-nixops-2.0.0/lib/python3.8/site-packages/nixops/deployment.py", line 243, in _set_attrs
    c.execute(
sqlite3.OperationalError: attempt to write a readonly database

pasqui23 avatar Mar 10 '22 11:03 pasqui23

https://github.com/NixOS/nixops/issues/1490 strikes again☹

pasqui23 avatar Mar 10 '22 11:03 pasqui23

#1490 strikes againfrowning_face

https://github.com/NixOS/nixops/pull/1511 should work around it.

roberth avatar Mar 10 '22 11:03 roberth

; nixops show-physical
def-matcher: command not found
{
  flatwoody = { config, lib, pkgs, ... }: {
    config = {
      boot.kernelModules = [];
      networking = { extraHosts = "\n"; firewall.trustedInterfaces = []; };
      system.stateVersion = ( lib.mkDefault "22.05" );
    };
    imports = [
      {
        config.users.extraUsers.root.openssh.authorizedKeys.keys = [
          "ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX NixOps client key for flatwoody"
        ];
      }
    ];
  };
}

pasqui23 avatar Mar 10 '22 11:03 pasqui23

The worst thing is that pkgs is not even used …

pasqui23 avatar Mar 10 '22 12:03 pasqui23

It seems that the "physical" expression is using the legacy format compatibility which isn't working correctly.

trace: warning: Please use the actual nodes.* option instead of assigning machines to the config's top level

roberth avatar Mar 10 '22 12:03 roberth

It seems that the "physical" expression is using the legacy format compatibility which isn't working correctly.

trace: warning: Please use the actual nodes.* option instead of assigning machines to the config's top level

No, I just have written the warning incorrectly

pasqui23 avatar Mar 10 '22 13:03 pasqui23

It seems that the "physical" expression is using the legacy format compatibility which isn't working correctly.

trace: warning: Please use the actual nodes.* option instead of assigning machines to the config's top level

No, I just have written the warning incorrectly

To pick out the legacy nodes from the freeformtype more easily, you can postcompose a function that puts the all the freeform stuff in a separate attribute.

Here's what that looks like (crux: freeformItems), although it shouldn't use // { merge = ... }. https://github.com/NixOS/nixpkgs/pull/163597/files#diff-619679c9f04a185497c93f14a481d1fe65cc75298f422169128a902ba907ffa2

It'd be better to use a copy of postTransform (https://github.com/NixOS/nixpkgs/pull/163601) instead of // { merge = ... }.

roberth avatar Mar 10 '22 20:03 roberth

Using your proposed deferredModule instead of anything in freeformType will give me:

; nixops create -n net.nix
def-matcher: command not found

WARNING: NixOps 1.0 -> 2.0 conversion step required

NixOps 2.0 added support for multiple storage backends.

Upgrade steps:
1. Open /home/me/src/my-nixos-conf
2. Add:
    network.storage.legacy = {
      databasefile = "~/.nixops/deployments.nixops";
    };
3. Rerun

See https://nixops.readthedocs.io/en/latest/manual/migrating.html#state-location for more guidance.

I suspect other storage backends would have similar problems as they would not be written with modules in mind.

pasqui23 avatar Mar 11 '22 22:03 pasqui23

Even after making defaults.type = deferredModules it still does the same error.

pasqui23 avatar Mar 11 '22 22:03 pasqui23

Where is the code evaluating physical.nix?

pasqui23 avatar Mar 11 '22 22:03 pasqui23