refactor: moved plugin files
This commit is contained in:
parent
c24bcf34d4
commit
ea4819e97a
5 changed files with 372 additions and 61 deletions
|
|
@ -1,58 +0,0 @@
|
||||||
package plugin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"git.towk2.me/towk/configurator/pkg/storage"
|
|
||||||
"github.com/nikolalohinski/gonja/v2"
|
|
||||||
"github.com/nikolalohinski/gonja/v2/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Jinja2 struct{}
|
|
||||||
|
|
||||||
func (p *Jinja2) Name() string { return "jinja2" }
|
|
||||||
func (p *Jinja2) Version() string { return "test" }
|
|
||||||
func (p *Jinja2) Description() string { return "Renders Jinja 2 templates" }
|
|
||||||
func (p *Jinja2) Metadata() map[string]string {
|
|
||||||
return map[string]string{
|
|
||||||
"author.name": "David J. Allen",
|
|
||||||
"author.email": "davidallendj@gmail.com",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Jinja2) Init() error {
|
|
||||||
// nothing to initialize
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Jinja2) Run(data storage.KVStore, args []string) error {
|
|
||||||
// render the files using Jinja 2 from args
|
|
||||||
newContent := []string{}
|
|
||||||
for _, arg := range args {
|
|
||||||
var (
|
|
||||||
context *exec.Context
|
|
||||||
template *exec.Template
|
|
||||||
output bytes.Buffer
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
template, err = gonja.FromString(arg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
context = exec.NewContext(data.GetData().(map[string]any))
|
|
||||||
if err = template.Execute(&output, context); err != nil { // Prints: Hello Bob!
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
newContent = append(newContent, output.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// write back to the data storage
|
|
||||||
data.Set("out", newContent)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Jinja2) Cleanup() error {
|
|
||||||
// nothing to clean up
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
98
pkg/plugins/jinja2/jinja2.go
Normal file
98
pkg/plugins/jinja2/jinja2.go
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
configurator "git.towk2.me/towk/makeshift/pkg"
|
||||||
|
"git.towk2.me/towk/makeshift/pkg/storage"
|
||||||
|
"github.com/nikolalohinski/gonja/v2"
|
||||||
|
"github.com/nikolalohinski/gonja/v2/exec"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
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) Init() error {
|
||||||
|
// nothing to initialize
|
||||||
|
log.Debug().Str("plugin", p.Name()).Msg("jinja2.Init()")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Jinja2) Run(data storage.KVStore, args []string) error {
|
||||||
|
// render the files using Jinja 2 from args
|
||||||
|
var (
|
||||||
|
rendered []string
|
||||||
|
context *exec.Context
|
||||||
|
template *exec.Template
|
||||||
|
mappings map[string]any
|
||||||
|
input any // must be a byte array
|
||||||
|
output bytes.Buffer
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
log.Debug().
|
||||||
|
Str("plugin", p.Name()).
|
||||||
|
Any("data", data).
|
||||||
|
// Bytes("input", input.([]byte)).
|
||||||
|
Int("arg_count", len(args)).
|
||||||
|
Msg("Run()")
|
||||||
|
|
||||||
|
input, err = data.Get("file")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(jinja2) failed to get input data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the templates provided as args to the plugin
|
||||||
|
template, err = gonja.FromBytes(input.([]byte))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(jinja2) failed to get template from args: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get mappings from shared data
|
||||||
|
shared, err := data.Get("shared")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(jinja2) failed to get data from store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(shared.([]byte), &mappings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(jinja2) failed to unmarshal mappings from shared data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("mappings", mappings)
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
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)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Jinja2) Cleanup() error {
|
||||||
|
// nothing to clean up
|
||||||
|
log.Debug().Str("plugin", p.Name()).Msg("jinja2.Cleanup()")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var Makeshift Jinja2
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package plugin
|
|
||||||
270
pkg/plugins/smd/smd.go
Normal file
270
pkg/plugins/smd/smd.go
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
makeshift "git.towk2.me/towk/makeshift/pkg"
|
||||||
|
"git.towk2.me/towk/makeshift/pkg/storage"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An struct that's meant to extend functionality of the base HTTP client by
|
||||||
|
// adding commonly made requests to SMD. The implemented functions are can be
|
||||||
|
// used in generator plugins to fetch data when it is needed to substitute
|
||||||
|
// values for the Jinja templates used.
|
||||||
|
type SmdClient struct {
|
||||||
|
http.Client `json:"-" yaml:"-"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
AccessToken string `yaml:"access-token"`
|
||||||
|
|
||||||
|
RedfishEndpoints []RedfishEndpoint `json:"redfish_endpoints"`
|
||||||
|
EthernetInterfaces []EthernetInterface `json:"ethernet_interfaces"`
|
||||||
|
Components []Component `json:"components"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPAddr struct {
|
||||||
|
IpAddress string `json:"IPAddress"`
|
||||||
|
Network string `json:"Network"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EthernetInterface struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
Description string `json:"Description"`
|
||||||
|
MacAddress string `json:"MACAddr"`
|
||||||
|
LastUpdate string `json:"LastUpdate"`
|
||||||
|
ComponentID string `json:"ComponentID"`
|
||||||
|
Type string `json:"Type"`
|
||||||
|
IpAddresses []IPAddr `json:"IPAddresses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Component struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
Type string `json:"Type"`
|
||||||
|
State string `json:"State,omitempty"`
|
||||||
|
Flag string `json:"Flag,omitempty"`
|
||||||
|
Enabled *bool `json:"Enabled,omitempty"`
|
||||||
|
SwStatus string `json:"SoftwareStatus,omitempty"`
|
||||||
|
Role string `json:"Role,omitempty"`
|
||||||
|
SubRole string `json:"SubRole,omitempty"`
|
||||||
|
NID json.Number `json:"NID,omitempty"`
|
||||||
|
Subtype string `json:"Subtype,omitempty"`
|
||||||
|
NetType string `json:"NetType,omitempty"`
|
||||||
|
Arch string `json:"Arch,omitempty"`
|
||||||
|
Class string `json:"Class,omitempty"`
|
||||||
|
ReservationDisabled bool `json:"ReservationDisabled,omitempty"`
|
||||||
|
Locked bool `json:"Locked,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedfishEndpoint struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
Type string `json:"Type"`
|
||||||
|
Name string `json:"Name,omitempty"` // user supplied descriptive name
|
||||||
|
Hostname string `json:"Hostname"`
|
||||||
|
Domain string `json:"Domain"`
|
||||||
|
FQDN string `json:"FQDN"`
|
||||||
|
Enabled bool `json:"Enabled"`
|
||||||
|
UUID string `json:"UUID,omitempty"`
|
||||||
|
User string `json:"User"`
|
||||||
|
Password string `json:"Password"` // Temporary until more secure method
|
||||||
|
UseSSDP bool `json:"UseSSDP,omitempty"`
|
||||||
|
MACRequired bool `json:"MACRequired,omitempty"`
|
||||||
|
MACAddr string `json:"MACAddr,omitempty"`
|
||||||
|
IPAddr string `json:"IPAddress,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SmdClient) Name() string { return "smd" }
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SmdClient) Init() error {
|
||||||
|
log.Debug().Str("plugin", p.Name()).Msg("smd.Init()")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SmdClient) Run(data storage.KVStore, args []string) error {
|
||||||
|
// set all the defaults for variables
|
||||||
|
var (
|
||||||
|
client SmdClient
|
||||||
|
bytes []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// if we have a client, try making the request for the ethernet interfaces
|
||||||
|
err = client.FetchEthernetInterfaces()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(smd) failed to fetch ethernet interfaces with client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.FetchRedfishEndpoints()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(smd) failed to fetch redfish endpoints with client: %v", err)
|
||||||
|
}
|
||||||
|
err = client.FetchComponents()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(smd) failed to fetch components with client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write data back to shared data store to be used by other plugins
|
||||||
|
bytes, err = json.Marshal(client)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(smd) failed to marshal SMD client: %v")
|
||||||
|
}
|
||||||
|
data.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()")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the ethernet interfaces from SMD service using its API. An access token may be required if the SMD
|
||||||
|
// service SMD_JWKS_URL envirnoment variable is set.
|
||||||
|
func (client *SmdClient) FetchEthernetInterfaces() error {
|
||||||
|
var (
|
||||||
|
bytes []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
// make request to SMD endpoint
|
||||||
|
bytes, err = client.makeRequest("/Inventory/EthernetInterfaces")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read HTTP response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal response body JSON and extract in object
|
||||||
|
err = json.Unmarshal(bytes, &client.EthernetInterfaces)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// print what we got if verbose is set
|
||||||
|
log.Debug().Str("ethernet_interfaces", string(bytes)).Msg("found interfaces")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the components from SMD using its API. An access token may be required if the SMD
|
||||||
|
// service SMD_JWKS_URL envirnoment variable is set.
|
||||||
|
func (client *SmdClient) FetchComponents() error {
|
||||||
|
var (
|
||||||
|
bytes []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
// make request to SMD endpoint
|
||||||
|
bytes, err = client.makeRequest("/State/Components")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to make HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure our response is actually JSON first
|
||||||
|
if !json.Valid(bytes) {
|
||||||
|
return fmt.Errorf("expected valid JSON response: %v", string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal response body JSON and extract in object
|
||||||
|
var tmp map[string]any
|
||||||
|
err = json.Unmarshal(bytes, &tmp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
|
}
|
||||||
|
bytes, err = json.Marshal(tmp["RedfishEndpoints"].([]any))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal JSON: %v", err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(bytes, &client.Components)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// print what we got if verbose is set
|
||||||
|
log.Debug().Str("components", string(bytes)).Msg("found components")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: improve implementation of this function
|
||||||
|
func (client *SmdClient) FetchRedfishEndpoints() error {
|
||||||
|
var (
|
||||||
|
store map[string]any
|
||||||
|
rfeps []RedfishEndpoint
|
||||||
|
body []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// make initial request to get JSON with 'RedfishEndpoints' as property
|
||||||
|
body, err = client.makeRequest("/Inventory/RedfishEndpoints")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to make HTTP resquest: %v", err)
|
||||||
|
}
|
||||||
|
// make sure response is in JSON
|
||||||
|
if !json.Valid(body) {
|
||||||
|
return fmt.Errorf("expected valid JSON response: %s", string(body))
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(body, &store)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshal RedfishEndpoint JSON back to makeshift.RedfishEndpoint
|
||||||
|
body, err = json.Marshal(store["RedfishEndpoints"].([]any))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal JSON: %v", err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(body, &rfeps)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show the final result
|
||||||
|
log.Debug().Bytes("redfish_endpoints", body).Msg("found redfish endpoints")
|
||||||
|
|
||||||
|
client.RedfishEndpoints = rfeps
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *SmdClient) makeRequest(endpoint string) ([]byte, error) {
|
||||||
|
if client == nil {
|
||||||
|
return nil, fmt.Errorf("client is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch DHCP related information from SMD's endpoint:
|
||||||
|
url := fmt.Sprintf("%s/hsm/v2%s", client.Host, endpoint)
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer([]byte{}))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create new HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// include access token in authorzation header if found
|
||||||
|
// NOTE: This shouldn't be needed for this endpoint since it's public
|
||||||
|
if client.AccessToken != "" {
|
||||||
|
req.Header.Add("Authorization", "Bearer "+client.AccessToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the request to SMD
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to make request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the contents of the response body
|
||||||
|
return io.ReadAll(res.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
var Makeshift SmdClient
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package plugin
|
package main
|
||||||
|
|
||||||
import "git.towk2.me/towk/configurator/pkg/storage"
|
import "git.towk2.me/towk/makeshift/pkg/storage"
|
||||||
|
|
||||||
type UserData struct{}
|
type UserData struct{}
|
||||||
|
|
||||||
|
|
@ -26,3 +26,5 @@ func (p *UserData) Run(data storage.KVStore, args []string) error {
|
||||||
func (p *UserData) Clean() error {
|
func (p *UserData) Clean() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Makeshift UserData
|
||||||
Loading…
Add table
Add a link
Reference in a new issue