How to add custom services in Nixos - nixos

using nixops one can easily configure services like:
{
network.description = "Web server";
webserver = { config, pkgs, ... }:
{
services.mysql = {
enable = true;
package = pkgs.mysql51;
};
but i want to extend services. for example by using override as done for pkgs below:
let
myfoo = callPackage ...
in
pkgs = pkgs.override {
overrides = self: super: {
myfoo-core = myfoo;
};
}
question
how to do that for services?

Adding a service requires that you first write a service definition for your service. That is, a nix file that declares the options of your service and provides an implementation.
Let's say our service is called foo, then we write a service definition for it an save it as the file foo.nix:
{ config, lib, pkgs, ... }:
with lib; # use the functions from lib, such as mkIf
let
# the values of the options set for the service by the user of the service
foocfg = config.services.foo;
in {
##### interface. here we define the options that users of our service can specify
options = {
# the options for our service will be located under services.foo
services.foo = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable foo.
'';
};
barOption = {
type = types.str;
default = "qux";
description = ''
The bar option for foo.
'';
};
};
};
##### implementation
config = mkIf foocfg.enable { # only apply the following settings if enabled
# here all options that can be specified in configuration.nix may be used
# configure systemd services
# add system users
# write config files, just as an example here:
environment.etc."foo-bar" = {
text = foocfg.bar; # we can use values of options for this service here
};
};
For example, for Hydra, this file can be found here: https://github.com/NixOS/hydra/blob/dd32033657fc7d6a755c2feae1714148ee43fc7e/hydra-module.nix.
After having written the service definition, we can use it our main configuration like this:
{
network.description = "Web server";
webserver = { config, pkgs, ... }: {
imports = [ ./foo.nix ]; # import our service
services.mysql = {
enable = true;
package = pkgs.mysql51;
};
services.foo = {
enable = true;
bar = "hello nixos modules!";
};
};
}
Disclaimer: there might be some typos in this, I have not tested it.

according to aszlig, we can do this:
configuration.nix
{ config, lib, ... }:
{
disabledModules = [ "services/monitoring/nagios.nix" ];
options.services.nagios.enable = lib.mkOption {
# Make sure that this option type conflicts with the one in
# the original NixOS module for illustration purposes.
type = lib.types.str;
default = "of course";
description = "Really enable nagios?";
};
config = lib.mkIf (config.services.nagios.enable == "of course") {
systemd.services.nagios = {
description = "my own shiny nagios service...";
};
};
}
evaluate it
$ nix-instantiate --eval '<nixpkgs/nixos>' --arg configuration ./test-disable.nix -A config.systemd.services.nagios.description
"my own shiny nagios service..."

versus without disabledModules:
$ nix-instantiate --eval '<nixpkgs/nixos>' --arg configuration ./test-disable.nix -A config.systemd.services.nagios.description
error: The option `services.nagios.enable' in `/home/aszlig/test-disable.nix' is already declared in `/nix/var/nix/profiles/per-user/root/channels/vuizvui/nixpkgs/nixos/modules/services/monitoring/nagios.nix'.
(use '--show-trace' to show detailed location information)

Related

Configuring withHoogle in default.nix:

I'm trying to setup withHoogle in my default.nix, but I'm getting this error:
developPackage, error: attempt to call something which is not a function but a set(at line 26).
Here is my default.nix code:
let
pkgs = import <nixpkgs> {};
compilerVersion = "ghc865";
compiler = pkgs.haskell.packages."${compilerVersion}";
in
compiler.developPackage
{
# returnShellEnv = false;
root = ./.;
# source-overrides = {};
modifier = drv:
let pkg = pkgs.haskell.lib.addBuildTools drv (with pkgs.haskellPackages;
[
cabal-install
cabal2nix
ghcid
control
text
brick
]);
in pkg // {
env = (pkg.env { withHoogle = true; }).overrideAttrs (old: {
shellHook =
''
export PS1='\n\[\033[1;32m\][\[\e]0;nix-shell: \W\a\]nix-shell:/\W]\$ \[\033[0m\]'
'';
});
};
}
I had a similar error message when trying to nix-build a seemingly correct derivation (default.nix).
Eventually I found out using the --show-trace parameter to nix-build, that the error lie in an overlay I had in my user's ~/.config/nixpkgs/overlays directory lying around.
HTH

overridePackages of local nixpkgs' config to add new packages

