mirror of
https://github.com/davidallendj/configurator.git
synced 2025-12-19 19:17:01 -07:00
refactor: more code cleanup and reorganization
This commit is contained in:
parent
72dd8416aa
commit
69aac3c929
4 changed files with 138 additions and 140 deletions
66
pkg/client/client.go
Normal file
66
pkg/client/client.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Option func(*Params)
|
||||
type Params struct {
|
||||
Host string `yaml:"host"`
|
||||
AccessToken string `yaml:"access-token"`
|
||||
Transport *http.Transport
|
||||
}
|
||||
|
||||
func ToParams(opts ...Option) *Params {
|
||||
params := &Params{}
|
||||
for _, opt := range opts {
|
||||
opt(params)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func WithHost(host string) Option {
|
||||
return func(c *Params) {
|
||||
c.Host = host
|
||||
}
|
||||
}
|
||||
|
||||
func WithAccessToken(token string) Option {
|
||||
return func(c *Params) {
|
||||
c.AccessToken = token
|
||||
}
|
||||
}
|
||||
|
||||
func WithCertPool(certPool *x509.CertPool) Option {
|
||||
return func(c *Params) {
|
||||
c.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
DisableKeepAlives: true,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 120 * time.Second,
|
||||
KeepAlive: 120 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 120 * time.Second,
|
||||
ResponseHeaderTimeout: 120 * time.Second,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Need to check for errors when reading from a file
|
||||
func WithCertPoolFile(certPath string) Option {
|
||||
if certPath == "" {
|
||||
return func(sc *Params) {}
|
||||
}
|
||||
cacert, _ := os.ReadFile(certPath)
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(cacert)
|
||||
return WithCertPool(certPool)
|
||||
}
|
||||
|
|
@ -1,22 +1,15 @@
|
|||
package configurator
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/OpenCHAMI/configurator/pkg/util"
|
||||
configurator "github.com/OpenCHAMI/configurator/pkg"
|
||||
)
|
||||
|
||||
type ClientOption func(*SmdClient)
|
||||
|
||||
// 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
|
||||
|
|
@ -28,101 +21,43 @@ type SmdClient struct {
|
|||
AccessToken string `yaml:"access-token"`
|
||||
}
|
||||
|
||||
// Constructor function that allows supplying ClientOption arguments to set
|
||||
// Constructor function that allows supplying Option arguments to set
|
||||
// things like the host, port, access token, etc.
|
||||
func NewSmdClient(opts ...ClientOption) SmdClient {
|
||||
client := SmdClient{}
|
||||
for _, opt := range opts {
|
||||
opt(&client)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func WithHost(host string) ClientOption {
|
||||
return func(c *SmdClient) {
|
||||
c.Host = host
|
||||
}
|
||||
}
|
||||
|
||||
func WithPort(port int) ClientOption {
|
||||
return func(c *SmdClient) {
|
||||
c.Port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithAccessToken(token string) ClientOption {
|
||||
return func(c *SmdClient) {
|
||||
c.AccessToken = token
|
||||
}
|
||||
}
|
||||
|
||||
func WithCertPool(certPool *x509.CertPool) ClientOption {
|
||||
return func(c *SmdClient) {
|
||||
c.Client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
DisableKeepAlives: true,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 120 * time.Second,
|
||||
KeepAlive: 120 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 120 * time.Second,
|
||||
ResponseHeaderTimeout: 120 * time.Second,
|
||||
func NewSmdClient(opts ...Option) SmdClient {
|
||||
var (
|
||||
params = ToParams(opts...)
|
||||
client = SmdClient{
|
||||
Host: params.Host,
|
||||
AccessToken: params.AccessToken,
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// FIXME: Need to check for errors when reading from a file
|
||||
func WithCertPoolFile(certPath string) ClientOption {
|
||||
if certPath == "" {
|
||||
return func(sc *SmdClient) {}
|
||||
}
|
||||
cacert, _ := os.ReadFile(certPath)
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(cacert)
|
||||
return WithCertPool(certPool)
|
||||
}
|
||||
|
||||
func WithVerbosity() util.Option {
|
||||
return func(p util.Params) {
|
||||
p["verbose"] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Create a set of params with all default values.
|
||||
func NewParams() util.Params {
|
||||
return util.Params{
|
||||
"verbose": false,
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// 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(opts ...util.Option) ([]EthernetInterface, error) {
|
||||
func (client *SmdClient) FetchEthernetInterfaces(verbose bool) ([]configurator.EthernetInterface, error) {
|
||||
var (
|
||||
params = util.ToDict(opts...)
|
||||
verbose = util.Get[bool](params, "verbose")
|
||||
eths = []EthernetInterface{}
|
||||
eths = []configurator.EthernetInterface{}
|
||||
bytes []byte
|
||||
err error
|
||||
)
|
||||
// make request to SMD endpoint
|
||||
b, err := client.makeRequest("/Inventory/EthernetInterfaces")
|
||||
bytes, err = client.makeRequest("/Inventory/EthernetInterfaces")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read HTTP response: %v", err)
|
||||
}
|
||||
|
||||
// unmarshal response body JSON and extract in object
|
||||
err = json.Unmarshal(b, ðs)
|
||||
err = json.Unmarshal(bytes, ðs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
// print what we got if verbose is set
|
||||
if verbose != nil {
|
||||
if *verbose {
|
||||
fmt.Printf("Ethernet Interfaces: %v\n", string(b))
|
||||
}
|
||||
if verbose {
|
||||
fmt.Printf("Ethernet Interfaces: %v\n", string(bytes))
|
||||
}
|
||||
|
||||
return eths, nil
|
||||
|
|
@ -130,68 +65,68 @@ func (client *SmdClient) FetchEthernetInterfaces(opts ...util.Option) ([]Etherne
|
|||
|
||||
// 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(opts ...util.Option) ([]Component, error) {
|
||||
func (client *SmdClient) FetchComponents(verbose bool) ([]configurator.Component, error) {
|
||||
var (
|
||||
params = util.ToDict(opts...)
|
||||
verbose = util.Get[bool](params, "verbose")
|
||||
comps = []Component{}
|
||||
comps = []configurator.Component{}
|
||||
bytes []byte
|
||||
err error
|
||||
)
|
||||
// make request to SMD endpoint
|
||||
b, err := client.makeRequest("/State/Components")
|
||||
bytes, err = client.makeRequest("/State/Components")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to make HTTP request: %v", err)
|
||||
}
|
||||
|
||||
// make sure our response is actually JSON
|
||||
if !json.Valid(b) {
|
||||
return nil, fmt.Errorf("expected valid JSON response: %v", string(b))
|
||||
if !json.Valid(bytes) {
|
||||
return nil, 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(b, &tmp)
|
||||
err = json.Unmarshal(bytes, &tmp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
b, err = json.Marshal(tmp["RedfishEndpoints"].([]any))
|
||||
bytes, err = json.Marshal(tmp["RedfishEndpoints"].([]any))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal JSON: %v", err)
|
||||
}
|
||||
err = json.Unmarshal(b, &comps)
|
||||
err = json.Unmarshal(bytes, &comps)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
// print what we got if verbose is set
|
||||
if verbose != nil {
|
||||
if *verbose {
|
||||
fmt.Printf("Components: %v\n", string(b))
|
||||
}
|
||||
if verbose {
|
||||
fmt.Printf("Components: %v\n", string(bytes))
|
||||
}
|
||||
|
||||
return comps, nil
|
||||
}
|
||||
|
||||
func (client *SmdClient) FetchRedfishEndpoints(opts ...util.Option) ([]RedfishEndpoint, error) {
|
||||
// TODO: improve implementation of this function
|
||||
func (client *SmdClient) FetchRedfishEndpoints(verbose bool) ([]configurator.RedfishEndpoint, error) {
|
||||
var (
|
||||
params = util.ToDict(opts...)
|
||||
verbose = util.Get[bool](params, "verbose")
|
||||
eps = []RedfishEndpoint{}
|
||||
eps = []configurator.RedfishEndpoint{}
|
||||
tmp map[string]any
|
||||
)
|
||||
|
||||
// make initial request to get JSON with 'RedfishEndpoints' as property
|
||||
b, err := client.makeRequest("/Inventory/RedfishEndpoints")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to make HTTP resquest: %v", err)
|
||||
}
|
||||
// make sure response is in JSON
|
||||
if !json.Valid(b) {
|
||||
return nil, fmt.Errorf("expected valid JSON response: %v", string(b))
|
||||
}
|
||||
var tmp map[string]any
|
||||
err = json.Unmarshal(b, &tmp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
// marshal RedfishEndpoint JSON back to configurator.RedfishEndpoint
|
||||
b, err = json.Marshal(tmp["RedfishEndpoints"].([]any))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal JSON: %v", err)
|
||||
|
|
@ -201,10 +136,9 @@ func (client *SmdClient) FetchRedfishEndpoints(opts ...util.Option) ([]RedfishEn
|
|||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
if verbose != nil {
|
||||
if *verbose {
|
||||
fmt.Printf("Redfish endpoints: %v\n", string(b))
|
||||
}
|
||||
// show the final result
|
||||
if verbose {
|
||||
fmt.Printf("Redfish endpoints: %v\n", string(b))
|
||||
}
|
||||
|
||||
return eps, nil
|
||||
|
|
@ -1,22 +1,15 @@
|
|||
package configurator
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
configurator "github.com/OpenCHAMI/configurator/pkg"
|
||||
"github.com/OpenCHAMI/configurator/pkg/client"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Options struct{}
|
||||
|
||||
type Target struct {
|
||||
PluginPath string `yaml:"plugin,omitempty"`
|
||||
TemplatePaths []string `yaml:"templates,omitempty"`
|
||||
FilePaths []string `yaml:"files,omitempty"`
|
||||
RunTargets []string `yaml:"targets,omitempty"`
|
||||
}
|
||||
|
||||
type Jwks struct {
|
||||
Uri string `yaml:"uri"`
|
||||
Retries int `yaml:"retries,omitempty"`
|
||||
|
|
@ -29,35 +22,34 @@ type Server struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Server Server `yaml:"server,omitempty"`
|
||||
SmdClient SmdClient `yaml:"smd,omitempty"`
|
||||
AccessToken string `yaml:"access-token,omitempty"`
|
||||
Targets map[string]Target `yaml:"targets,omitempty"`
|
||||
PluginDirs []string `yaml:"plugins,omitempty"`
|
||||
CertPath string `yaml:"cacert,omitempty"`
|
||||
Options Options `yaml:"options,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Server Server `yaml:"server,omitempty"`
|
||||
SmdClient client.SmdClient `yaml:"smd,omitempty"`
|
||||
AccessToken string `yaml:"access-token,omitempty"`
|
||||
Targets map[string]configurator.Target `yaml:"targets,omitempty"`
|
||||
PluginDirs []string `yaml:"plugins,omitempty"`
|
||||
CertPath string `yaml:"cacert,omitempty"`
|
||||
}
|
||||
|
||||
// Creates a new config with default parameters.
|
||||
func NewConfig() Config {
|
||||
func New() Config {
|
||||
return Config{
|
||||
Version: "",
|
||||
SmdClient: SmdClient{
|
||||
SmdClient: client.SmdClient{
|
||||
Host: "http://127.0.0.1",
|
||||
Port: 27779,
|
||||
},
|
||||
Targets: map[string]Target{
|
||||
"dnsmasq": Target{
|
||||
PluginPath: "",
|
||||
Targets: map[string]configurator.Target{
|
||||
"dnsmasq": configurator.Target{
|
||||
Plugin: "",
|
||||
TemplatePaths: []string{},
|
||||
},
|
||||
"conman": Target{
|
||||
PluginPath: "",
|
||||
"conman": configurator.Target{
|
||||
Plugin: "",
|
||||
TemplatePaths: []string{},
|
||||
},
|
||||
"warewulf": Target{
|
||||
PluginPath: "",
|
||||
"warewulf": configurator.Target{
|
||||
Plugin: "",
|
||||
TemplatePaths: []string{
|
||||
"templates/warewulf/defaults/node.jinja",
|
||||
"templates/warewulf/defaults/provision.jinja",
|
||||
|
|
@ -74,12 +66,11 @@ func NewConfig() Config {
|
|||
Retries: 5,
|
||||
},
|
||||
},
|
||||
Options: Options{},
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfig(path string) Config {
|
||||
var c Config = NewConfig()
|
||||
func Load(path string) Config {
|
||||
var c Config = New()
|
||||
file, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Printf("failed to read config file: %v\n", err)
|
||||
|
|
@ -93,7 +84,7 @@ func LoadConfig(path string) Config {
|
|||
return c
|
||||
}
|
||||
|
||||
func (config *Config) SaveConfig(path string) {
|
||||
func (config *Config) Save(path string) {
|
||||
path = filepath.Clean(path)
|
||||
if path == "" || path == "." {
|
||||
path = "config.yaml"
|
||||
|
|
@ -110,12 +101,12 @@ func (config *Config) SaveConfig(path string) {
|
|||
}
|
||||
}
|
||||
|
||||
func SaveDefaultConfig(path string) {
|
||||
func SaveDefault(path string) {
|
||||
path = filepath.Clean(path)
|
||||
if path == "" || path == "." {
|
||||
path = "config.yaml"
|
||||
}
|
||||
var c = NewConfig()
|
||||
var c = New()
|
||||
data, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
log.Printf("failed to marshal config: %v\n", err)
|
||||
|
|
@ -2,6 +2,13 @@ package configurator
|
|||
|
||||
import "encoding/json"
|
||||
|
||||
type Target struct {
|
||||
Plugin string `yaml:"plugin,omitempty"` // Set the plugin or it's path
|
||||
TemplatePaths []string `yaml:"templates,omitempty"` // Set the template paths
|
||||
FilePaths []string `yaml:"files,omitempty"` // Set the file paths
|
||||
RunTargets []string `yaml:"targets,omitempty"` // Set additional targets to run
|
||||
}
|
||||
|
||||
type IPAddr struct {
|
||||
IpAddress string `json:"IPAddress"`
|
||||
Network string `json:"Network"`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue