feat: added working rendering with plugins
This commit is contained in:
parent
fbdaf218eb
commit
1ebea8cb73
7 changed files with 139 additions and 81 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,20 @@ const (
|
|||
<html>
|
||||
<body>
|
||||
<p>
|
||||
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</br>
|
||||
export MAKESHIFT_HOST={{ makeshift.host }}</br>
|
||||
export MAKESHIFT_PATH={{ makeshift.path }}</br>
|
||||
export MAKESHIFT_SERVER_ROOT={{ makeshift.server.root }}</br>
|
||||
export MAKESHIFT_HOST={{ makeshift.profiles.default.data.host }}</br>
|
||||
export MAKESHIFT_PATH={{ makeshift.profiles.default.data.path }}</br>
|
||||
export MAKESHIFT_SERVER_ROOT={{ makeshift.profiles.default.data.server_root }}</br>
|
||||
</br>
|
||||
# start the service</br>
|
||||
makeshift serve --root $HOME/apps/makeshift/server --init</br>
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue