jx/internal/resource/http.go

184 lines
3.7 KiB
Go
Raw Normal View History

// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
package resource
import (
"context"
2024-04-05 17:22:17 +00:00
_ "errors"
"fmt"
"gopkg.in/yaml.v3"
2024-04-05 17:22:17 +00:00
"io"
"net/url"
2024-04-05 17:22:17 +00:00
"net/http"
_ "os"
2024-04-10 19:38:12 +00:00
"encoding/json"
"strings"
"log/slog"
2024-05-06 00:48:54 +00:00
"gitea.rosskeen.house/rosskeen.house/machine"
)
func init() {
ResourceTypes.Register("http", HTTPFactory)
ResourceTypes.Register("https", HTTPFactory)
}
func HTTPFactory(u *url.URL) Resource {
h := NewHTTP()
2024-04-10 19:38:12 +00:00
h.Endpoint = u.String()
return h
}
2024-04-10 19:38:12 +00:00
type HTTPHeader struct {
Name string `yaml:"name" json:"name"`
2024-04-10 20:27:34 +00:00
Value string `yaml:"value" json:"value"`
2024-04-10 19:38:12 +00:00
}
// Manage the state of an HTTP endpoint
type HTTP struct {
2024-05-09 07:39:45 +00:00
stater machine.Stater `yaml:"-" json:"-"`
2024-04-10 19:38:12 +00:00
client *http.Client `yaml:"-" json:"-"`
2024-04-09 19:30:05 +00:00
Endpoint string `yaml:"endpoint" json:"endpoint"`
2024-04-10 20:27:34 +00:00
Headers []HTTPHeader `yaml:"headers,omitempty" json:"headers,omitempty"`
2024-04-09 19:30:05 +00:00
Body string `yaml:"body,omitempty" json:"body,omitempty"`
2024-05-06 00:48:54 +00:00
State string `yaml:"state,omitempty" json:"state,omitempty"`
}
func NewHTTP() *HTTP {
2024-04-10 19:38:12 +00:00
return &HTTP{ client: &http.Client{} }
}
2024-04-19 07:52:10 +00:00
func (h *HTTP) Clone() Resource {
return &HTTP {
client: h.client,
Endpoint: h.Endpoint,
Headers: h.Headers,
Body: h.Body,
State: h.State,
}
}
2024-05-06 00:48:54 +00:00
func (h *HTTP) StateMachine() machine.Stater {
2024-05-09 07:39:45 +00:00
if h.stater == nil {
h.stater = StorageMachine(h)
}
return h.stater
}
func (h *HTTP) Notify(m *machine.EventMessage) {
ctx := context.Background()
switch m.On {
case machine.ENTERSTATEEVENT:
switch m.Dest {
case "start_create":
2024-05-13 17:13:20 +00:00
if e := h.Create(ctx); e == nil {
if triggerErr := h.stater.Trigger("created"); triggerErr == nil {
return
2024-05-13 05:41:12 +00:00
}
2024-05-09 07:39:45 +00:00
}
2024-05-13 17:13:20 +00:00
h.State = "absent"
2024-05-09 07:39:45 +00:00
case "present":
h.State = "present"
}
case machine.EXITSTATEEVENT:
}
2024-05-06 00:48:54 +00:00
}
func (h *HTTP) URI() string {
return h.Endpoint
}
func (h *HTTP) SetURI(uri string) error {
if _, e := url.Parse(uri); e != nil {
return fmt.Errorf("%w: %s is not a file", ErrInvalidResourceURI, uri)
}
h.Endpoint = uri
return nil
}
2024-04-10 19:38:12 +00:00
func (h *HTTP) JSON() ([]byte, error) {
return json.Marshal(h)
}
2024-04-09 19:30:05 +00:00
func (h *HTTP) Validate() error {
2024-04-10 19:38:12 +00:00
s := NewSchema(h.Type())
jsonDoc, jsonErr := h.JSON()
if jsonErr == nil {
return s.Validate(string(jsonDoc))
}
return jsonErr
2024-04-09 19:30:05 +00:00
}
func (h *HTTP) Apply() error {
switch h.State {
case "absent":
case "present":
}
2024-04-10 19:38:12 +00:00
_,e := h.Read(context.Background())
if e == nil {
h.State = "present"
}
return e
}
2024-04-05 17:22:17 +00:00
func (h *HTTP) Load(r io.Reader) error {
2024-04-10 19:38:12 +00:00
c := NewYAMLDecoder(r)
return c.Decode(h)
2024-04-05 17:22:17 +00:00
}
func (h *HTTP) LoadDecl(yamlResourceDeclaration string) error {
2024-04-10 19:38:12 +00:00
slog.Info("LoadDecl()", "yaml", yamlResourceDeclaration)
c := NewYAMLStringDecoder(yamlResourceDeclaration)
return c.Decode(h)
}
func (h *HTTP) ResolveId(ctx context.Context) string {
return h.Endpoint
}
2024-05-09 07:39:45 +00:00
func (h *HTTP) Create(ctx context.Context) error {
2024-04-10 19:38:12 +00:00
body := strings.NewReader(h.Body)
req, reqErr := http.NewRequest("POST", h.Endpoint, body)
if reqErr != nil {
return reqErr
}
for _,header := range h.Headers {
req.Header.Add(header.Name, header.Value)
}
resp, err := h.client.Do(req)
if err != nil {
return err
}
2024-04-10 20:27:34 +00:00
defer resp.Body.Close()
2024-04-10 19:38:12 +00:00
return err
}
func (h *HTTP) Read(ctx context.Context) ([]byte, error) {
2024-04-10 19:38:12 +00:00
req, reqErr := http.NewRequestWithContext(ctx, "GET", h.Endpoint, nil)
if reqErr != nil {
return nil, reqErr
}
slog.Info("HTTP.Read() ", "request", req, "err", reqErr)
if len(h.Headers) > 0 {
for _,header := range h.Headers {
req.Header.Add(header.Name, header.Value)
}
}
resp, err := h.client.Do(req)
2024-04-05 17:22:17 +00:00
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, errReadBody := io.ReadAll(resp.Body)
if errReadBody != nil {
return nil, errReadBody
}
h.Body = string(body)
return yaml.Marshal(h)
}
func (h *HTTP) Type() string {
2024-04-10 19:38:12 +00:00
return "http"
}