diff --git a/cmd/download.go b/cmd/download.go index b431920..e8f43b6 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -112,8 +112,12 @@ var downloadCmd = cobra.Command{ }, } -var downloadProfileCmd = &cobra.Command{} -var downloadPluginCmd = &cobra.Command{} +var downloadProfileCmd = &cobra.Command{ + Use: "profile", +} +var downloadPluginCmd = &cobra.Command{ + Use: "plugin", +} func init() { downloadCmd.Flags().String("host", "http://localhost:5050", "Set the makeshift remote host (can be set with MAKESHIFT_HOST)") @@ -122,5 +126,7 @@ func init() { downloadCmd.Flags().StringSlice("profiles", []string{}, "Set the profile to use to populate data store") downloadCmd.Flags().StringSlice("plugins", []string{}, "Set the plugins to run before downloading files") + downloadCmd.AddCommand(downloadProfileCmd, downloadPluginCmd) + rootCmd.AddCommand(&downloadCmd) } diff --git a/pkg/models.go b/pkg/models.go index 53a0103..c101203 100644 --- a/pkg/models.go +++ b/pkg/models.go @@ -2,6 +2,7 @@ package makeshift import "git.towk2.me/towk/makeshift/pkg/storage" +type ProfileMap map[string]*Profile type Profile struct { ID string `json:"id"` // profile ID Description string `json:"description,omitempty"` // profile description diff --git a/pkg/plugins/jinja2/jinja2.go b/pkg/plugins/jinja2/jinja2.go index 4b64a11..ff6b98d 100644 --- a/pkg/plugins/jinja2/jinja2.go +++ b/pkg/plugins/jinja2/jinja2.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - configurator "git.towk2.me/towk/makeshift/pkg" + makeshift "git.towk2.me/towk/makeshift/pkg" "git.towk2.me/towk/makeshift/pkg/storage" "github.com/nikolalohinski/gonja/v2" "github.com/nikolalohinski/gonja/v2/exec" @@ -17,13 +17,15 @@ type Jinja2 struct{} func (p *Jinja2) Name() string { return "jinja2" } func (p *Jinja2) Version() string { return "v0.0.1-alpha" } func (p *Jinja2) Description() string { return "Renders Jinja 2 templates" } -func (p *Jinja2) Metadata() configurator.Metadata { - return configurator.Metadata{ - "author.name": "David J. Allen", - "author.email": "davidallendj@gmail.com", - "author.links": []string{ - "https://github.com/davidallendj", - "https://git.towk2.me/towk", +func (p *Jinja2) Metadata() makeshift.Metadata { + return makeshift.Metadata{ + "author": map[string]any{ + "name": "David J. Allen", + "email": "davidallendj@gmail.com", + "links": []string{ + "https://github.com/davidallendj", + "https://git.towk2.me/towk", + }, }, } } @@ -34,25 +36,32 @@ func (p *Jinja2) Init() error { return nil } -func (p *Jinja2) Run(data storage.KVStore, args []string) error { +func (p *Jinja2) Run(store storage.KVStore, args []string) error { // render the files using Jinja 2 from args var ( - rendered []string + mappings struct { + Data map[string]any `json:"data"` + } context *exec.Context template *exec.Template - mappings map[string]any - input any // must be a byte array + profiles any // makeshift.ProfileMap + input any // []byte output bytes.Buffer err error ) log.Debug(). Str("plugin", p.Name()). - Any("data", data). - // Bytes("input", input.([]byte)). + Any("store", store). + Strs("args", args). Int("arg_count", len(args)). - Msg("Run()") + Msg("(jinja2) Run()") - input, err = data.Get("file") + profiles, err = store.Get("profiles") + if err != nil { + return fmt.Errorf("(jinja2) failed to get profiles: %v", err) + } + + input, err = store.Get("file") if err != nil { return fmt.Errorf("(jinja2) failed to get input data: %v", err) } @@ -63,35 +72,56 @@ func (p *Jinja2) Run(data storage.KVStore, args []string) error { return fmt.Errorf("(jinja2) failed to get template from args: %v", err) } - // get mappings from shared data - shared, err := data.Get("shared") + // get mappings from shared data (optional) + shared, err := store.Get("shared") if err != nil { - return fmt.Errorf("(jinja2) failed to get data from store: %v", err) + log.Warn().Err(err).Msg("(jinja2) could not retrieve shared data") + } else { + err = json.Unmarshal(shared.([]byte), &mappings) + if err != nil { + return fmt.Errorf("(jinja2) failed to unmarshal mappings from shared data: %v", err) + } } - err = json.Unmarshal(shared.([]byte), &mappings) - if err != nil { - return fmt.Errorf("(jinja2) failed to unmarshal mappings from shared data: %v", err) + var ps = make(map[string]any) + for profileID, profile := range profiles.(makeshift.ProfileMap) { + ps[profileID] = map[string]any{ + "id": profile.ID, + "description": profile.Description, + "data": profile.Data, + } } - data.Set("mappings", mappings) + // inject profiles and plugin-specific mapping + mappings.Data = map[string]any{ + "makeshift": map[string]any{ + "profiles": ps, + "plugin": map[string]any{ + "name": p.Name(), + "version": p.Version(), + "description": p.Description(), + "metadata": p.Metadata(), + }, + }, + } + + log.Debug().Any("mappings", mappings).Send() // use the provided data in the store to render templates // NOTE: this may be changed to specifically use "shared" data instead - context = exec.NewContext(data.GetData().(map[string]any)) + context = exec.NewContext(mappings.Data) if err = template.Execute(&output, context); err != nil { // Prints: Hello Bob! return fmt.Errorf("(jinja2) failed to render template: %v", err) } - rendered = append(rendered, output.String()) // write render templates to data store output - data.Set("out", rendered) + store.Set("out", output.String()) return nil } func (p *Jinja2) Cleanup() error { // nothing to clean up - log.Debug().Str("plugin", p.Name()).Msg("jinja2.Cleanup()") + log.Debug().Str("plugin", p.Name()).Msg("(jinja2) Cleanup()") return nil } diff --git a/pkg/plugins/smd/smd.go b/pkg/plugins/smd/smd.go index cb263ab..859c8da 100644 --- a/pkg/plugins/smd/smd.go +++ b/pkg/plugins/smd/smd.go @@ -82,21 +82,23 @@ func (p *SmdClient) Version() string { return "v0.0.1-alpha" } func (p *SmdClient) Description() string { return "Fetchs data from SMD and writes to store" } func (p *SmdClient) Metadata() makeshift.Metadata { return makeshift.Metadata{ - "author.name": "David J. Allen", - "author.email": "davidallendj@gmail.com", - "author.links": []string{ - "https://github.com/davidallendj", - "https://git.towk2.me/towk", + "author": map[string]any{ + "name": "David J. Allen", + "email": "davidallendj@gmail.com", + "links": []string{ + "https://github.com/davidallendj", + "https://git.towk2.me/towk", + }, }, } } func (p *SmdClient) Init() error { - log.Debug().Str("plugin", p.Name()).Msg("smd.Init()") + log.Debug().Str("plugin", p.Name()).Msg("(smd) Init()") return nil } -func (p *SmdClient) Run(data storage.KVStore, args []string) error { +func (p *SmdClient) Run(store storage.KVStore, args []string) error { // set all the defaults for variables var ( client SmdClient @@ -104,6 +106,13 @@ func (p *SmdClient) Run(data storage.KVStore, args []string) error { err error ) + log.Debug(). + Str("plugin", p.Name()). + Strs("args", args). + Int("arg_count", len(args)). + Any("store", store). + Msg("(smd) Run()") + // if we have a client, try making the request for the ethernet interfaces err = client.FetchEthernetInterfaces() if err != nil { @@ -124,14 +133,14 @@ func (p *SmdClient) Run(data storage.KVStore, args []string) error { if err != nil { return fmt.Errorf("(smd) failed to marshal SMD client: %v") } - data.Set("shared", bytes) + store.Set("shared", bytes) // apply template substitutions and return output as byte array return nil } func (p *SmdClient) Cleanup() error { - log.Debug().Str("plugin", p.Name()).Msg("smd.Init()") + log.Debug().Str("plugin", p.Name()).Msg("(smd) Init()") return nil } diff --git a/pkg/service/constants.go b/pkg/service/constants.go index 07e0ed2..719a9e4 100644 --- a/pkg/service/constants.go +++ b/pkg/service/constants.go @@ -20,10 +20,20 @@ const (
+ Plugin Information: + Name: {{ makeshift.plugin.name }} + Version: {{ makeshift.plugin.version }} + Description: {{ makeshift.plugin.description }} + Metadata: {{ makeshift.plugin.metadata }} + + Profile Information: + ID: {{ makeshift.profiles.default.id }} + Description: {{ makeshift.profiles.default.description }} + # setup environment variables - export MAKESHIFT_HOST={{ makeshift.host }} - export MAKESHIFT_PATH={{ makeshift.path }} - export MAKESHIFT_SERVER_ROOT={{ makeshift.server.root }} + export MAKESHIFT_HOST={{ makeshift.profiles.default.data.host }} + export MAKESHIFT_PATH={{ makeshift.profiles.default.data.path }} + export MAKESHIFT_SERVER_ROOT={{ makeshift.profiles.default.data.server_root }} # start the service makeshift serve --root $HOME/apps/makeshift/server --init @@ -53,13 +63,9 @@ const ( "id": "default", "description": "Makeshift default profile", "data": { - "makeshift": { - "host": "localhost", - "path": "/test", - "server": { - "root": "/test" - } - } + "host": "localhost", + "path": "/test", + "server_root": "./test" } } ` diff --git a/pkg/service/routes.go b/pkg/service/routes.go index 375fbd0..28c9886 100644 --- a/pkg/service/routes.go +++ b/pkg/service/routes.go @@ -20,8 +20,8 @@ func (s *Service) Download() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( path = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/download") - pluginNames = r.URL.Query()["plugins"] - profileIDs = r.URL.Query()["profiles"] + pluginNames = strings.Split(r.URL.Query().Get("plugins"), ",") + profileIDs = strings.Split(r.URL.Query().Get("profiles"), ",") fileInfo os.FileInfo out *os.File @@ -126,38 +126,42 @@ func (s *Service) Download() http.HandlerFunc { log.Error().Errs("errs", errs).Msg("errors occurred loading plugins") errs = []error{} } + 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{ - "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 + // 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.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 { + w.Write(contents) } - // 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) @@ -228,6 +232,7 @@ func (s *Service) GetStatus(w http.ResponseWriter, r *http.Request) { func (s *Service) loadProfiles(profileIDs []string, store storage.KVStore, errs []error) []error { // load data from profiles into the data store + var profiles = make(makeshift.ProfileMap, len(profileIDs)) for i, profileID := range profileIDs { var ( profilePath = s.PathForProfileWithID(profileID) @@ -251,8 +256,9 @@ func (s *Service) loadProfiles(profileIDs []string, store storage.KVStore, errs errs = append(errs, err) continue } - store.Set(profileID, profile) + profiles[profileID] = profile } + store.Set("profiles", profiles) return errs } diff --git a/pkg/storage/memory.go b/pkg/storage/memory.go index 84d4612..e554fbd 100644 --- a/pkg/storage/memory.go +++ b/pkg/storage/memory.go @@ -3,7 +3,7 @@ package storage import "fmt" type MemoryStorage struct { - Data map[string]any + Data map[string]any `json:"data"` } func (ms *MemoryStorage) Init() error { @@ -20,7 +20,7 @@ func (ms *MemoryStorage) Get(k string) (any, error) { if ok { return v, nil } - return nil, fmt.Errorf("value does not exist") + return nil, fmt.Errorf("value '%s' does not exist", k) } func (ms *MemoryStorage) Set(k string, v any) error {