diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index cd375352c4ca..a7ed5d1faa38 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -372,6 +372,17 @@ sub getUnitInfo {
return $info;
}
+# Fail if the given systemd unit is not in the "active" state.
+sub requireActiveUnit {
+ my ($self, $unit) = @_;
+ $self->nest("checking if unit ‘$unit’ has reached state 'active'", sub {
+ my $info = $self->getUnitInfo($unit);
+ my $state = $info->{ActiveState};
+ if ($state ne "active") {
+ die "Expected unit ‘$unit’ to to be in state 'active' but it is in state ‘$state’\n";
+ };
+ });
+}
# Wait for a systemd unit to reach the "active" state.
sub waitForUnit {
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index 332a04634d06..01b4aca91731 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -7,6 +7,19 @@ let
writeTextOrNull = f: t: mapNullable (pkgs.writeTextDir f) t;
dataDir = cfg.dataDir;
+ staticDir = cfg.dataDir + "/static";
+
+ graphiteLocalSettingsDir = pkgs.runCommand "graphite_local_settings"
+ {inherit graphiteLocalSettings;} ''
+ mkdir -p $out
+ ln -s $graphiteLocalSettings $out/graphite_local_settings.py
+ '';
+
+ graphiteLocalSettings = pkgs.writeText "graphite_local_settings.py" (
+ "STATIC_ROOT = '${staticDir}'\n" +
+ optionalString (! isNull config.time.timeZone) "TIME_ZONE = '${config.time.timeZone}'\n"
+ + cfg.web.extraConfig
+ );
graphiteApiConfig = pkgs.writeText "graphite-api.yaml" ''
time_zone: ${config.time.timeZone}
@@ -94,6 +107,15 @@ in {
default = 8080;
type = types.int;
};
+
+ extraConfig = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ Graphite webapp settings. See:
+
+ '';
+ };
};
api = {
@@ -460,9 +482,13 @@ in {
];
};
penvPack = "${penv}/${pkgs.python.sitePackages}";
- # opt/graphite/webapp contains graphite/settings.py
- # explicitly adding pycairo in path because it cannot be imported via buildEnv
- in "${penvPack}/opt/graphite/webapp:${penvPack}:${pkgs.pythonPackages.pycairo}/${pkgs.python.sitePackages}";
+ in concatStringsSep ":" [
+ "${graphiteLocalSettingsDir}"
+ "${penvPack}/opt/graphite/webapp"
+ "${penvPack}"
+ # explicitly adding pycairo in path because it cannot be imported via buildEnv
+ "${pkgs.pythonPackages.pycairo}/${pkgs.python.sitePackages}"
+ ];
DJANGO_SETTINGS_MODULE = "graphite.settings";
GRAPHITE_CONF_DIR = configDir;
GRAPHITE_STORAGE_DIR = dataDir;
@@ -470,9 +496,9 @@ in {
};
serviceConfig = {
ExecStart = ''
- ${pkgs.python27Packages.waitress}/bin/waitress-serve \
- --host=${cfg.web.listenAddress} --port=${toString cfg.web.port} \
- --call django.core.handlers.wsgi:WSGIHandler'';
+ ${pkgs.python27Packages.waitress-django}/bin/waitress-serve-django \
+ --host=${cfg.web.listenAddress} --port=${toString cfg.web.port}
+ '';
User = "graphite";
Group = "graphite";
PermissionsStartOnly = true;
@@ -482,16 +508,20 @@ in {
mkdir -p ${dataDir}/{whisper/,log/webapp/}
chmod 0700 ${dataDir}/{whisper/,log/webapp/}
- # populate database
- ${pkgs.python27Packages.graphite_web}/bin/manage-graphite.py syncdb --noinput
+ ${pkgs.pythonPackages.django_1_8}/bin/django-admin.py migrate --noinput
- # create index
- ${pkgs.python27Packages.graphite_web}/bin/build-index.sh
-
- chown -R graphite:graphite ${cfg.dataDir}
+ chown -R graphite:graphite ${dataDir}
touch ${dataDir}/db-created
fi
+
+ # Only collect static files when graphite_web changes.
+ if ! [ "${dataDir}/current_graphite_web" -ef "${pkgs.python27Packages.graphite_web}" ]; then
+ mkdir -p ${staticDir}
+ ${pkgs.pythonPackages.django_1_8}/bin/django-admin.py collectstatic --noinput --clear
+ chown -R graphite:graphite ${staticDir}
+ ln -sfT "${pkgs.python27Packages.graphite_web}" "${dataDir}/current_graphite_web"
+ fi
'';
};
diff --git a/nixos/release.nix b/nixos/release.nix
index 63f739e8e896..3016b1ef9442 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -257,6 +257,7 @@ in rec {
tests.gnome3 = callTest tests/gnome3.nix {};
tests.gnome3-gdm = callTest tests/gnome3-gdm.nix {};
tests.grafama = callTest tests/grafana.nix {};
+ tests.graphite = callTest tests/graphite.nix {};
tests.hardened = callTest tests/hardened.nix { };
tests.hibernate = callTest tests/hibernate.nix {};
tests.hound = callTest tests/hound.nix {};
diff --git a/nixos/tests/graphite.nix b/nixos/tests/graphite.nix
new file mode 100644
index 000000000000..4fd7de192d55
--- /dev/null
+++ b/nixos/tests/graphite.nix
@@ -0,0 +1,26 @@
+import ./make-test.nix ({ pkgs, ...} :
+{
+ name = "graphite";
+ nodes = {
+ one =
+ { config, pkgs, ... }: {
+ services.graphite = {
+ web = {
+ enable = true;
+ };
+ carbon = {
+ enableCache = true;
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ startAll;
+ $one->waitForUnit("default.target");
+ $one->requireActiveUnit("graphiteWeb.service");
+ $one->requireActiveUnit("carbonCache.service");
+ $one->succeed("echo \"foo 1 `date +%s`\" | nc -q0 localhost 2003");
+ $one->waitUntilSucceeds("curl 'http://localhost:8080/metrics/find/?query=foo&format=treejson' --silent | grep foo")
+ '';
+})
diff --git a/pkgs/development/python-modules/waitress-django/default.nix b/pkgs/development/python-modules/waitress-django/default.nix
new file mode 100644
index 000000000000..6efaf800b3cd
--- /dev/null
+++ b/pkgs/development/python-modules/waitress-django/default.nix
@@ -0,0 +1,8 @@
+{ buildPythonPackage, django_1_8, waitress }:
+buildPythonPackage {
+ name = "waitress-django";
+ src = ./.;
+ pythonPath = [ django_1_8 waitress ];
+ doCheck = false;
+ meta.description = "A waitress WSGI server serving django";
+}
diff --git a/pkgs/development/python-modules/waitress-django/setup.py b/pkgs/development/python-modules/waitress-django/setup.py
new file mode 100644
index 000000000000..07f7b326fdaf
--- /dev/null
+++ b/pkgs/development/python-modules/waitress-django/setup.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup( name = "waitress-django"
+ , version = "0.0.0"
+ , description = "A waitress WSGI server serving django"
+ , author = "Bas van Dijk"
+ , author_email = "v.dijk.bas@gmail.com"
+ , package_dir = {"" : "src"}
+ , scripts = ["src/waitress-serve-django"]
+ )
diff --git a/pkgs/development/python-modules/waitress-django/src/waitress-serve-django b/pkgs/development/python-modules/waitress-django/src/waitress-serve-django
new file mode 100755
index 000000000000..b710086c22b9
--- /dev/null
+++ b/pkgs/development/python-modules/waitress-django/src/waitress-serve-django
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+import sys
+from waitress import serve
+from waitress.adjustments import Adjustments
+import django
+from django.core.handlers.wsgi import WSGIHandler
+from django.contrib.staticfiles.handlers import StaticFilesHandler
+
+if __name__ == "__main__":
+ kw, args = Adjustments.parse_args(sys.argv[1:])
+ django.setup()
+ # These arguments are specific to the runner, not waitress itself.
+ del kw['call'], kw['help']
+ serve(StaticFilesHandler(WSGIHandler()), **kw)
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index 231d5fecf6ba..a94c8e8e4097 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -60,7 +60,7 @@ let
buildPythonApplication = args: buildPythonPackage ({namePrefix="";} // args );
- graphiteVersion = "0.9.15";
+ graphiteVersion = "1.0.2";
fetchPypi = makeOverridable( {format ? "setuptools", ... } @attrs:
let
@@ -8286,14 +8286,14 @@ in {
django_tagging = callPackage ../development/python-modules/django_tagging { };
- django_tagging_0_3 = self.django_tagging.overrideAttrs (attrs: rec {
- name = "django-tagging-0.3.6";
+ django_tagging_0_4_3 = self.django_tagging.overrideAttrs (attrs: rec {
+ name = "django-tagging-0.4.3";
src = pkgs.fetchurl {
url = "mirror://pypi/d/django-tagging/${name}.tar.gz";
- sha256 = "03zlbq13rydfh28wh0jk3x3cjk9x6jjmqnx1i3ngjmfwbxf8x6j1";
+ sha256 = "0617azpmp6jpg3d88v2ir97qrc9aqcs2s9gyvv9bgf2cp55khxhs";
};
- propagatedBuildInputs = with self; [ django ];
+ propagatedBuildInputs = with self; [ django_1_8 ];
});
django_classytags = buildPythonPackage rec {
@@ -22250,11 +22250,11 @@ EOF
};
waitress = buildPythonPackage rec {
- name = "waitress-0.8.9";
+ name = "waitress-1.0.2";
src = pkgs.fetchurl {
url = "mirror://pypi/w/waitress/${name}.tar.gz";
- sha256 = "826527dc9d334ed4ed76cdae672fdcbbccf614186657db71679ab58df869458a";
+ sha256 = "0pw6yyxi348r2xpq3ykqnf7gwi881azv2422d2ixb0xi5jws2ky7";
};
doCheck = false;
@@ -22265,6 +22265,8 @@ EOF
};
};
+ waitress-django = callPackage ../development/python-modules/waitress-django { };
+
webassets = buildPythonPackage rec {
name = "webassets-${version}";
version = "0.12.1";
@@ -23457,7 +23459,7 @@ EOF
src = pkgs.fetchurl {
url = "mirror://pypi/w/whisper/${name}.tar.gz";
- sha256 = "1chkphxwnwvy2cs7jc2h2i0lqqvi9jx6vqj3ly88lwk7m35r4ss2";
+ sha256 = "1v1bi3fl1i6p4z4ki692bykrkw6907dn3mfq0151f70lvi3zpns3";
};
# error: invalid command 'test'
@@ -23524,7 +23526,7 @@ EOF
src = pkgs.fetchurl {
url = "mirror://pypi/c/carbon/${name}.tar.gz";
- sha256 = "f01db6d37726c6fc0a8aaa66a7bf14436b0dd0d62ef3c20ecb31605a4d365d2e";
+ sha256 = "142smpmgbnjinvfb6s4ijazish4vfgzyd8zcmdkh55y051fkixkn";
};
propagatedBuildInputs = with self; [ whisper txamqp zope_interface twisted ];
@@ -23739,10 +23741,13 @@ EOF
src = pkgs.fetchurl rec {
url = "mirror://pypi/g/graphite-web/${name}.tar.gz";
- sha256 = "1c0kclbv8shv9nvjx19wqm4asia58s3qmd9fapchc6y9fjpjax6q";
+ sha256 = "0q8bwlj75jqyzmazfsi5sa26xl58ssa8wdxm2l4j0jqyn8xpfnmc";
};
- propagatedBuildInputs = with self; [ django django_tagging_0_3 whisper pycairo ldap memcached pytz ];
+ propagatedBuildInputs = with self; [
+ django_1_8 django_tagging_0_4_3 whisper pycairo cairocffi
+ ldap memcached pytz urllib3 scandir
+ ];
postInstall = ''
wrapProgram $out/bin/run-graphite-devel-server.py \
@@ -23750,10 +23755,20 @@ EOF
'';
preConfigure = ''
- substituteInPlace webapp/graphite/thirdparty/pytz/__init__.py --replace '/usr/share/zoneinfo' '/etc/zoneinfo'
- substituteInPlace webapp/graphite/settings.py --replace "join(WEBAPP_DIR, 'content')" "join('$out', 'webapp', 'content')"
- cp webapp/graphite/manage.py bin/manage-graphite.py
- substituteInPlace bin/manage-graphite.py --replace 'settings' 'graphite.settings'
+ # graphite is configured by storing a local_settings.py file inside the
+ # graphite python package. Since that package is stored in the immutable
+ # Nix store we can't modify it. So how do we configure graphite?
+ #
+ # First of all we rename "graphite.local_settings" to
+ # "graphite_local_settings" so that the settings are not looked up in the
+ # graphite package anymore. Secondly we place a directory containing a
+ # graphite_local_settings.py on the PYTHONPATH in the graphite module
+ # .
+ substituteInPlace webapp/graphite/settings.py \
+ --replace "graphite.local_settings" " graphite_local_settings"
+
+ substituteInPlace webapp/graphite/settings.py \
+ --replace "join(WEBAPP_DIR, 'content')" "join('$out', 'webapp', 'content')"
'';
# error: invalid command 'test'