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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"net/http"
|
"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
|
// 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
|
// 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
|
// 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"`
|
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.
|
// things like the host, port, access token, etc.
|
||||||
func NewSmdClient(opts ...ClientOption) SmdClient {
|
func NewSmdClient(opts ...Option) SmdClient {
|
||||||
client := SmdClient{}
|
var (
|
||||||
for _, opt := range opts {
|
params = ToParams(opts...)
|
||||||
opt(&client)
|
client = SmdClient{
|
||||||
|
Host: params.Host,
|
||||||
|
AccessToken: params.AccessToken,
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the ethernet interfaces from SMD service using its API. An access token may be required if the SMD
|
// 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.
|
// 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 (
|
var (
|
||||||
params = util.ToDict(opts...)
|
eths = []configurator.EthernetInterface{}
|
||||||
verbose = util.Get[bool](params, "verbose")
|
bytes []byte
|
||||||
eths = []EthernetInterface{}
|
err error
|
||||||
)
|
)
|
||||||
// make request to SMD endpoint
|
// make request to SMD endpoint
|
||||||
b, err := client.makeRequest("/Inventory/EthernetInterfaces")
|
bytes, err = client.makeRequest("/Inventory/EthernetInterfaces")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read HTTP response: %v", err)
|
return nil, fmt.Errorf("failed to read HTTP response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshal response body JSON and extract in object
|
// unmarshal response body JSON and extract in object
|
||||||
err = json.Unmarshal(b, ðs)
|
err = json.Unmarshal(bytes, ðs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// print what we got if verbose is set
|
// print what we got if verbose is set
|
||||||
if verbose != nil {
|
if verbose {
|
||||||
if *verbose {
|
fmt.Printf("Ethernet Interfaces: %v\n", string(bytes))
|
||||||
fmt.Printf("Ethernet Interfaces: %v\n", string(b))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return eths, nil
|
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
|
// 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.
|
// 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 (
|
var (
|
||||||
params = util.ToDict(opts...)
|
comps = []configurator.Component{}
|
||||||
verbose = util.Get[bool](params, "verbose")
|
bytes []byte
|
||||||
comps = []Component{}
|
err error
|
||||||
)
|
)
|
||||||
// make request to SMD endpoint
|
// make request to SMD endpoint
|
||||||
b, err := client.makeRequest("/State/Components")
|
bytes, err = client.makeRequest("/State/Components")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to make HTTP request: %v", err)
|
return nil, fmt.Errorf("failed to make HTTP request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure our response is actually JSON
|
// make sure our response is actually JSON
|
||||||
if !json.Valid(b) {
|
if !json.Valid(bytes) {
|
||||||
return nil, fmt.Errorf("expected valid JSON response: %v", string(b))
|
return nil, fmt.Errorf("expected valid JSON response: %v", string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshal response body JSON and extract in object
|
// unmarshal response body JSON and extract in object
|
||||||
var tmp map[string]any
|
var tmp map[string]any
|
||||||
err = json.Unmarshal(b, &tmp)
|
err = json.Unmarshal(bytes, &tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to marshal JSON: %v", err)
|
return nil, fmt.Errorf("failed to marshal JSON: %v", err)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(b, &comps)
|
err = json.Unmarshal(bytes, &comps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// print what we got if verbose is set
|
// print what we got if verbose is set
|
||||||
if verbose != nil {
|
if verbose {
|
||||||
if *verbose {
|
fmt.Printf("Components: %v\n", string(bytes))
|
||||||
fmt.Printf("Components: %v\n", string(b))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return comps, nil
|
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 (
|
var (
|
||||||
params = util.ToDict(opts...)
|
eps = []configurator.RedfishEndpoint{}
|
||||||
verbose = util.Get[bool](params, "verbose")
|
tmp map[string]any
|
||||||
eps = []RedfishEndpoint{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// make initial request to get JSON with 'RedfishEndpoints' as property
|
||||||
b, err := client.makeRequest("/Inventory/RedfishEndpoints")
|
b, err := client.makeRequest("/Inventory/RedfishEndpoints")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to make HTTP resquest: %v", err)
|
return nil, fmt.Errorf("failed to make HTTP resquest: %v", err)
|
||||||
}
|
}
|
||||||
|
// make sure response is in JSON
|
||||||
if !json.Valid(b) {
|
if !json.Valid(b) {
|
||||||
return nil, fmt.Errorf("expected valid JSON response: %v", string(b))
|
return nil, fmt.Errorf("expected valid JSON response: %v", string(b))
|
||||||
}
|
}
|
||||||
var tmp map[string]any
|
|
||||||
err = json.Unmarshal(b, &tmp)
|
err = json.Unmarshal(b, &tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// marshal RedfishEndpoint JSON back to configurator.RedfishEndpoint
|
||||||
b, err = json.Marshal(tmp["RedfishEndpoints"].([]any))
|
b, err = json.Marshal(tmp["RedfishEndpoints"].([]any))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to marshal JSON: %v", err)
|
return nil, fmt.Errorf("failed to marshal JSON: %v", err)
|
||||||
|
|
@ -201,11 +136,10 @@ func (client *SmdClient) FetchRedfishEndpoints(opts ...util.Option) ([]RedfishEn
|
||||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if verbose != nil {
|
// show the final result
|
||||||
if *verbose {
|
if verbose {
|
||||||
fmt.Printf("Redfish endpoints: %v\n", string(b))
|
fmt.Printf("Redfish endpoints: %v\n", string(b))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return eps, nil
|
return eps, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,15 @@
|
||||||
package configurator
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
configurator "github.com/OpenCHAMI/configurator/pkg"
|
||||||
|
"github.com/OpenCHAMI/configurator/pkg/client"
|
||||||
"gopkg.in/yaml.v2"
|
"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 {
|
type Jwks struct {
|
||||||
Uri string `yaml:"uri"`
|
Uri string `yaml:"uri"`
|
||||||
Retries int `yaml:"retries,omitempty"`
|
Retries int `yaml:"retries,omitempty"`
|
||||||
|
|
@ -31,33 +24,32 @@ type Server struct {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Version string `yaml:"version,omitempty"`
|
Version string `yaml:"version,omitempty"`
|
||||||
Server Server `yaml:"server,omitempty"`
|
Server Server `yaml:"server,omitempty"`
|
||||||
SmdClient SmdClient `yaml:"smd,omitempty"`
|
SmdClient client.SmdClient `yaml:"smd,omitempty"`
|
||||||
AccessToken string `yaml:"access-token,omitempty"`
|
AccessToken string `yaml:"access-token,omitempty"`
|
||||||
Targets map[string]Target `yaml:"targets,omitempty"`
|
Targets map[string]configurator.Target `yaml:"targets,omitempty"`
|
||||||
PluginDirs []string `yaml:"plugins,omitempty"`
|
PluginDirs []string `yaml:"plugins,omitempty"`
|
||||||
CertPath string `yaml:"cacert,omitempty"`
|
CertPath string `yaml:"cacert,omitempty"`
|
||||||
Options Options `yaml:"options,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new config with default parameters.
|
// Creates a new config with default parameters.
|
||||||
func NewConfig() Config {
|
func New() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Version: "",
|
Version: "",
|
||||||
SmdClient: SmdClient{
|
SmdClient: client.SmdClient{
|
||||||
Host: "http://127.0.0.1",
|
Host: "http://127.0.0.1",
|
||||||
Port: 27779,
|
Port: 27779,
|
||||||
},
|
},
|
||||||
Targets: map[string]Target{
|
Targets: map[string]configurator.Target{
|
||||||
"dnsmasq": Target{
|
"dnsmasq": configurator.Target{
|
||||||
PluginPath: "",
|
Plugin: "",
|
||||||
TemplatePaths: []string{},
|
TemplatePaths: []string{},
|
||||||
},
|
},
|
||||||
"conman": Target{
|
"conman": configurator.Target{
|
||||||
PluginPath: "",
|
Plugin: "",
|
||||||
TemplatePaths: []string{},
|
TemplatePaths: []string{},
|
||||||
},
|
},
|
||||||
"warewulf": Target{
|
"warewulf": configurator.Target{
|
||||||
PluginPath: "",
|
Plugin: "",
|
||||||
TemplatePaths: []string{
|
TemplatePaths: []string{
|
||||||
"templates/warewulf/defaults/node.jinja",
|
"templates/warewulf/defaults/node.jinja",
|
||||||
"templates/warewulf/defaults/provision.jinja",
|
"templates/warewulf/defaults/provision.jinja",
|
||||||
|
|
@ -74,12 +66,11 @@ func NewConfig() Config {
|
||||||
Retries: 5,
|
Retries: 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Options: Options{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(path string) Config {
|
func Load(path string) Config {
|
||||||
var c Config = NewConfig()
|
var c Config = New()
|
||||||
file, err := os.ReadFile(path)
|
file, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to read config file: %v\n", err)
|
log.Printf("failed to read config file: %v\n", err)
|
||||||
|
|
@ -93,7 +84,7 @@ func LoadConfig(path string) Config {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) SaveConfig(path string) {
|
func (config *Config) Save(path string) {
|
||||||
path = filepath.Clean(path)
|
path = filepath.Clean(path)
|
||||||
if path == "" || path == "." {
|
if path == "" || path == "." {
|
||||||
path = "config.yaml"
|
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)
|
path = filepath.Clean(path)
|
||||||
if path == "" || path == "." {
|
if path == "" || path == "." {
|
||||||
path = "config.yaml"
|
path = "config.yaml"
|
||||||
}
|
}
|
||||||
var c = NewConfig()
|
var c = New()
|
||||||
data, err := yaml.Marshal(c)
|
data, err := yaml.Marshal(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to marshal config: %v\n", err)
|
log.Printf("failed to marshal config: %v\n", err)
|
||||||
|
|
@ -2,6 +2,13 @@ package configurator
|
||||||
|
|
||||||
import "encoding/json"
|
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 {
|
type IPAddr struct {
|
||||||
IpAddress string `json:"IPAddress"`
|
IpAddress string `json:"IPAddress"`
|
||||||
Network string `json:"Network"`
|
Network string `json:"Network"`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue