vagrant-nixos 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,22 +1,66 @@
1
1
  # NixOS Vagrant Plugin
2
2
 
3
- This plugin makes working with [NixOS](http://nixos.org) guests much easier to work with in [Vagrant](http://www.vagrantup.com) by:
3
+ This plugin makes working with [NixOS](http://nixos.org) guests in [Vagrant](http://www.vagrantup.com) a bit nicer:
4
4
 
5
- * Allowing network configurations from Vagrantfile (Vagrant does not have a default NixOS guest capability)
6
- * Provide a convention for nixos
5
+ * Allow network configurations
6
+ * Allow hostname setting
7
+ * Provide nix provisioning
7
8
 
8
- ## Usage:
9
+ ## Install:
9
10
 
10
11
  ```bash
11
12
  $ vagrant plugin install vagrant-nixos
12
- ...
13
13
  ```
14
14
 
15
+ ## Example Vagrantfile
16
+
17
+ ```ruby
18
+ Vagrant.configure("2") do |config|
19
+
20
+ # use a suitable NixOS box
21
+ config.vm.box = "nixos-14.02-x86_64"
22
+
23
+ # set hostname
24
+ config.vm.hostname = "nixy"
25
+
26
+ # Setup networking
27
+ config.vm.network "public_network", :bridge => 'en1: Wi-Fi (AirPort)'
28
+ config.vm.network "private_network", :ip => "172.16.16.16"
29
+
30
+ # Add the htop package
31
+ config.vm.provision :nixos, :expression => {
32
+ environment: {
33
+ systemPackages: [ :htop ]
34
+ }
35
+ }, :NIX_PATH => "/custom/path/to/nixpkgs"
36
+
37
+ end
38
+ ```
39
+
40
+ In the above `Vagrantfile` example we provision the box using the `:expression` method, which will perform a simple ruby -> nix conversion. `:expression` provisioning creates a nix module that executes with `pkgs` in scope. It is roughly equivilent to the below version that uses the `:inline` method.
41
+
42
+ ```ruby
43
+ config.vm.provision :nixos, :inline => %{
44
+ {config, pkgs, ...}: with pkgs; {
45
+ environment.systemPackages = [ htop ];
46
+ }
47
+ }, :NIX_PATH => "/custom/path/to/nixpkgs"
48
+ ```
49
+
50
+ Both examples show the optional configuring of a custom `NIX_PATH` path.
51
+
52
+
15
53
  ## How it works
16
54
 
17
- In nixos we don't mess around with the files in `/etc` instead we write delarative expressions for the system configuration starting in `/etc/nixos/configuration.nix`.
55
+ In nixos we don't mess around with the files in `/etc` instead we write expressions for the system configuration starting in `/etc/nixos/configuration.nix`.
56
+
57
+ This plugin sets some ground rules for nixos boxes to keep this configuration clean and provisioning possible.
58
+
59
+ Box creators should ensure that their `configuration.nix` file imports an nix module `/etc/nixos/vagrant.nix` which will be overwritten by `vagrant-nixos` during `vagrant up` or `vagrant provision`.
60
+
61
+ See the configuration in our [NixOS packer template](http://github.com/oxdi/nixos) for an example.
18
62
 
19
- This plugin sets some ground rules for nixos boxes to keep this configuration clean.
63
+ ## Issues
20
64
 
21
- Box creators should ensure that their `configuration.nix` file imports an empty file `/etc/nixos/vagrant.nix` which will be overwritten by `vagrant-nixos` during `vagrant up` or `vagrant provision` and a `nixos-rebuild switch` will be triggerd.
65
+ It's a bit slow on the initial boot/provision at the moment as it must run nixos-rebuild several times. This is far from ideal I'm sure I'll find a better place to hook in the rebuild step soon.
22
66
 
@@ -0,0 +1,30 @@
1
+ require 'set'
2
+ require 'tempfile'
3
+
4
+
5
+ module VagrantPlugins
6
+ module Nixos
7
+ module Cap
8
+ class ChangeHostName
9
+ include Vagrant::Util
10
+
11
+ def self.nix_module(name)
12
+ <<-NIX
13
+ { config, pkgs, ... }:
14
+ {
15
+ networking.hostName = "#{name}";
16
+ }
17
+ NIX
18
+ end
19
+
20
+ def self.change_host_name(machine, name)
21
+ if Nixos.write_config(machine, "vagrant-hostname.nix", nix_module(name))
22
+ Nixos.rebuild(machine)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -11,60 +11,41 @@ module VagrantPlugins
11
11
 
12
12
  def self.nix_interface_expr(options)
13
13
  <<-NIX
14
- {
15
- name = "eth#{options[:interface]}";
16
- ipAddress = "#{options[:ip]}";
17
- subnetMask = "#{options[:netmask]}";
18
- }
14
+ {
15
+ name = "eth#{options[:interface]}";
16
+ ipAddress = "#{options[:ip]}";
17
+ subnetMask = "#{options[:netmask]}";
18
+ }
19
19
  NIX
20
20
  end
21
21
 
22
- def self.nix_interface_module(networks)
22
+ def self.nix_module(networks)
23
23
  exprs = networks.inject([]) do |exprs, network|
24
24
  # Interfaces without an ip set will fallback to
25
25
  # DHCP if useDHCP is set. So skip them.
26
- if network[:ip].empty?
26
+ if network[:ip].nil? or network[:ip].empty?
27
27
  exprs
28
28
  else
29
29
  exprs << nix_interface_expr(network)
30
30
  end
31
31
  end
32
32
  <<-NIX
33
- { config, pkgs, ... }:
34
- {
35
- networking.useDHCP = true;
36
- networking.interfaces = [
37
- #{exprs.join("\n")}
38
- ];
39
- }
33
+ { config, pkgs, ... }:
34
+ {
35
+ networking.usePredictableInterfaceNames = false;
36
+ networking.useDHCP = true;
37
+ networking.interfaces = [
38
+ #{exprs.join("\n")}
39
+ ];
40
+ }
40
41
  NIX
41
42
  end
42
43
 
43
44
  def self.configure_networks(machine, networks)
44
- machine.communicate.tap do |comm|
45
- # build the network config
46
- conf = nix_interface_module(networks)
47
-
48
- # Perform the careful dance necessary to reconfigure
49
- # the network interfaces
50
- temp = Tempfile.new("vagrant")
51
- temp.binmode
52
- temp.write(conf)
53
- temp.close
54
-
55
- puts conf
56
-
57
- # add the network config
58
- filename = "vagrant-interfaces.nix"
59
- comm.upload(temp.path, "/tmp/#{filename}")
60
- comm.sudo("mv /tmp/#{filename} /etc/nixos/#{filename}")
61
-
62
- # TODO: check that the network config is referenced in vagrant.nix
63
-
64
- # cleanup
65
- temp.unlink
66
- end
67
- end
45
+ if Nixos.write_config(machine, "vagrant-interfaces.nix", nix_module(networks))
46
+ Nixos.rebuild(machine)
47
+ end
48
+ end
68
49
 
69
50
  end
70
51
  end
@@ -0,0 +1,46 @@
1
+
2
+ module VagrantPlugins
3
+ module Nixos
4
+ class Config < Vagrant.plugin("2", :config)
5
+ attr_accessor :inline
6
+ attr_accessor :path
7
+ attr_accessor :expression
8
+ attr_accessor :NIX_PATH
9
+
10
+ def initialize
11
+ @inline = UNSET_VALUE
12
+ @path = UNSET_VALUE
13
+ @expression = UNSET_VALUE
14
+ @NIX_PATH = UNSET_VALUE
15
+ end
16
+
17
+ def finalize!
18
+ @inline = nil if @inline == UNSET_VALUE
19
+ @path = nil if @path == UNSET_VALUE
20
+ @expression = nil if @expression == UNSET_VALUE
21
+ @NIX_PATH = nil if @NIX_PATH == UNSET_VALUE
22
+ end
23
+
24
+ def expression=(v)
25
+ @expression = v.to_nix
26
+ end
27
+
28
+ def validate(machine)
29
+ errors = _detected_errors
30
+
31
+ if (path && inline) or (path && expression) or (inline && expression)
32
+ errors << "You can have one and only one of :path, :expression or :inline for nixos provisioner"
33
+ elsif !path && !inline && !expression
34
+ errors << "Missing :inline, :expression or :path for nixos provisioner"
35
+ end
36
+
37
+ if path && !File.exist?(path)
38
+ errors << "Invalid path #{path}"
39
+ end
40
+
41
+ { "nixos provisioner" => errors }
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -1,9 +1,13 @@
1
1
  module VagrantPlugins
2
2
  module Nixos
3
3
  class Guest < Vagrant.plugin("2", :guest)
4
+
5
+ attr_accessor :nix_imports
6
+
4
7
  def detect?(machine)
5
8
  machine.communicate.test("test -f /etc/nixos/configuration.nix")
6
9
  end
10
+
7
11
  end
8
12
  end
9
13
  end
@@ -12,6 +12,9 @@ end
12
12
 
13
13
  module VagrantPlugins
14
14
  module Nixos
15
+
16
+ @@nix_imports = {}
17
+
15
18
  class Plugin < Vagrant.plugin("2")
16
19
  name "nixos"
17
20
  description <<-DESC
@@ -28,6 +31,22 @@ module VagrantPlugins
28
31
  Cap::ConfigureNetworks
29
32
  end
30
33
 
34
+ guest_capability("nixos", "change_host_name") do
35
+ require_relative "cap/change_host_name"
36
+ Cap::ChangeHostName
37
+ end
38
+
39
+ config :nixos, :provisioner do
40
+ require_relative "config"
41
+ Config
42
+ end
43
+
44
+ provisioner :nixos do
45
+ require_relative "provisioner"
46
+ Provisioner
47
+ end
48
+
31
49
  end
50
+
32
51
  end
33
52
  end
@@ -0,0 +1,28 @@
1
+ module VagrantPlugins
2
+ module Nixos
3
+ class NixosConfigError < Vagrant::Errors::VagrantError
4
+ end
5
+
6
+ class Provisioner < Vagrant.plugin("2", :provisioner)
7
+ # This is the method called when the actual provisioning should be
8
+ # done. The communicator is guaranteed to be ready at this point,
9
+ # and any shared folders or networks are already setup.
10
+ #
11
+ # No return value is expected.
12
+ def provision
13
+ conf = if @config.inline
14
+ @config.inline
15
+ elsif @config.path
16
+ File.read(@config.path)
17
+ elsif @config.expression
18
+ "{config, pkgs, ...}: with pkgs; #{@config.expression}"
19
+ else
20
+ raise NixosConfigError, "Mising :path, :inline or :expression"
21
+ end
22
+ Nixos.write_config(machine, "vagrant-provision.nix", conf)
23
+ Nixos.rebuild!(machine, @config.NIX_PATH)
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,156 @@
1
+ module VagrantPlugins
2
+ module Nixos
3
+ ##############################################
4
+ # Not sure if this is legit. But We'll use this module
5
+ # to store the state of the nix configuration and handle
6
+ # sending it to the machine.
7
+ #############################################
8
+
9
+ @@imports = {}
10
+
11
+ # Send file to machine and report if it changed
12
+ # See _write_config
13
+ def self.write_config(machine, filename, conf)
14
+ include_config(machine, filename)
15
+ _write_config(machine, filename, conf)
16
+ end
17
+
18
+ # mark a file that should be imported to the main config
19
+ def self.include_config(machine, filename)
20
+ if @@imports[machine.id].nil?
21
+ @@imports[machine.id] = {}
22
+ end
23
+ @@imports[machine.id][filename] = true
24
+ end
25
+
26
+ def imports
27
+ end
28
+
29
+ # rebuild the base vagrant.nix configuration
30
+ def self.rebuild(machine, nix_env=nil)
31
+ # build
32
+ conf = "{ config, pkgs, ... }:\n{"
33
+ # Add a mock provision file if it is missing as during boot the
34
+ # provisioning file may not be deployed yet.
35
+ if !machine.communicate.test("test -f /etc/nixos/vagrant-provision.nix")
36
+ _write_config(machine, "vagrant-provision.nix", "{ config, pkgs, ... }: {}")
37
+ end
38
+ # imports
39
+ conf_paths = if @@imports[machine.id].nil?
40
+ []
41
+ else
42
+ @@imports[machine.id].keys.inject([]) do |paths, filename|
43
+ paths << "./#{filename}"
44
+ end
45
+ end
46
+ conf_paths << "./vagrant-provision.nix"
47
+ # construct the nix module
48
+ conf << %{
49
+ imports = [
50
+ #{conf_paths.join("\n\t\t")}
51
+ ];
52
+ }
53
+ # default NIX_PATH
54
+ if nix_env
55
+ conf << %{
56
+ config.environment.shellInit = ''
57
+ export NIX_PATH=#{nix_env}:$NIX_PATH
58
+ '';
59
+ }
60
+ end
61
+ conf << "}"
62
+ # output / build the config
63
+ _write_config(machine, "vagrant.nix", conf)
64
+ rebuild!(machine, nix_env)
65
+ end
66
+
67
+ # just do nixos-rebuild
68
+ def self.rebuild!(machine, nix_env=nil)
69
+ rebuild_cmd = "nixos-rebuild switch"
70
+ rebuild_cmd = "NIX_PATH=#{nix_env}:$NIX_PATH #{rebuild_cmd}" if nix_env
71
+ machine.communicate.tap do |comm|
72
+ comm.sudo(rebuild_cmd)
73
+ end
74
+ end
75
+
76
+ def self.same?(machine, f1, f2)
77
+ machine.communicate.test("cmp --silent '#{f1}' '#{f2}'")
78
+ end
79
+
80
+ protected
81
+
82
+ # Send file to machine.
83
+ # Returns true if the uploaded file if different from any
84
+ # preexisting file, false if the file is indentical
85
+ def self._write_config(machine, filename, conf)
86
+ temp = Tempfile.new("vagrant")
87
+ temp.binmode
88
+ temp.write(conf)
89
+ temp.close
90
+ changed = true
91
+ machine.communicate.tap do |comm|
92
+ source = "/tmp/#{filename}"
93
+ target = "/etc/nixos/#{filename}"
94
+ comm.upload(temp.path, source)
95
+ if same?(machine, source, target)
96
+ changed = false
97
+ else
98
+ comm.sudo("mv '#{source}' '#{target}'")
99
+ end
100
+ end
101
+ return changed
102
+ end
103
+ end
104
+ end
105
+
106
+ #################################################
107
+ # Naughty extending of builtins.
108
+ # Add to_nix functions to convert ruby2nix (ish).
109
+ #################################################
110
+
111
+ module Nix
112
+ INDENT_STRING=" "
113
+ end
114
+
115
+ class Symbol
116
+ def to_nix(indent = 0)
117
+ to_s
118
+ end
119
+ end
120
+
121
+ class NilClass
122
+ def to_nix(indent = 0)
123
+ "null"
124
+ end
125
+ end
126
+
127
+ class Hash
128
+ def to_nix(indent = 0)
129
+ "{\n" +
130
+ sort {|a, b| a[0].to_s <=> b[0].to_s}.map do |key, value|
131
+ raise "Key must be a Symbol, not #{key.class}" unless key.is_a?(Symbol)
132
+ Nix::INDENT_STRING * (indent + 1)+ key.to_nix +
133
+ " = " + value.to_nix(indent + 1) + ";"
134
+ end.join("\n") + "\n" +
135
+ Nix::INDENT_STRING * indent + "}"
136
+ end
137
+ end
138
+
139
+ class Array
140
+ def to_nix(indent = 0)
141
+ "[ " + map(&:to_nix).join(" ") + " ]"
142
+ end
143
+ end
144
+
145
+ class String
146
+ def to_nix(indent = 0)
147
+ # TODO: escape ${var} in string
148
+ "''#{self}''"
149
+ end
150
+ end
151
+
152
+ class Fixnum
153
+ def to_nix(indent = 0)
154
+ to_s
155
+ end
156
+ end
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Nixos
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
data/lib/vagrant-nixos.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "vagrant-nixos/version"
2
2
  require "vagrant-nixos/plugin"
3
+ require "vagrant-nixos/util"
3
4
 
4
5
  module VagrantPlugins
5
6
  module Nixos
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-nixos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -57,9 +57,13 @@ files:
57
57
  - README.md
58
58
  - Rakefile
59
59
  - lib/vagrant-nixos.rb
60
+ - lib/vagrant-nixos/cap/change_host_name.rb
60
61
  - lib/vagrant-nixos/cap/configure_networks.rb
62
+ - lib/vagrant-nixos/config.rb
61
63
  - lib/vagrant-nixos/guest.rb
62
64
  - lib/vagrant-nixos/plugin.rb
65
+ - lib/vagrant-nixos/provisioner.rb
66
+ - lib/vagrant-nixos/util.rb
63
67
  - lib/vagrant-nixos/version.rb
64
68
  - vagrant-nixos.gemspec
65
69
  homepage: ''
@@ -77,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
81
  version: '0'
78
82
  segments:
79
83
  - 0
80
- hash: 867435977267935838
84
+ hash: -2672908896929741571
81
85
  required_rubygems_version: !ruby/object:Gem::Requirement
82
86
  none: false
83
87
  requirements:
@@ -86,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
90
  version: '0'
87
91
  segments:
88
92
  - 0
89
- hash: 867435977267935838
93
+ hash: -2672908896929741571
90
94
  requirements: []
91
95
  rubyforge_project:
92
96
  rubygems_version: 1.8.23