diff options
author | Alex Auvolat <alex@adnab.me> | 2022-11-29 11:41:40 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-11-29 11:41:40 +0100 |
commit | ec3eba576a1a9574c6a2be8d90d973badb34f455 (patch) | |
tree | 5e139459046f23b00abdb4501bb9cf82bdefb722 /nix2/nix.go | |
parent | 50412d4cf0c2ae780bc8a1acce8dd7aa1d0b19b0 (diff) | |
download | nomad-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.go | 164 |
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 +} |