makeshift/pkg/util/util.go

162 lines
3.8 KiB
Go

package util
import (
"archive/tar"
"bytes"
"cmp"
"compress/gzip"
"crypto/tls"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"slices"
"strings"
)
// Wrapper function to simplify checking if a path exists.
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
// Wrapper function to simplify checking if a path is a directory.
func IsDirectory(path string) (bool, error) {
// This returns an *os.FileInfo type
fileInfo, err := os.Stat(path)
if err != nil {
return false, fmt.Errorf("failed to stat path (%s): %v", path, err)
}
// IsDir is short for fileInfo.Mode().IsDir()
return fileInfo.IsDir(), nil
}
// Wrapper function to confine making a HTTP request into a single function
// instead of multiple.
func MakeRequest(url string, httpMethod string, body []byte, headers map[string]string) (*http.Response, []byte, error) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
req, err := http.NewRequest(httpMethod, url, bytes.NewBuffer(body))
if err != nil {
return nil, nil, fmt.Errorf("could not create new HTTP request: %v", err)
}
req.Header.Add("User-Agent", "configurator")
for k, v := range headers {
req.Header.Add(k, v)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, nil, fmt.Errorf("could not make request: %v", err)
}
b, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return nil, nil, fmt.Errorf("could not read response body: %v", err)
}
return res, b, err
}
// Returns the git commit string by executing command.
// NOTE: This currently requires git to be installed.
// TODO: Change how this is done to not require executing a command.
func GitCommit() string {
c := exec.Command("git", "rev-parse", "--short=8", "HEAD")
stdout, err := c.Output()
if err != nil {
return ""
}
return strings.TrimRight(string(stdout), "\n")
}
// General function to remove element by a given index.
// NOTE: would it be better to use slices.DeleteFunc instead?
func RemoveIndex[T comparable](s []T, index int) []T {
ret := make([]T, 0)
ret = append(ret, s[:index]...)
return append(ret, s[index+1:]...)
}
func RemoveDuplicates[T cmp.Ordered](s []T) []T {
slices.Sort(s)
return slices.Compact(s)
}
// General function to copy elements from slice if condition is true.
func CopyIf[T comparable](s []T, condition func(t T) bool) []T {
var f = make([]T, 0)
for _, e := range s {
if condition(e) {
f = append(f, e)
}
}
return f
}
func CreateArchive(files []string, buf io.Writer) error {
// Create new Writers for gzip and tar
// These writers are chained. Writing to the tar writer will
// write to the gzip writer which in turn will write to
// the "buf" writer
gw := gzip.NewWriter(buf)
defer gw.Close()
tw := tar.NewWriter(gw)
defer tw.Close()
// Iterate over files and add them to the tar archive
for _, file := range files {
err := addToArchive(tw, file)
if err != nil {
return err
}
}
return nil
}
func addToArchive(tw *tar.Writer, filename string) error {
// open file to write to archive
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
// get FileInfo for file size, mode, etc.
info, err := file.Stat()
if err != nil {
return err
}
// create a tar Header from the FileInfo data
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
// use full path as name (FileInfoHeader only takes the basename) to
// preserve directory structure
// see for more info: https://golang.org/src/archive/tar/common.go?#L626
header.Name = filename
// Write file header to the tar archive
err = tw.WriteHeader(header)
if err != nil {
return err
}
// copy file content to tar archive
_, err = io.Copy(tw, file)
if err != nil {
return err
}
return nil
}