diff --git a/pkg/client/client.go b/pkg/client/client.go index d338fd7..f9cf5d9 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -8,7 +8,7 @@ import ( "os" "strings" - "git.towk2.me/towk/configurator/pkg/util" + "git.towk2.me/towk/makeshift/pkg/util" "github.com/cavaliergopher/grab/v3" ) diff --git a/pkg/service/constants.go b/pkg/service/constants.go index 52e990a..07e0ed2 100644 --- a/pkg/service/constants.go +++ b/pkg/service/constants.go @@ -4,48 +4,67 @@ const ( RELPATH_PLUGINS = "/plugins" RELPATH_PROFILES = "/profiles" RELPATH_DATA = "/data" - RELPATH_METADATA = "/.configurator" + RELPATH_METADATA = "/.makeshift" RELPATH_HELP = RELPATH_DATA + "/index.html" + RELPATH_PROFILE = RELPATH_PROFILES + "/default.json" + + PATH_CONFIG = "$HOME/.config/makeshift/config.yaml" DEFAULT_TIMEOUT_IN_SECS = 60 - DEFAULT_PLUGINS_MAX_COUNT = 64 + DEFAULT_PLUGINS_MAX_COUNT = 32 DEFAULT_PROFILES_MAX_COUNT = 256 - DEFAULT_METADATA = `` - DEFAULT_HOME = ` + + FILE_METADATA = `` + FILE_HOME_PAGE = `

# setup environment variables
- export CONFIGURATOR_HOST={{ configurator.host }}
- export CONFIGURATOR_PATH={{ configurator.path }}
- export CONFIGURATOR_SERVER_ROOT={{ configurator.server_root }}
+ export MAKESHIFT_HOST={{ makeshift.host }}
+ export MAKESHIFT_PATH={{ makeshift.path }}
+ export MAKESHIFT_SERVER_ROOT={{ makeshift.server.root }}

# start the service
- configurator serve --root $HOME/apps/configurator/server --init
+ makeshift serve --root $HOME/apps/makeshift/server --init

# download a file or directory (as archive)
- configurator download
- configurator download --host http://localhost:5050 --path help.txt
+ makeshift download
+ makeshift download --host http://localhost:5050 --path help.txt

# download files with rendering using plugins
- configurator download --plugins smd,jinja2 --profile compute
- curl $CONFIGURATOR_HOST/download/help.txt?plugins=smd,jinja2
+ makeshift download --plugins smd,jinja2 --profile compute
+ curl $MAKESHIFT_HOST/download/help.txt?plugins=smd,jinja2

# upload a file or directory (recursively)
- configurator upload
- configurator upload --host http://localhost:5050 --path help.txt
+ makeshift upload
+ makeshift upload --host http://localhost:5050 --path help.txt

# list the files in a directory
- configurator list --path help.txt
- configurator list --host http://localhost:5050 --path help.txt
+ makeshift list --path help.txt
+ makeshift list --host http://localhost:5050 --path help.txt
curl http://localhost:5050/list/test

+` + FILE_DEFAULT_PROFILE = ` +{ + "id": "default", + "description": "Makeshift default profile", + "data": { + "makeshift": { + "host": "localhost", + "path": "/test", + "server": { + "root": "/test" + } + } + } +} ` ) -// configurator.host: https://example.com -// configurator.path: test -// configurator.server_root: $HOME/apps/configurator +// makeshift.host: https://localhost:5050 +// makeshift.path: test +// makeshift.server.root: $HOME/apps/makeshift diff --git a/pkg/service/plugins.go b/pkg/service/plugins.go index c3a6c43..5b00216 100644 --- a/pkg/service/plugins.go +++ b/pkg/service/plugins.go @@ -5,14 +5,15 @@ import ( "io" "net/http" "os" + "strings" - configurator "git.towk2.me/towk/configurator/pkg" + makeshift "git.towk2.me/towk/makeshift/pkg" ) func (s *Service) ListPlugins() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( - plugins map[string]configurator.Plugin + plugins map[string]makeshift.Plugin names []string body []byte err error @@ -38,10 +39,35 @@ func (s *Service) ListPlugins() http.HandlerFunc { } } +func (s *Service) GetPlugin() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + pluginName = strings.TrimPrefix(r.URL.Path, "/plugin") + plugin makeshift.Plugin + body []byte + err error + ) + + plugin, err = LoadPluginFromFile(pluginName) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + body, err = json.Marshal(plugin) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Write(body) + } +} + func (s *Service) CreatePlugin() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( - plugin configurator.Plugin + plugin makeshift.Plugin path string err error ) @@ -79,7 +105,7 @@ func (s *Service) DeletePlugin() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( path string - plugin configurator.Plugin + plugin makeshift.Plugin err error ) @@ -100,9 +126,9 @@ func (s *Service) DeletePlugin() http.HandlerFunc { } } -func getPluginFromRequestBody(r *http.Request) (configurator.Plugin, error) { +func getPluginFromRequestBody(r *http.Request) (makeshift.Plugin, error) { var ( - plugin configurator.Plugin + plugin makeshift.Plugin body []byte err error ) diff --git a/pkg/service/profiles.go b/pkg/service/profiles.go new file mode 100644 index 0000000..08aeff7 --- /dev/null +++ b/pkg/service/profiles.go @@ -0,0 +1,282 @@ +package service + +import ( + "encoding/json" + "fmt" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + + makeshift "git.towk2.me/towk/makeshift/pkg" + "github.com/go-chi/chi/v5" + "github.com/tidwall/sjson" +) + +func (s *Service) ListProfiles() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + path = s.RootPath + RELPATH_PROFILES + profiles []*makeshift.Profile + contents []byte + err error + ) + + // walk profiles directory to load all profiles + err = filepath.Walk(path, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + // skip directories + if info.IsDir() { + return nil + } + + // read file contents + var profile *makeshift.Profile + profile, err = LoadProfileFromFile(path) + if err != nil { + return err + } + + profiles = append(profiles, profile) + + fmt.Println(path, info.Size()) + return nil + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + // marshal and send all the profiles + contents, err = json.Marshal(profiles) + if err != nil { + http.Error(w, fmt.Sprintf("failed to marshal profiles: %v", err), http.StatusInternalServerError) + } + + _, err = w.Write(contents) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +// func (s *Service) CreateProfiles() http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// var ( +// path = chi.URLParam(r, "path") +// err error +// ) + +// } +// } + +func (s *Service) GetProfile() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + id = chi.URLParam(r, "id") + path = s.PathForProfileWithID(id) + contents []byte + err error + ) + + contents, err = loadProfileContents(path) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + _, err = w.Write(contents) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +func (s *Service) CreateProfile() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + type input struct { + Path string `json:"path"` + Profile *makeshift.Profile `json:"profile"` + } + var ( + body, contents []byte + in input + err error + ) + + body, err = io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // use the request info to build profile + err = json.Unmarshal(body, &in) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // serialize just the profile part + contents, err = json.Marshal(in.Profile) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // create a new profile on disk + err = os.WriteFile(in.Path, contents, os.ModePerm) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusOK) + } +} + +func (s *Service) SetProfileData() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + body, contents []byte + newContents string + profile *makeshift.Profile + path string + err error + ) + + body, err = io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = json.Unmarshal(body, &profile) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // make sure the request data sets an ID + if profile.ID == "" { + http.Error(w, "ID must be set to a non-empty value", http.StatusBadRequest) + return + } + + // read the contents the file with profile ID + path = s.PathForProfileWithID(profile.ID) + contents, err = os.ReadFile(path) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // modify the data of the profile's contents + newContents, err = sjson.Set(string(contents), "data", profile.Data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // write only the data to the file with ID + err = os.WriteFile(path, []byte(newContents), os.ModePerm) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +func (s *Service) DeleteProfileData() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + id = chi.URLParam(r, "id") + path = s.PathForProfileWithID(id) + profile *makeshift.Profile + err error + ) + + // get the profile + profile, err = LoadProfileFromFile(path) + if err != nil { + http.Error(w, err.Error(), http.StatusBadGateway) + return + } + + // delete the profile data + profile.Data = map[string]any{} + + // save the profile back to the file to update + SaveProfileToFile(path, profile) + + } +} + +func (s *Service) GetProfileData() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var ( + id = chi.URLParam(r, "id") + path = s.PathForProfileWithID(id) + profile *makeshift.Profile + body []byte + err error + ) + + profile, err = LoadProfileFromFile(path) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // only marshal the profile data and not entire profile + body, err = json.Marshal(profile.Data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // write body to response + _, err = w.Write(body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +// func (s *Service) CreateProfilePath() http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { + +// w.WriteHeader(http.StatusOK) +// } +// } + +// func (s *Service) DeleteProfilePath() http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) {} +// } + +// func (s *Service) GetProfilePath() http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) {} +// } + +func loadProfileContents(path string) ([]byte, error) { + var ( + contents []byte + profile *makeshift.Profile + err error + ) + profile, err = LoadProfileFromFile(path) + if err != nil { + return nil, fmt.Errorf("failed to load profile from file: %v", err) + } + + contents, err = json.Marshal(profile) + if err != nil { + return nil, fmt.Errorf("failed to marshal profile: %v", err) + } + return contents, nil +} diff --git a/pkg/service/routes.go b/pkg/service/routes.go index eb6f6cc..375fbd0 100644 --- a/pkg/service/routes.go +++ b/pkg/service/routes.go @@ -10,33 +10,55 @@ import ( "strings" "time" - "git.towk2.me/towk/configurator/pkg/util" + "git.towk2.me/towk/makeshift/internal/archive" + makeshift "git.towk2.me/towk/makeshift/pkg" + "git.towk2.me/towk/makeshift/pkg/storage" "github.com/rs/zerolog/log" ) func (s *Service) Download() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( - path = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/download") + path = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/download") + pluginNames = r.URL.Query()["plugins"] + profileIDs = r.URL.Query()["profiles"] + fileInfo os.FileInfo out *os.File + store *storage.MemoryStorage = new(storage.MemoryStorage) + hooks []makeshift.Hook contents []byte + errs []error err error ) + // initialize storage + store.Init() + log.Debug(). Str("path", path). Str("client_host", r.Host). + Strs("plugins", pluginNames). + Strs("profiles", profileIDs). + Any("query", r.URL.Query()). Msg("Service.Download()") + // prepare profiles + errs = s.loadProfiles(profileIDs, store, errs) + if len(errs) > 0 { + log.Error().Errs("errs", errs).Msg("errors occurred loading profiles") + errs = []error{} + } + // determine if path is directory, file, or exists if fileInfo, err = os.Stat(path); err == nil { if fileInfo.IsDir() { - // create an archive of the directory and download + log.Debug(). Str("type", "directory"). Msg("Service.Download()") + // get the final archive path archivePath := fmt.Sprintf("%d.tar.gz", time.Now().Unix()) out, err = os.Create(archivePath) if err != nil { @@ -44,28 +66,40 @@ func (s *Service) Download() http.HandlerFunc { return } - filesToArchive := []string{} + // get a list of filenames to archive + filenamesToArchive := []string{} filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { if !d.IsDir() { - filesToArchive = append(filesToArchive, path) + filenamesToArchive = append(filenamesToArchive, path) } return nil }) - log.Debug().Strs("files", filesToArchive).Send() - err = util.CreateArchive(filesToArchive, out) + log.Debug().Strs("files", filenamesToArchive).Send() + + // prepare plugins + hooks, errs = s.loadPlugins(pluginNames, store, nil, errs) + if len(errs) > 0 { + log.Error().Errs("errs", errs).Msg("errors occurred loading plugins") + errs = []error{} + } + + // create an archive of the directory, run hooks, and download + err = archive.Create(filenamesToArchive, out, hooks) if err != nil { s.writeErrorResponse(w, fmt.Sprintf("failed to create archive: %v", err.Error()), http.StatusInternalServerError) return } + // load the final archive contents, err = os.ReadFile(archivePath) if err != nil { - s.writeErrorResponse(w, fmt.Sprintf("failed to read archive: %v", err.Error()), http.StatusInternalServerError) + s.writeErrorResponse(w, fmt.Sprintf("failed to read archive contents: %v", err.Error()), http.StatusInternalServerError) return } w.Write(contents) + // clean up the temporary archive err = os.Remove(archivePath) if err != nil { log.Error().Err(err).Msg("failed to remove temporary archive") @@ -77,12 +111,53 @@ func (s *Service) Download() http.HandlerFunc { log.Debug(). Str("type", "file"). Msg("Service.Download()") + contents, err = os.ReadFile(path) if err != nil { - s.writeErrorResponse(w, fmt.Sprintf("failed to read file to download: %v", err.Error()), http.StatusInternalServerError) + s.writeErrorResponse(w, fmt.Sprintf("failed to read file to download: %v", err), http.StatusInternalServerError) return } - w.Write(contents) + + // prepare plugins + + store.Set("file", contents) + hooks, errs = s.loadPlugins(pluginNames, store, nil, errs) + if len(errs) > 0 { + log.Error().Errs("errs", errs).Msg("errors occurred loading plugins") + errs = []error{} + } + + // 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{ + "data": hook.Data, + "args": hook.Args, + "plugin": map[string]string{ + "name": hook.Plugin.Name(), + "description": hook.Plugin.Description(), + "version": hook.Plugin.Version(), + }, + }).Send() + err = hook.Run() + if err != nil { + log.Error().Err(err).Str("plugin", hook.Plugin.Name()).Msg("failed to run 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 { + s.writeErrorResponse(w, fmt.Sprintf("failed to get data from hook: %v", err), http.StatusInternalServerError) + return + } + + w.Write([]byte(data.(string))) } } else { s.writeErrorResponse(w, err.Error(), http.StatusBadRequest) @@ -150,3 +225,70 @@ func (s *Service) GetStatus(w http.ResponseWriter, r *http.Request) { return } } + +func (s *Service) loadProfiles(profileIDs []string, store storage.KVStore, errs []error) []error { + // load data from profiles into the data store + for i, profileID := range profileIDs { + var ( + profilePath = s.PathForProfileWithID(profileID) + profile *makeshift.Profile + err error + ) + if i > DEFAULT_PROFILES_MAX_COUNT { + log.Warn().Msg("max profiles count reached...stopping") + return errs + } + if profileID == "" { + log.Warn().Msg("profile ID is empty...skipping") + continue + } + log.Debug(). + Str("id", profileID). + Str("path", profilePath). + Msg("load profile") + profile, err = LoadProfileFromFile(profilePath) + if err != nil { + errs = append(errs, err) + continue + } + store.Set(profileID, profile) + } + + return errs +} + +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 { + var ( + pluginPath string = s.PathForPluginWithName(pluginName) + plugin makeshift.Plugin + err error + ) + if i > DEFAULT_PLUGINS_MAX_COUNT { + log.Warn().Msg("max plugins count reached...stopping") + return hooks, errs + } + if pluginName == "" { + log.Warn().Msg("plugin name is empty...skipping") + continue + } + log.Debug(). + Str("name", pluginName). + Str("path", pluginPath). + Msg("load plugin") + // load the plugin from disk + plugin, err = LoadPluginFromFile(pluginPath) + if err != nil { + errs = append(errs, err) + continue + } + hooks = append(hooks, makeshift.Hook{ + Data: store, + Args: args, + Plugin: plugin, + }) + } + return hooks, errs +} diff --git a/pkg/service/service.go b/pkg/service/service.go index 528cdac..26d66ea 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -11,10 +11,10 @@ import ( "slices" "time" - configurator "git.towk2.me/towk/configurator/pkg" - "git.towk2.me/towk/configurator/pkg/util" - "github.com/go-chi/chi/middleware" + makeshift "git.towk2.me/towk/makeshift/pkg" + "git.towk2.me/towk/makeshift/pkg/util" "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" "github.com/rs/zerolog/log" ) @@ -35,9 +35,9 @@ func New() *Service { Addr: ":5050", RootPath: "./", Environment: map[string]string{ - "CONFIGURATOR_HOST": "", - "CONFIGURATOR_ROOT": "", - "ACCESS_TOKEN": "", + "MAKESHIFT_HOST": "", + "MAKESHIFT_ROOT": "", + "ACCESS_TOKEN": "", }, PluginsMaxCount: DEFAULT_PLUGINS_MAX_COUNT, ProfilesMaxCount: DEFAULT_PROFILES_MAX_COUNT, @@ -63,22 +63,27 @@ func (s *Service) Init() error { } err = os.MkdirAll(s.PathForData(), 0o777) if err != nil { - return fmt.Errorf("failed to make service profile path: %v", err) + return fmt.Errorf("failed to make service data path: %v", err) } // create the default files - err = os.WriteFile(s.PathForMetadata(), []byte(DEFAULT_METADATA), 0o777) + err = os.WriteFile(s.PathForMetadata(), []byte(FILE_METADATA), 0o777) if err != nil { return fmt.Errorf("failed to make service metadata file: %v", err) } - err = os.WriteFile(s.PathForHome(), []byte(DEFAULT_HOME), 0o777) + err = os.WriteFile(s.PathForHome(), []byte(FILE_HOME_PAGE), 0o777) if err != nil { - return fmt.Errorf("failed to make service metadata file: %v", err) + return fmt.Errorf("failed to make service home page file: %v", err) } + err = os.WriteFile(s.PathForProfileWithID("default"), []byte(FILE_DEFAULT_PROFILE), 0o777) + if err != nil { + return fmt.Errorf("failed to make service default profile file: %v", err) + } + return nil } -// Serve() starts the configurator service and waits for requests. +// Serve() starts the makeshift service and waits for requests. func (s *Service) Serve() error { router := chi.NewRouter() router.Use(middleware.RequestID) @@ -98,7 +103,7 @@ func (s *Service) Serve() error { router.Get("/list/*", s.List()) // profiles - router.Get("/profiles", s.GetProfiles()) + router.Get("/profiles", s.ListProfiles()) // router.Post("/profiles", s.CreateProfiles()) router.Get("/profile/{id}", s.GetProfile()) router.Post("/profile/{id}", s.CreateProfile()) @@ -111,8 +116,9 @@ func (s *Service) Serve() error { // plugins router.Get("/plugins", s.ListPlugins()) - router.Post("/plugins", s.CreatePlugin()) - router.Delete("/plugins/{id}", s.DeletePlugin()) + router.Get("/plugin/{name}", s.GetPlugin()) + router.Post("/plugin/{name}", s.CreatePlugin()) + router.Delete("/plugin/{name}", s.DeletePlugin()) } // always available public routes go here @@ -128,22 +134,23 @@ func (s *Service) FetchJwks(uri string) { } -func LoadProfileFromFile(path string) (*Profile, error) { - return loadFromJSONFile[Profile](path) +func LoadProfileFromFile(path string) (*makeshift.Profile, error) { + return loadFromJSONFile[makeshift.Profile](path) } // LoadPluginFromFile loads a single plugin given a single file path -func LoadPluginFromFile(path string) (configurator.Plugin, error) { +func LoadPluginFromFile(path string) (makeshift.Plugin, error) { var ( isDir bool err error loadedPlugin *plugin.Plugin ) // skip loading plugin if path is a directory with no error - if isDir, err = util.IsDirectory(path); err == nil && isDir { - return nil, nil - } else if err != nil { + isDir, err = util.IsDirectory(path) + if err != nil { return nil, fmt.Errorf("failed to test if plugin path is directory: %v", err) + } else if isDir { + return nil, fmt.Errorf("path is a directory") } // try and open the plugin @@ -153,15 +160,15 @@ func LoadPluginFromFile(path string) (configurator.Plugin, error) { } // load the "Target" symbol from plugin - symbol, err := loadedPlugin.Lookup("Target") + symbol, err := loadedPlugin.Lookup("Makeshift") if err != nil { return nil, fmt.Errorf("failed to look up symbol at path '%s': %v", path, err) } - // assert that the plugin is a valid configurator.Plugin - target, ok := symbol.(configurator.Plugin) + // assert that the plugin is a valid makeshift.Plugin + target, ok := symbol.(makeshift.Plugin) if !ok { - return nil, fmt.Errorf("failed to load the correct symbol type at path '%s'", path) + return nil, fmt.Errorf("failed to assert the correct symbol type at path '%s'", path) } return target, nil } @@ -170,10 +177,10 @@ func LoadPluginFromFile(path string) (configurator.Plugin, error) { // // Returns a map of plugins. Each plugin can be accessed by the name // returned by the plugin.GetName() implemented. -func LoadPluginsFromDir(dirpath string) (map[string]configurator.Plugin, error) { +func LoadPluginsFromDir(dirpath string) (map[string]makeshift.Plugin, error) { // check if verbose option is supplied var ( - cps = make(map[string]configurator.Plugin) + cps = make(map[string]makeshift.Plugin) err error ) @@ -213,11 +220,11 @@ func LoadPluginsFromDir(dirpath string) (map[string]configurator.Plugin, error) return cps, nil } -func SaveProfileToFile(path string, profile *Profile) error { +func SaveProfileToFile(path string, profile *makeshift.Profile) error { return saveToJSONFile(path, profile) } -func SavePluginToFile(path string, plugin *configurator.Plugin) error { +func SavePluginToFile(path string, plugin *makeshift.Plugin) error { return saveToJSONFile(path, plugin) } @@ -230,12 +237,12 @@ func loadFromJSONFile[T any](path string) (*T, error) { contents, err = os.ReadFile(path) if err != nil { - return nil, fmt.Errorf("failed to read plugin file: %v", err) + return nil, fmt.Errorf("failed to read file: %v", err) } err = json.Unmarshal(contents, &res) if err != nil { - return nil, fmt.Errorf("failed to unmarshal plugin: %v", err) + return nil, fmt.Errorf("failed to unmarshal contents from JSON: %v", err) } return res, err @@ -259,11 +266,11 @@ func saveToJSONFile[T any](path string, data T) error { } func (s *Service) PathForProfileWithID(id string) string { - return s.RootPath + RELPATH_PROFILES + "/" + id + return s.RootPath + RELPATH_PROFILES + "/" + id + ".json" } func (s *Service) PathForPluginWithName(name string) string { - return s.RootPath + RELPATH_PLUGINS + "/" + name + return s.RootPath + RELPATH_PLUGINS + "/" + name + ".so" } func (s *Service) PathForProfiles() string { diff --git a/pkg/storage/disk.go b/pkg/storage/disk.go index 0a17cef..46d3f1b 100644 --- a/pkg/storage/disk.go +++ b/pkg/storage/disk.go @@ -2,10 +2,22 @@ package storage type DiskStorage struct{} -func (ds *DiskStorage) Read(k string) error { +func (ds DiskStorage) Init() error { return nil } -func (ms *DiskStorage) Write(k string, v any) error { +func (ds DiskStorage) Cleanup() error { + return nil +} + +func (ds DiskStorage) Get(k string) error { + return nil +} + +func (ds DiskStorage) Set(k string, v any) error { + return nil +} + +func (ds DiskStorage) GetData() any { return nil } diff --git a/pkg/storage/memory.go b/pkg/storage/memory.go index d3890c5..84d4612 100644 --- a/pkg/storage/memory.go +++ b/pkg/storage/memory.go @@ -6,7 +6,16 @@ type MemoryStorage struct { Data map[string]any } -func (ms *MemoryStorage) Read(k string) (any, error) { +func (ms *MemoryStorage) Init() error { + ms.Data = map[string]any{} + return nil +} + +func (ms *MemoryStorage) Cleanup() error { + return nil +} + +func (ms *MemoryStorage) Get(k string) (any, error) { v, ok := ms.Data[k] if ok { return v, nil @@ -14,7 +23,11 @@ func (ms *MemoryStorage) Read(k string) (any, error) { return nil, fmt.Errorf("value does not exist") } -func (ms *MemoryStorage) Write(k string, v any) error { +func (ms *MemoryStorage) Set(k string, v any) error { ms.Data[k] = v return nil } + +func (ms *MemoryStorage) GetData() any { + return ms.Data +} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 28f437b..3bc9877 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -1,12 +1,18 @@ package storage type KVStore interface { + Init() error + Cleanup() error + Get(k string) (any, error) Set(k string, v any) error GetData() any } type KVStaticStore[T any] interface { + Init() error + Cleanup() error + Get(k string) (T, error) Set(k string, v T) error GetData() T diff --git a/pkg/util/util.go b/pkg/util/util.go index 2b120e0..fc53b67 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1,10 +1,8 @@ package util import ( - "archive/tar" "bytes" "cmp" - "compress/gzip" "crypto/tls" "fmt" "io" @@ -99,69 +97,3 @@ func CopyIf[T comparable](s []T, condition func(t T) bool) []T { } 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 - } - - // skip file if it's a directory - if info.IsDir() { - return nil - } - - // 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 -}