2022-11-29 10:41:40 +00:00
|
|
|
package nix2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2022-11-29 12:01:46 +00:00
|
|
|
"path/filepath"
|
2022-11-29 10:41:40 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
closureNix = `
|
|
|
|
{ path }:
|
|
|
|
let
|
2022-11-29 12:01:46 +00:00
|
|
|
nixpkgs = builtins.getFlake "%s";
|
2022-11-29 10:41:40 +00:00
|
|
|
inherit (nixpkgs.legacyPackages.x86_64-linux) buildPackages;
|
|
|
|
in buildPackages.closureInfo { rootPaths = builtins.storePath path; }
|
|
|
|
`
|
|
|
|
)
|
|
|
|
|
2022-11-29 12:01:46 +00:00
|
|
|
func prepareNixPackages(taskDir string, packages []string, nixpkgs string) (hclutils.MapStrStr, error) {
|
2022-11-29 10:41:40 +00:00
|
|
|
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")
|
2022-11-29 12:01:46 +00:00
|
|
|
closure, err := nixBuildClosure(profileLink, closureLink, nixpkgs)
|
2022-11-29 10:41:40 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-29 12:01:46 +00:00
|
|
|
func nixBuildClosure(profile string, link string, nixpkgs string) (string, error) {
|
2022-11-29 10:41:40 +00:00
|
|
|
cmd := exec.Command(
|
|
|
|
"nix",
|
|
|
|
"--extra-experimental-features", "nix-command",
|
|
|
|
"--extra-experimental-features", "flakes",
|
|
|
|
"build",
|
|
|
|
"--out-link", link,
|
2022-11-29 12:01:46 +00:00
|
|
|
"--expr", fmt.Sprintf(closureNix, nixpkgs),
|
2022-11-29 10:41:40 +00:00
|
|
|
"--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
|
|
|
|
}
|