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{}
|
||||
|
||||
|
|
@ -26,3 +26,5 @@ func (p *UserData) Run(data storage.KVStore, args []string) error {
|
|||
func (p *UserData) Clean() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var Makeshift UserData
|
||||
Loading…
Add table
Add a link
Reference in a new issue