refactor progress
This commit is contained in:
258
fluxer_devops/livekitctl/internal/util/util.go
Normal file
258
fluxer_devops/livekitctl/internal/util/util.go
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/fluxerapp/fluxer/fluxer_devops/livekitctl/internal/errors"
|
||||
)
|
||||
|
||||
func Log(msg string) {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
func Logf(format string, args ...interface{}) {
|
||||
fmt.Printf(format+"\n", args...)
|
||||
}
|
||||
|
||||
func Which(binName string) string {
|
||||
path, err := exec.LookPath(binName)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
type RunOptions struct {
|
||||
Check bool
|
||||
Capture bool
|
||||
Env []string
|
||||
Cwd string
|
||||
}
|
||||
|
||||
type RunResult struct {
|
||||
ExitCode int
|
||||
Output string
|
||||
}
|
||||
|
||||
func Run(cmd []string, opts RunOptions) (*RunResult, error) {
|
||||
if len(cmd) == 0 {
|
||||
return nil, errors.NewCmdError("empty command", nil)
|
||||
}
|
||||
|
||||
c := exec.Command(cmd[0], cmd[1:]...)
|
||||
if opts.Cwd != "" {
|
||||
c.Dir = opts.Cwd
|
||||
}
|
||||
if len(opts.Env) > 0 {
|
||||
c.Env = append(os.Environ(), opts.Env...)
|
||||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
if opts.Capture {
|
||||
c.Stdout = &output
|
||||
c.Stderr = &output
|
||||
} else {
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
err := c.Run()
|
||||
exitCode := 0
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
exitCode = exitErr.ExitCode()
|
||||
} else {
|
||||
return nil, errors.NewCmdError(fmt.Sprintf("command not found: %s", cmd[0]), err)
|
||||
}
|
||||
}
|
||||
|
||||
result := &RunResult{
|
||||
ExitCode: exitCode,
|
||||
Output: output.String(),
|
||||
}
|
||||
|
||||
if opts.Check && exitCode != 0 {
|
||||
return result, errors.NewCmdError(
|
||||
fmt.Sprintf("command failed (%d): %v\n%s", exitCode, cmd, result.Output),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func RunSimple(cmd []string) error {
|
||||
_, err := Run(cmd, RunOptions{Check: true, Capture: false})
|
||||
return err
|
||||
}
|
||||
|
||||
func RunCapture(cmd []string) (string, error) {
|
||||
result, err := Run(cmd, RunOptions{Check: true, Capture: true})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.Output, nil
|
||||
}
|
||||
|
||||
func RunCaptureNoCheck(cmd []string) (string, int) {
|
||||
result, _ := Run(cmd, RunOptions{Check: false, Capture: true})
|
||||
if result == nil {
|
||||
return "", -1
|
||||
}
|
||||
return result.Output, result.ExitCode
|
||||
}
|
||||
|
||||
func AtomicWriteText(path string, content string, mode os.FileMode, uid, gid int) error {
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpFile, err := os.CreateTemp(dir, ".tmp-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpName := tmpFile.Name()
|
||||
|
||||
_, err = tmpFile.WriteString(content)
|
||||
if err != nil {
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpName)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tmpFile.Sync(); err != nil {
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpName)
|
||||
return err
|
||||
}
|
||||
|
||||
tmpFile.Close()
|
||||
|
||||
if err := os.Chmod(tmpName, mode); err != nil {
|
||||
os.Remove(tmpName)
|
||||
return err
|
||||
}
|
||||
|
||||
if uid >= 0 || gid >= 0 {
|
||||
if err := os.Chown(tmpName, uid, gid); err != nil {
|
||||
os.Remove(tmpName)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return os.Rename(tmpName, path)
|
||||
}
|
||||
|
||||
func ReadJSON(path string, v interface{}) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func WriteJSON(path string, v interface{}, mode os.FileMode, uid, gid int) error {
|
||||
data, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content := string(data) + "\n"
|
||||
return AtomicWriteText(path, content, mode, uid, gid)
|
||||
}
|
||||
|
||||
func NowRFC3339() string {
|
||||
return time.Now().UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func EnsureDir(path string, mode os.FileMode, uid, gid int) error {
|
||||
if err := os.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chmod(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if uid >= 0 || gid >= 0 {
|
||||
if err := os.Chown(path, uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserGroup struct {
|
||||
UID int
|
||||
GID int
|
||||
}
|
||||
|
||||
func LookupUserGroup(username string) *UserGroup {
|
||||
u, err := user.Lookup(username)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uid, err := strconv.Atoi(u.Uid)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
gid, err := strconv.Atoi(u.Gid)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &UserGroup{UID: uid, GID: gid}
|
||||
}
|
||||
|
||||
func FileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func CopyFile(src, dst string) error {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user