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_send_cmd_path, err := filepath.Abs(filepath.Join(instance, "exec", "bin", "bnd-send-cmd")) contents := "#!/usr/bin/env bash\n" + bnd_send_cmd_path + " " + instance + " \"{\\\"command\\\": \\\"run\\\", \\\"package\\\": \\\"" + program + "\\\", \\\"workdir\\\": \\\"$PWD\\\", \\\"arguments\\\": \\\"$@\\\"}\"\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") } }