i'm looking for enlightenment on how to do the best way on my situation as follows:
I have a nixpkgs folder on my project, which I download from other repo. It located on my theproject/front-plat/default.nix then the code is look like this.
{ nixpkgsFunc ? import ./nixpkgs }: let nixpkgs = nixpkgsFunc ({ config = { ... }; lib = {...}; etc.
In my theproject/release.nix I want to add a new packages which will be used for my built using the nixpkgs' front-plat. So the code looks like this.
{ front-plat ? import ./deps/front-plat {}}:
let
nixpkgs = front-platform.nixpkgs;
packageCompiler = front-plat.compiler;
config = rec { ...
packageOverrides = pkgs: rec {
packageCompiler = pkgs.packageCompiler.override {
overrides = self: super: rec {
fronFrint = self.callPackage (import ./frontFrint { inherit front-plat; });
};
};
};
3 Which I assume, now i'm using the nixpkgs from the front-plat but i'm still not sure does my nixpkgs' packageCompiler already contains fronFrint package or not.
The question is, how can I add my new packages called "frontFrint" to the nixpkgs's packageCompiler. so that the packageCompiler can built fronFrint. for example in the end, the code will be
in { front = pkgs.packageCompiler.frontFrint; }

groovy how to test for environment existence inside config.groovy

best practice for testing for existence in tree structure? For example testing for environment existence within a config file. Current working example below. Tried .hasProperty but couldn't get right object reference with in a tree structure like config.
def config = new ConfigSlurper(myenvironment).parse(new File('cfg.groovy').toURL())
def results = config.admin.server
try {
assert results.size != 0
} catch...
cfg.groovy
environments {
dev01 {
admin {
server = 'http://'
port = '1'
}
}
test {
admin {
server = 'http://'
port = '1'
}
}
}
I don't know if there is a better way to do so, but you can check if the generated config object for the passed environment is empty:
def environments = '''
environments {
dev01 {
admin {
server = 'http://'
port = '1'
}
}
test {
admin {
server = 'http://'
port = '1'
}
}
}
'''
def noExists = new ConfigSlurper('noExistEnv').parse(environments)
assert !noExists.isEmpty()
Since noExistEnv doesn't exists this execution shows:
Assertion failed:
assert !noExists.isEmpty()
|| |
|[:] true
false
Trying with an existent environment all works correctly:
def environments = '''
environments {
dev01 {
admin {
server = 'http://'
port = '1'
}
}
test {
admin {
server = 'http://'
port = '1'
}
}
}
'''
def testCfg = new ConfigSlurper('test').parse(environments)
def result = testCfg.admin.server
// check that string is not empty
assert !result.isEmpty()
println result // prints "http://"
Hope it helps,

What is the best way to expose methods from Node.js?

Consider I want to expose a method called Print
Binding method as prototype:
File Saved as Printer.js
var printerObj = function(isPrinted) {
this.printed = isPrinted;
}
printerObj.prototype.printNow = function(printData) {
console.log('= Print Started =');
};
module.exports = printerObj;
Then access printNow() by putting code require('Printer.js').printNow() in any external .js node program file.
Export method itself using module.exports:
File Saved as Printer2.js
var printed = false;
function printNow() {
console.log('= Print Started =');
}
module.exports.printNow = printNow;
Then access printNow() by putting code require('Printer2.js').printNow() in any external .js node program file.
Can anyone tell what is the difference and best way of doing it with respect to Node.js?
Definitely the first way. It is called the substack pattern and you can read about it on Twitter and on Mikeal Rogers' blog. Some code examples can be found at the jade github repo in the parser:
var Parser = exports = module.exports = function Parser(str, filename, options){
this.input = str;
this.lexer = new Lexer(str, options);
...
};
Parser.prototype = {
context: function(parser){
if (parser) {
this.contexts.push(parser);
} else {
return this.contexts.pop();
}
},
advance: function(){
return this.lexer.advance();
}
};
In the first example you are creating a class, ideally you should use it with "new" in your caller program:
var PrinterObj = require('Printer.js').PrinterObj;
var printer = new PrinterObj();
printer.PrintNow();
This is a good read on the subject: http://www.2ality.com/2012/01/js-inheritance-by-example.html
In the second example you are returning a function.
The difference is that you can have multiple instances of the first example (provided you use new as indicated) but only one instance of the second approach.

Gnome Shell Extension Key Binding

What is the simplest way to (globally) bind a key combination (e.g. <Super>+A) to a function in a gnome shell extension?
Inspecting a couple of extensions, I ran into the following code:
global.display.add_keybinding('random-name',
new Gio.Settings({schema: 'org.gnome.shell.keybindings'}),
Meta.KeyBindingFlags.NONE,
function() { /* ... some code */ });
I understand that the key combination is specified by the schema parameter, and that it's possible to create an XML file describing the combination. Is there a simpler way to do this?
The question is old, but I just implemented that for Gnome Shell 40. So here is how I did it.
The key is defined in your normal schema file that you use for the settings of the extension. So it looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema id="org.gnome.shell.extensions.mycoolstuff" path="/org/gnome/shell/extensions/mycoolstuff/">
<key name="cool-hotkey" type="as">
<default><![CDATA[['<Ctrl><Super>T']]]></default>
<summary>Hotkey to open the cool stuff.</summary>
</key>
... other config options
</schema>
</schemalist>
The key type is a "Array of String", so you can configure multiple key-combinations for the action.
In your code you use it like this:
const Main = imports.ui.main;
const Meta = imports.gi.Meta
const Shell = imports.gi.Shell
const ExtensionUtils = imports.misc.extensionUtils;
...
let my_settings = ExtensionUtils.getSettings("org.gnome.shell.extensions.mycoolstuff");
Main.wm.addKeybinding("cool-hotkey", my_settings,
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW
this._hotkeyActionMethod.bind(this));
I would recommend to remove the key binding when the extension gets disabled.
Don't know what happens if you don't do this.
Main.wm.removeKeybinding("cool-hotkey");
BTW: Changes to the settings (via dconf editor, gsettings or your extensions preferences) are active immediately.
Following is a copy of my answer here
I've only tested this in Gnome 3.22
TL;DR
Here is a class:
KeyManager: new Lang.Class({
Name: 'MyKeyManager',
_init: function() {
this.grabbers = new Map()
global.display.connect(
'accelerator-activated',
Lang.bind(this, function(display, action, deviceId, timestamp){
log('Accelerator Activated: [display={}, action={}, deviceId={}, timestamp={}]',
display, action, deviceId, timestamp)
this._onAccelerator(action)
}))
},
listenFor: function(accelerator, callback){
log('Trying to listen for hot key [accelerator={}]', accelerator)
let action = global.display.grab_accelerator(accelerator)
if(action == Meta.KeyBindingAction.NONE) {
log('Unable to grab accelerator [binding={}]', accelerator)
} else {
log('Grabbed accelerator [action={}]', action)
let name = Meta.external_binding_name_for_action(action)
log('Received binding name for action [name={}, action={}]',
name, action)
log('Requesting WM to allow binding [name={}]', name)
Main.wm.allowKeybinding(name, Shell.ActionMode.ALL)
this.grabbers.set(action, {
name: name,
accelerator: accelerator,
callback: callback
})
}
},
_onAccelerator: function(action) {
let grabber = this.grabbers.get(action)
if(grabber) {
this.grabbers.get(action).callback()
} else {
log('No listeners [action={}]', action)
}
}
})
And that's how you you use it:
let keyManager = new KeyManager()
keyManager.listenFor("<ctrl><shift>a", function(){
log("Hot keys are working!!!")
})
You're going to need imports:
const Lang = imports.lang
const Meta = imports.gi.Meta
const Shell = imports.gi.Shell
const Main = imports.ui.main
Explanation
I might be terribly wrong, but that what I've figured out in last couple days.
First of all it is Mutter who is responsible for listening for hotkeys. Mutter is a framework for creating Window Managers, it is not an window manager itself.
Gnome Shell has a class written in JS and called "Window Manager" - this is the real Window Manager which uses Mutter internally to do all low-level stuff.
Mutter has an object MetaDisplay. This is object you use to request listening for a hotkey.
But!
But Mutter will require Window Manager to approve usage of this hotkey. So what happens when hotkey is pressed?
- MetaDisplay generates event 'filter-keybinding'.
- Window Manager in Gnome Shell checks if this hotkey allowed to be processed.
- Window Manager returns appropriate value to MetaDisplay
- If it is allowed to process this hotkey, MetaDisplay generates event 'accelerator-actived'
- Your extension must listen for that event and figure out by action id which hotkey is activated.
Same as that of #p2t2p but recast using ES5 class. This is also using my logger class but you can replace that with log().
const Lang = imports.lang
const Meta = imports.gi.Meta
const Shell = imports.gi.Shell
const Main = imports.ui.main
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Logger = Me.imports.logger.Logger;
var KeyboardShortcuts = class KeyboardShortcuts {
constructor(settings) {
this._grabbers = {};
this.logger = new Logger('kt kbshortcuts', settings);
global.display.connect('accelerator-activated', (display, action, deviceId, timestamp) => {
this.logger.debug("Accelerator Activated: [display=%s, action=%s, deviceId=%s, timestamp=%s]",
display, action, deviceId, timestamp)
this._onAccelerator(action)
});
}
listenFor(accelerator, callback) {
this.logger.debug('Trying to listen for hot key [accelerator=%s]', accelerator);
let action = global.display.grab_accelerator(accelerator, 0);
if (action == Meta.KeyBindingAction.NONE) {
this.logger.error('Unable to grab accelerator [%s]', accelerator);
return;
}
this.logger.debug('Grabbed accelerator [action={}]', action);
let name = Meta.external_binding_name_for_action(action);
this.logger.debug('Received binding name for action [name=%s, action=%s]',
name, action)
this.logger.debug('Requesting WM to allow binding [name=%s]', name)
Main.wm.allowKeybinding(name, Shell.ActionMode.ALL)
this._grabbers[action]={
name: name,
accelerator: accelerator,
callback: callback
};
}
_onAccelerator(action) {
let grabber = this._grabbers[action];
if (grabber) {
grabber.callback();
} else {
this.logger.debug('No listeners [action=%s]', action);
}
}
}
and use it like,
this.accel = new KeyboardShortcuts(this.settings);
this.accel.listenFor("<ctrl><super>T", () => {
this.logger.debug("Toggling show endtime");
this._timers.settings.show_endtime = !this._timers.settings.show_endtime;
});

Resources