feat: initial implementation of run cmd
This commit is contained in:
parent
d408893389
commit
4d55a3edc2
1 changed files with 275 additions and 2 deletions
277
cmd/run.go
277
cmd/run.go
|
|
@ -1,6 +1,22 @@
|
|||
package cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.towk2.me/towk/makeshift/internal/archive"
|
||||
makeshift "git.towk2.me/towk/makeshift/pkg"
|
||||
"git.towk2.me/towk/makeshift/pkg/service"
|
||||
"git.towk2.me/towk/makeshift/pkg/storage"
|
||||
"git.towk2.me/towk/makeshift/pkg/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
|
|
@ -13,16 +29,273 @@ var runCmd = &cobra.Command{
|
|||
export MAKESHIFT_ROOT=/opt/makeshift
|
||||
|
||||
# run locally similar to 'download'
|
||||
makeshift run --plugins jinja2 --profiles default
|
||||
makeshift run -p help.txt --plugins jinja2 --profiles default
|
||||
makeshift run --root $HOME/apps/makeshift -p help.txt --plugins jinja2 --profiles default
|
||||
`,
|
||||
Args: cobra.NoArgs,
|
||||
Short: "Run locally with plugins and profiles",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
host, _ = cmd.Flags().GetString("host")
|
||||
path, _ = cmd.Flags().GetString("path")
|
||||
rootPath, _ = cmd.Flags().GetString("root")
|
||||
outputPath, _ = cmd.Flags().GetString("output")
|
||||
cacertPath, _ = cmd.Flags().GetString("cacert")
|
||||
keyfile, _ = cmd.Flags().GetString("keyfile")
|
||||
timeout, _ = cmd.Flags().GetInt("timeout")
|
||||
pluginNames, _ = cmd.Flags().GetStringSlice("plugins")
|
||||
profileIDs, _ = cmd.Flags().GetStringSlice("profiles")
|
||||
extract, _ = cmd.Flags().GetBool("extract")
|
||||
removeArchive, _ = cmd.Flags().GetBool("remove-archive")
|
||||
|
||||
contents []byte
|
||||
parsed *url.URL
|
||||
localServer *service.Service
|
||||
fileInfo os.FileInfo
|
||||
out *os.File
|
||||
store *storage.MemoryStorage = new(storage.MemoryStorage)
|
||||
hooks []makeshift.Hook
|
||||
errs []error
|
||||
err error
|
||||
)
|
||||
|
||||
// parse the host to remove scheme if needed
|
||||
parsed, err = url.Parse(host)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).
|
||||
Str("host", host).
|
||||
Msg("could not parse host")
|
||||
}
|
||||
|
||||
// set the server values
|
||||
localServer = service.New()
|
||||
localServer.Addr = parsed.Host
|
||||
localServer.RootPath = rootPath
|
||||
localServer.CACertFile = cacertPath
|
||||
localServer.CACertKeyfile = keyfile
|
||||
localServer.Timeout = time.Duration(timeout) * time.Second
|
||||
|
||||
// initialize storage
|
||||
store.Init()
|
||||
store.SetKWArgs(&pluginKWArgs)
|
||||
|
||||
// prepare the profiles
|
||||
errs = localServer.LoadProfiles(profileIDs, store, errs)
|
||||
if len(errs) > 0 {
|
||||
log.Error().
|
||||
Errs("errs", errs).
|
||||
Msg("errors occurred loading profiles")
|
||||
err = util.FormatErrors("failed to load plugins", "", errs)
|
||||
errs = []error{}
|
||||
}
|
||||
|
||||
// prepare the plugins
|
||||
// determine if path is directory, file, or exists
|
||||
if fileInfo, err = os.Stat(path); err == nil {
|
||||
if fileInfo.IsDir() {
|
||||
// get the final archive path
|
||||
archivePath := fmt.Sprintf("%s.tar.gz", path)
|
||||
|
||||
log.Debug().
|
||||
Str("archive_path", archivePath).
|
||||
Str("type", "directory").
|
||||
Msg("Service.Download()")
|
||||
|
||||
out, err = os.Create(archivePath)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("path", archivePath).
|
||||
Msg("failed to create named file")
|
||||
return
|
||||
}
|
||||
|
||||
// get a list of filenames to archive
|
||||
filenamesToArchive := []string{}
|
||||
filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
|
||||
if !d.IsDir() {
|
||||
filenamesToArchive = append(filenamesToArchive, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
log.Debug().Strs("files", filenamesToArchive).Send()
|
||||
|
||||
// prepare plugins
|
||||
hooks, errs = localServer.LoadPlugins(pluginNames, store, pluginArgs, errs)
|
||||
if len(errs) > 0 {
|
||||
log.Error().
|
||||
Errs("errs", errs).
|
||||
Strs("plugins", pluginNames).
|
||||
Strs("args", pluginArgs).
|
||||
Msg("errors occurred loading plugins")
|
||||
errs = []error{}
|
||||
return
|
||||
}
|
||||
|
||||
// create an archive of the directory, run hooks, and download
|
||||
err = archive.Create(filenamesToArchive, out, hooks)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("path", archivePath).
|
||||
Msg("failed to create archive")
|
||||
return
|
||||
}
|
||||
|
||||
// load the final archive
|
||||
contents, err = os.ReadFile(archivePath)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("path", archivePath).
|
||||
Msg("failed to read archive contents")
|
||||
return
|
||||
}
|
||||
|
||||
// clean up the temporary archive
|
||||
err = os.Remove(archivePath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to remove temporary archive")
|
||||
return
|
||||
}
|
||||
|
||||
// extract files if '-x' flag is passed
|
||||
if extract {
|
||||
var (
|
||||
dir = filepath.Dir(outputPath)
|
||||
base = strings.TrimSuffix(filepath.Base(outputPath), ".tar.gz")
|
||||
)
|
||||
err = archive.Expand(outputPath, fmt.Sprintf("%s/%s", dir, base))
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("path", outputPath).
|
||||
Msg("failed to expand archive")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// optionally, remove archive if '-r' flag is passed
|
||||
// NOTE: this can only be used if `-x` flag is set
|
||||
if removeArchive {
|
||||
if !extract {
|
||||
log.Warn().Msg("requires '-x/--extract' flag to be set to 'true'")
|
||||
} else {
|
||||
err = os.Remove(outputPath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("path", outputPath).
|
||||
Msg("failed to remove archive")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// download individual file
|
||||
log.Debug().
|
||||
Str("type", "file").
|
||||
Msg("Service.Download()")
|
||||
|
||||
contents, err = os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to read file to download")
|
||||
return
|
||||
}
|
||||
|
||||
// prepare plugins
|
||||
store.Set("file", contents)
|
||||
hooks, errs = localServer.LoadPlugins(pluginNames, store, pluginArgs, errs)
|
||||
if len(errs) > 0 {
|
||||
log.Error().
|
||||
Strs("plugins", pluginNames).
|
||||
Strs("args", pluginArgs).
|
||||
Errs("errs", errs).
|
||||
Msg("errors occurred loading plugins")
|
||||
errs = []error{}
|
||||
return
|
||||
}
|
||||
if len(hooks) > 0 {
|
||||
// run pre-hooks to modify the contents of the file before archiving
|
||||
log.Debug().Int("hook_count", len(hooks)).Msg("running hooks")
|
||||
for _, hook := range hooks {
|
||||
log.Debug().Any("hook", map[string]any{
|
||||
"store": hook.Data,
|
||||
"args": hook.Args,
|
||||
"plugin": map[string]string{
|
||||
"name": hook.Plugin.Name(),
|
||||
"description": hook.Plugin.Description(),
|
||||
"version": hook.Plugin.Version(),
|
||||
},
|
||||
}).Send()
|
||||
err = hook.Init()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("plugin", hook.Plugin.Name()).
|
||||
Msg("failed to initialize plugin")
|
||||
continue
|
||||
}
|
||||
err = hook.Run()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("plugin", hook.Plugin.Name()).
|
||||
Msg("failed to run plugin")
|
||||
continue
|
||||
}
|
||||
err = hook.Cleanup()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("plugin", hook.Plugin.Name()).
|
||||
Msg("failed to cleanup plugin")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// take the contents from the last hook and update files
|
||||
var (
|
||||
hook = hooks[len(hooks)-1]
|
||||
data any
|
||||
)
|
||||
data, err = hook.Data.Get("out")
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("plugin", hook.Plugin.Name()).
|
||||
Msg("failed to get data from hook")
|
||||
return
|
||||
}
|
||||
|
||||
// write to file if '-o' specified otherwise stdout
|
||||
if outputPath != "" {
|
||||
writeFiles(outputPath, data.([]byte))
|
||||
log.Debug().Str("path", outputPath).Msg("wrote file to specified path")
|
||||
} else {
|
||||
fmt.Println(string(data.([]byte)))
|
||||
}
|
||||
|
||||
} else {
|
||||
// write contents to file
|
||||
// send non-processed file back as response
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
runCmd.PersistentFlags().String("host", "http://localhost:5050", "Set the makeshift remote host (can be set with MAKESHIFT_HOST)")
|
||||
runCmd.PersistentFlags().StringP("output", "o", "", "Set the output path to write files")
|
||||
runCmd.PersistentFlags().String("cacert", "", "Set the CA certificate path to load")
|
||||
runCmd.Flags().StringP("path", "p", ".", "Set the path to list files (can be set with MAKESHIFT_PATH)")
|
||||
runCmd.Flags().StringSlice("profiles", []string{}, "Set the profile(s) to use to populate data store")
|
||||
runCmd.Flags().StringSlice("plugins", []string{}, "Set the plugin(s) to run before downloading files")
|
||||
runCmd.Flags().StringSlice("plugin-args", []string{}, "Set the argument list to pass to plugin(s)")
|
||||
runCmd.Flags().Var(&pluginKWArgs, "plugin-kwargs", "Set the argument map to pass to plugin(s)")
|
||||
runCmd.Flags().BoolP("extract", "x", false, "Set whether to extract archive locally after downloading")
|
||||
runCmd.Flags().BoolP("remove-archive", "r", false, "Set whether to remove the archive after extracting (used with '--extract' flag)")
|
||||
rootCmd.AddCommand(runCmd)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue