bndd/install.go

218 lines
4.7 KiB
Go

package main
import (
"encoding/json"
"errors"
"io/fs"
"net"
"os"
"path/filepath"
"github.com/mholt/archiver/v4"
)
func installMap(conn net.Conn, instance string, objmap map[string]interface{}) error {
package_path, has := objmap["path"]
if !has {
return errors.New("a path is required")
}
return install(conn, instance, package_path.(string))
}
func install(conn net.Conn, instance string, package_path string) error {
unpack_path, err := filepath.Abs(filepath.Join("/tmp", "boundaries"))
if err != nil {
return err
}
if exists, _ := path_exists(unpack_path); exists {
err = os.RemoveAll(unpack_path)
if err != nil {
return err
}
}
package_path, err = filepath.Abs(package_path)
if err != nil {
return err
}
fsys, err := archiver.FileSystem(nil, package_path, nil)
if err != nil {
return err
}
err = os.CopyFS(unpack_path, fsys)
if err != nil {
return err
}
dir, _, err := find_infofile(unpack_path)
if err != nil {
return err
}
info_path := filepath.Join(dir, "boundaries.json")
infofile_content, err := os.ReadFile(info_path)
if err != nil {
return err
}
var info map[string]interface{}
err = json.Unmarshal(infofile_content, &info)
if err != nil {
return err
}
package_name, has := info["name"]
if !has {
return errors.New("infofile does not contain name field")
}
package_path, err = filepath.Abs(filepath.Join(instance, "apps", package_name.(string)))
if err != nil {
return err
}
if exists, _ := path_exists(package_path); exists {
err := remove(conn, instance, package_name.(string), true)
if err != nil {
return err
}
}
err = os.CopyFS(package_path, os.DirFS(dir))
if err != nil {
return err
}
err = os.MkdirAll(filepath.Join(instance, "var", package_name.(string)), 0755)
if err != nil {
return err
}
commands, has := info["command"]
if !has {
return errors.New("infofile does not contain command field")
}
targets := make(map[string]string)
for target, command := range commands.(map[string]interface{}) {
if target == "install" {
return_code, err := run_command(conn, command.(string), package_path, nil)
if err != nil {
return err
}
if return_code != 0 {
return errors.New("install script failed")
}
}
targets[target] = command.(string)
}
if _, has := targets["run"]; !has {
return errors.New("run target not found")
}
bin_files, has := info["bin"]
if has {
install_wrapped_bin, has := info["wrap_bin"]
if !has {
install_wrapped_bin = true
}
switch bin_files.(type) {
case string:
if install_wrapped_bin.(bool) {
err = install_bin(instance, bin_files.(string), "run", package_name.(string))
} else {
err = install_unwrapped_bin(instance, bin_files.(string), filepath.Join(package_path, targets["run"]))
}
if err != nil {
return err
}
case map[string]interface{}:
for target, path := range bin_files.(map[string]interface{}) {
if install_wrapped_bin.(bool) {
err = install_bin(instance, path.(string), target, package_name.(string))
} else {
err = install_unwrapped_bin(instance, path.(string), filepath.Join(package_path, targets[target]))
}
if err != nil {
return err
}
}
default:
return errors.New("unknown type for bin")
}
}
return nil
}
func install_bin(instance string, path string, target string, program string) error {
bin_path, err := filepath.Abs(filepath.Join(instance, "exec", "bin", path))
if err != nil {
return err
}
if exists, err := path_exists(bin_path); exists && err == nil {
err = os.Remove(bin_path)
if err != nil {
return err
}
}
bnd_run_path, err := filepath.Abs(filepath.Join(instance, "exec", "bin", "bnd-run"))
contents := "#!/usr/bin/env bash\nexec " + bnd_run_path + " -i " + instance + " -t " + target + " -w $PWD " + program + " $@\n"
err = os.WriteFile(bin_path, []byte(contents), 0755)
if err != nil {
return err
}
return nil
}
func install_unwrapped_bin(instance string, path string, source string) error {
bin_path, err := filepath.Abs(filepath.Join(instance, "exec", "bin", path))
if err != nil {
return err
}
if exists, err := path_exists(bin_path); exists && err == nil {
err = os.Remove(bin_path)
if err != nil {
return err
}
}
os.Symlink(source, bin_path)
return nil
}
func find_infofile(source string) (string, string, error) {
var found_file string = ""
var found_dir string = ""
fs.WalkDir(os.DirFS(source), ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
dir, file := filepath.Split(filepath.Join(source, path))
if file == "boundaries.json" {
found_file = file
found_dir = dir
}
return nil
})
if found_dir != "" && found_file != "" {
return found_dir, found_file, nil
} else {
return "", "", errors.New("no infofile found")
}
}