aboutsummaryrefslogtreecommitdiff
path: root/nix2/nix.go
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-11-29 11:41:40 +0100
committerAlex Auvolat <alex@adnab.me>2022-11-29 11:41:40 +0100
commitec3eba576a1a9574c6a2be8d90d973badb34f455 (patch)
tree5e139459046f23b00abdb4501bb9cf82bdefb722 /nix2/nix.go
parent50412d4cf0c2ae780bc8a1acce8dd7aa1d0b19b0 (diff)
downloadnomad-driver-nix2-ec3eba576a1a9574c6a2be8d90d973badb34f455.tar.gz
nomad-driver-nix2-ec3eba576a1a9574c6a2be8d90d973badb34f455.zip
Ability to run Nix jobs
Diffstat (limited to 'nix2/nix.go')
-rw-r--r--nix2/nix.go164
1 files changed, 164 insertions, 0 deletions
diff --git a/nix2/nix.go b/nix2/nix.go
new file mode 100644
index 0000000..7a86934
--- /dev/null
+++ b/nix2/nix.go
@@ -0,0 +1,164 @@
+package nix2
+
+import (
+ "bytes"
+ "path/filepath"
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/hashicorp/nomad/helper/pluginutils/hclutils"
+)
+
+const (
+ closureNix = `
+{ path }:
+let
+ nixpkgs = builtins.getFlake "github:nixos/nixpkgs/nixos-22.05";
+ inherit (nixpkgs.legacyPackages.x86_64-linux) buildPackages;
+in buildPackages.closureInfo { rootPaths = builtins.storePath path; }
+`
+)
+
+func prepareNixPackages(taskDir string, packages []string) (hclutils.MapStrStr, error) {
+ mounts := make(hclutils.MapStrStr)
+
+ profileLink := filepath.Join(taskDir, "current-profile")
+ profile, err := nixBuildProfile(packages, profileLink)
+ if err != nil {
+ return nil, fmt.Errorf("Build of the flakes failed: %v", err)
+ }
+
+ closureLink := filepath.Join(taskDir, "current-closure")
+ closure, err := nixBuildClosure(profileLink, closureLink)
+ if err != nil {
+ return nil, fmt.Errorf("Build of the flakes failed: %v", err)
+ }
+
+ mounts[profile] = profile
+
+ if entries, err := os.ReadDir(profile); err != nil {
+ return nil, fmt.Errorf("Couldn't read profile directory: %w", err)
+ } else {
+ for _, entry := range entries {
+ if name := entry.Name(); name != "etc" {
+ mounts[filepath.Join(profile, name)] = "/" + name
+ continue
+ }
+
+ etcEntries, err := os.ReadDir(filepath.Join(profile, "etc"))
+ if err != nil {
+ return nil, fmt.Errorf("Couldn't read profile's /etc directory: %w", err)
+ }
+
+ for _, etcEntry := range etcEntries {
+ etcName := etcEntry.Name()
+ mounts[filepath.Join(profile, "etc", etcName)] = "/etc/" + etcName
+ }
+ }
+ }
+
+ mounts[filepath.Join(closure, "registration")] = "/registration"
+
+ requisites, err := nixRequisites(closure)
+ if err != nil {
+ return nil, fmt.Errorf("Couldn't determine flake requisites: %v", err)
+ }
+
+ for _, requisite := range requisites {
+ mounts[requisite] = requisite
+ }
+
+ return mounts, nil
+}
+
+func nixBuildProfile(flakes []string, link string) (string, error) {
+ cmd := exec.Command("nix", append(
+ []string{
+ "--extra-experimental-features", "nix-command",
+ "--extra-experimental-features", "flakes",
+ "profile",
+ "install",
+ "--no-write-lock-file",
+ "--profile",
+ link},
+ flakes...)...)
+ stderr := &bytes.Buffer{}
+ cmd.Stderr = stderr
+
+ if err := cmd.Run(); err != nil {
+ return "", fmt.Errorf("%v failed: %s. Err: %v", cmd.Args, stderr.String(), err)
+ }
+
+ if target, err := os.Readlink(link); err == nil {
+ return os.Readlink(filepath.Join(filepath.Dir(link), target))
+ } else {
+ return "", err
+ }
+}
+
+func nixBuildClosure(profile string, link string) (string, error) {
+ cmd := exec.Command(
+ "nix",
+ "--extra-experimental-features", "nix-command",
+ "--extra-experimental-features", "flakes",
+ "build",
+ "--out-link", link,
+ "--expr", closureNix,
+ "--impure",
+ "--no-write-lock-file",
+ "--argstr", "path", profile)
+
+ stderr := &bytes.Buffer{}
+ cmd.Stderr = stderr
+
+ if err := cmd.Run(); err != nil {
+ return "", fmt.Errorf("%v failed: %s. Err: %v", cmd.Args, stderr.String(), err)
+ }
+
+ return os.Readlink(link)
+}
+
+type nixPathInfo struct {
+ Path string `json:"path"`
+ NarHash string `json:"narHash"`
+ NarSize uint64 `json:"narSize"`
+ References []string `json:"references"`
+ Deriver string `json:"deriver"`
+ RegistrationTime uint64 `json:"registrationTime"`
+ Signatures []string `json:"signatures"`
+}
+
+func nixRequisites(path string) ([]string, error) {
+ cmd := exec.Command(
+ "nix",
+ "--extra-experimental-features", "nix-command",
+ "--extra-experimental-features", "flakes",
+ "path-info",
+ "--json",
+ "--recursive",
+ path)
+
+ stdout := &bytes.Buffer{}
+ cmd.Stdout = stdout
+
+ stderr := &bytes.Buffer{}
+ cmd.Stderr = stderr
+
+ if err := cmd.Run(); err != nil {
+ return nil, fmt.Errorf("%v failed: %s. Err: %v", cmd.Args, stderr.String(), err)
+ }
+
+ result := []*nixPathInfo{}
+ if err := json.Unmarshal(stdout.Bytes(), &result); err != nil {
+ return nil, err
+ }
+
+ requisites := []string{}
+ for _, result := range result {
+ requisites = append(requisites, result.Path)
+ }
+
+ return requisites, nil
+}