diff --git a/cmd/root.go b/cmd/root.go index f5b1fc6..fb354f0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -118,12 +118,3 @@ func handleResponseError(res *http.Response, host, query string, err error) { os.Exit(1) } } - -// helper to write downloaded files -func writeFiles(path string, body []byte) { - var err = os.WriteFile(path, body, 0o755) - if err != nil { - log.Error().Err(err).Msg("failed to write file(s) from download") - os.Exit(1) - } -} diff --git a/cmd/run.go b/cmd/run.go index 846e560..ab698e7 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -1,22 +1,6 @@ package cmd -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" -) +import "github.com/spf13/cobra" var runCmd = &cobra.Command{ Use: "run", @@ -29,273 +13,16 @@ var runCmd = &cobra.Command{ export MAKESHIFT_ROOT=/opt/makeshift # run locally similar to 'download' - makeshift run -p help.txt --plugins jinja2 --profiles default + makeshift run --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) } diff --git a/pkg/service/routes.go b/pkg/service/routes.go index e4fdb04..1cbfc7f 100644 --- a/pkg/service/routes.go +++ b/pkg/service/routes.go @@ -55,7 +55,7 @@ func (s *Service) Download() http.HandlerFunc { Msg("Service.Download()") // prepare profiles - errs = s.LoadProfiles(profileIDs, store, errs) + errs = s.loadProfiles(profileIDs, store, errs) if len(errs) > 0 { log.Error(). Errs("errs", errs). @@ -93,7 +93,7 @@ func (s *Service) Download() http.HandlerFunc { log.Debug().Strs("files", filenamesToArchive).Send() // prepare plugins - hooks, errs = s.LoadPlugins(pluginNames, store, pluginArgs, errs) + hooks, errs = s.loadPlugins(pluginNames, store, pluginArgs, errs) if len(errs) > 0 { log.Error(). Errs("errs", errs). @@ -143,7 +143,7 @@ func (s *Service) Download() http.HandlerFunc { // prepare plugins store.Set("file", contents) - hooks, errs = s.LoadPlugins(pluginNames, store, pluginArgs, errs) + hooks, errs = s.loadPlugins(pluginNames, store, nil, errs) if len(errs) > 0 { log.Error(). Errs("errs", errs). @@ -329,7 +329,7 @@ func (s *Service) GetStatus(w http.ResponseWriter, r *http.Request) { } } -func (s *Service) LoadProfiles(profileIDs []string, store storage.KVStore, errs []error) []error { +func (s *Service) loadProfiles(profileIDs []string, store storage.KVStore, errs []error) []error { // check for special case profile ID (e.g. '*' or 'all') useAll := slices.ContainsFunc(profileIDs, func(profileID string) bool { return profileID == "*" || profileID == "all" @@ -381,7 +381,7 @@ func (s *Service) LoadProfiles(profileIDs []string, store storage.KVStore, errs return errs } -func (s *Service) LoadPlugins(pluginNames []string, store storage.KVStore, args []string, errs []error) ([]makeshift.Hook, []error) { +func (s *Service) loadPlugins(pluginNames []string, store storage.KVStore, args []string, errs []error) ([]makeshift.Hook, []error) { // create hooks to run from provided plugins specified var hooks []makeshift.Hook for i, pluginName := range pluginNames {