fixes for http resource use of common
All checks were successful
Lint / golangci-lint (push) Successful in 10m21s
Declarative Tests / test (push) Successful in 36s

This commit is contained in:
Matthew Rich 2024-09-28 05:04:15 +00:00
parent 71a074ca05
commit 10e854583f
12 changed files with 145 additions and 147 deletions

View File

@ -17,6 +17,11 @@ _ "gitea.rosskeen.house/rosskeen.house/machine"
"decl/internal/schema"
"net/url"
"runtime/debug"
"errors"
)
var (
ErrUnknownStateTransition error = errors.New("Unknown state transition")
)
type ConfigName string
@ -177,6 +182,8 @@ func (d *Declaration) Apply(stateTransition string) (result error) {
if doc, ok := DocumentRegistry.DeclarationMap[d]; ok {
d.SetDocument(doc)
}
case "stat":
result = stater.Trigger("stat")
case "read":
result = stater.Trigger("read")
case "delete", "absent":
@ -189,7 +196,7 @@ func (d *Declaration) Apply(stateTransition string) (result error) {
}
result = stater.Trigger("read")
default:
fallthrough
return fmt.Errorf("%w: %s on %s", ErrUnknownStateTransition, stateTransition, d.Attributes.URI())
case "create", "present":
if stater.CurrentState() == "absent" || stater.CurrentState() == "unknown" {
if result = stater.Trigger("create"); result != nil {

View File

@ -277,6 +277,7 @@ func (d *Document) Apply(state string) error {
if d.ResourceDeclarations[idx].Requires.Check() {
if e := d.ResourceDeclarations[idx].Apply(state); e != nil {
d.ResourceDeclarations[idx].Error = e.Error()
slog.Info("Document.Apply() ERROR", "index", idx, "uri", d.ResourceDeclarations[idx].Resource().URI(), "error", e.Error())
switch d.ResourceDeclarations[idx].OnError.GetStrategy() {
case OnErrorStop:
return e

View File

@ -3,14 +3,14 @@
package resource
import (
_ "context"
_ "context"
"encoding/json"
"fmt"
"gopkg.in/yaml.v3"
"io"
"log/slog"
_ "net/url"
_ "os"
_ "net/url"
_ "os"
"os/exec"
"strings"
"text/template"

View File

@ -9,9 +9,13 @@ import (
"path/filepath"
"decl/internal/data"
"decl/internal/folio"
"log/slog"
)
type UriSchemeValidator func(scheme string) bool
type Common struct {
SchemeCheck UriSchemeValidator `json:"-" yaml:"-"`
includeQueryParamsInURI bool `json:"-" yaml:"-"`
resourceType TypeName `json:"-" yaml:"-"`
Uri folio.URI `json:"uri,omitempty" yaml:"uri,omitempty"`
@ -27,8 +31,14 @@ type Common struct {
Resources data.ResourceMapper `json:"-" yaml:"-"`
}
func NewCommon() *Common {
return &Common{ includeQueryParamsInURI: false }
func NewCommon(resourceType TypeName, includeQueryParams bool) *Common {
c := &Common{ includeQueryParamsInURI: includeQueryParams, resourceType: resourceType }
c.SchemeCheck = c.IsValidResourceScheme
return c
}
func (c *Common) IsValidResourceScheme(scheme string) bool {
return c.Type() == scheme
}
func (c *Common) ContentType() string {
@ -65,7 +75,7 @@ func (c *Common) URIPath() string {
}
func (c *Common) URI() string {
return fmt.Sprintf("%s://%s", c.Type(), c.Path)
return string(c.Uri)
}
func (c *Common) SetURI(uri string) (err error) {
@ -81,11 +91,12 @@ func (c *Common) SetURIFromString(uri string) {
func (c *Common) SetParsedURI(u *url.URL) (err error) {
if u != nil {
slog.Info("Common.SetParsedURI()", "parsed", u, "uri", c.Uri)
if c.Uri.IsEmpty() {
c.SetURIFromString(u.String())
}
c.parsedURI = u
if c.parsedURI.Scheme == c.Type() {
if c.SchemeCheck(c.parsedURI.Scheme) {
if c.includeQueryParamsInURI {
c.Path = filepath.Join(c.parsedURI.Hostname(), c.parsedURI.RequestURI())
} else {
@ -97,7 +108,7 @@ func (c *Common) SetParsedURI(u *url.URL) (err error) {
return
}
}
err = fmt.Errorf("%w: %s is not a file", ErrInvalidResourceURI, c.Uri)
err = fmt.Errorf("%w: %s is not a %s resource, parsed: %t", ErrInvalidResourceURI, c.Uri, c.Type(), (u != nil))
return
}

View File

@ -9,19 +9,27 @@ import (
)
func TestNewCommon(t *testing.T) {
c := NewCommon()
c := NewCommon(TypeName("foo"), false)
assert.NotNil(t, c)
}
func TestCommon(t *testing.T) {
for _, v := range []struct{ uri string; expected Common }{
{ uri: "file:///tmp/foo", expected: Common{ resourceType: "file", Uri: "file:///tmp/foo", parsedURI: &url.URL{ Scheme: "file", Path: "/tmp/foo"}, Path: "/tmp/foo" } },
expectedCommon := NewCommon(FileTypeName, false)
expectedCommon.resourceType = "file"
expectedCommon.Uri = "file:///tmp/foo"
expectedCommon.parsedURI = &url.URL{ Scheme: "file", Path: "/tmp/foo"}
expectedCommon.Path = "/tmp/foo"
for _, v := range []struct{ uri string; expected *Common }{
{ uri: "file:///tmp/foo", expected: expectedCommon },
}{
c := NewCommon()
c := NewCommon(FileTypeName, false)
c.resourceType = "file"
assert.Nil(t, c.SetURI(v.uri))
assert.Equal(t, v.expected.resourceType , c.resourceType)
assert.Equal(t, v.expected.Path, c.Path)
assert.Equal(t, &v.expected, c)
assert.Equal(t, v.expected.parsedURI.Scheme, c.parsedURI.Scheme)
assert.Equal(t, v.expected.parsedURI.Path, c.parsedURI.Path)
}
}

View File

@ -587,6 +587,7 @@ func (c *ContainerImage) Create(ctx context.Context) (err error) {
buildOptions := types.ImageBuildOptions{
Dockerfile: dockerfileURI.Path,
Tags: []string{c.Name},
NetworkMode: "host",
}
var reader io.ReadCloser

View File

@ -46,7 +46,8 @@ func init() {
}
func NewExec() *Exec {
return &Exec{ Common: &Common{ includeQueryParamsInURI: true, resourceType: ExecTypeName } }
e := &Exec{ Common: NewCommon(ExecTypeName, true) }
return e
}
func (x *Exec) SetResourceMapper(resources data.ResourceMapper) {
@ -87,28 +88,6 @@ func (x *Exec) SetParsedURI(uri *url.URL) (err error) {
return
}
/*
func (x *Exec) SetURI(uri string) error {
resourceUri, e := url.Parse(uri)
if e == nil {
if resourceUri.Scheme == "exec" {
x.Id = filepath.Join(resourceUri.Hostname(), resourceUri.RequestURI())
} else {
e = fmt.Errorf("%w: %s is not an exec resource ", ErrInvalidResourceURI, uri)
}
}
return e
}
func (x *Exec) UseConfig(config data.ConfigurationValueGetter) {
x.config = config
}
func (x *Exec) ResolveId(ctx context.Context) string {
return ""
}
*/
func (x *Exec) Validate() (err error) {
var execJson []byte
if execJson, err = x.JSON(); err == nil {

View File

@ -29,6 +29,10 @@ import (
"embed"
)
const (
FileTypeName TypeName = "file"
)
// Describes the type of file the resource represents
type FileType string
@ -176,6 +180,16 @@ func (f *File) Notify(m *machine.EventMessage) {
switch m.On {
case machine.ENTERSTATEEVENT:
switch m.Dest {
case "start_stat":
if statErr := f.ReadStat(); statErr == nil {
if triggerErr := f.StateMachine().Trigger("exists"); triggerErr == nil {
return
}
} else {
if triggerErr := f.StateMachine().Trigger("notexists"); triggerErr == nil {
return
}
}
case "start_read":
if _,readErr := f.Read(ctx); readErr == nil {
if triggerErr := f.StateMachine().Trigger("state_read"); triggerErr == nil {

View File

@ -39,12 +39,13 @@ func init() {
func HTTPFactory(u *url.URL) data.Resource {
var err error
h := NewHTTP()
(&h.Endpoint).SetURL(u)
h.parsedURI = u
if h.reader, err = transport.NewReader(u); err != nil {
slog.Info("HTTP.Factory", "http", h, "url", u)
if err = h.SetParsedURI(u); err != nil {
panic(err)
}
if h.writer, err = transport.NewWriter(u); err != nil {
if err = h.Open(); err != nil {
panic(err)
}
return h
@ -58,7 +59,6 @@ type HTTPHeader struct {
// Manage the state of an HTTP endpoint
type HTTP struct {
*Common `yaml:",inline" json:",inline"`
parsedURI *url.URL `yaml:"-" json:"-"`
stater machine.Stater `yaml:"-" json:"-"`
client *http.Client `yaml:"-" json:"-"`
Endpoint folio.URI `yaml:"endpoint" json:"endpoint"`
@ -79,16 +79,37 @@ type HTTP struct {
}
func NewHTTP() *HTTP {
return &HTTP{ client: &http.Client{} }
return &HTTP{ client: &http.Client{}, Common: &Common{ includeQueryParamsInURI: true, resourceType: HTTPTypeName, SchemeCheck: func(scheme string) bool {
switch scheme {
case "http", "https":
return true
}
return false
} } }
}
func (h *HTTP) SetResourceMapper(resources data.ResourceMapper) {
h.Resources = resources
}
func (h *HTTP) Open() (err error) {
u := h.Common.parsedURI
if u != nil {
if h.reader, err = transport.NewReader(u); err != nil {
return
}
if h.writer, err = transport.NewWriter(u); err != nil {
return
}
} else {
err = fmt.Errorf("HTTP parsed URI is not set: %s", h.Endpoint)
}
return
}
func (h *HTTP) Clone() data.Resource {
return &HTTP {
Common: &Common{ includeQueryParamsInURI: true, resourceType: HTTPTypeName },
Common: h.Common.Clone(),
client: h.client,
Endpoint: h.Endpoint,
Headers: h.Headers,
@ -111,6 +132,16 @@ func (h *HTTP) Notify(m *machine.EventMessage) {
switch m.On {
case machine.ENTERSTATEEVENT:
switch m.Dest {
case "start_stat":
if statErr := h.ReadStat(); statErr == nil {
if triggerErr := h.StateMachine().Trigger("exists"); triggerErr == nil {
return
}
} else {
if triggerErr := h.StateMachine().Trigger("notexists"); triggerErr == nil {
return
}
}
case "start_read":
if _,readErr := h.Read(ctx); readErr == nil {
if triggerErr := h.StateMachine().Trigger("state_read"); triggerErr == nil {
@ -152,28 +183,26 @@ func (h *HTTP) Notify(m *machine.EventMessage) {
}
func (h *HTTP) URI() string {
return string(h.Endpoint)
}
func (h *HTTP) setParsedURI(uri folio.URI) error {
if parsed := uri.Parse(); parsed == nil {
return folio.ErrInvalidURI
} else {
h.parsedURI = parsed
}
return nil
return h.Endpoint.String()
}
func (h *HTTP) SetURI(uri string) (err error) {
v := folio.URI(uri)
if err = h.setParsedURI(v); err == nil {
h.Endpoint = v
if err = h.Common.SetURI(uri); err == nil {
h.Endpoint = h.Common.Uri
}
return
}
func (h *HTTP) UseConfig(config data.ConfigurationValueGetter) {
h.config = config
func (h *HTTP) SetParsedURI(u *url.URL) (err error) {
if err = h.Common.SetParsedURI(u); err == nil {
h.Endpoint = h.Common.Uri
}
return
}
func (h *HTTP) ReadStat() (err error) {
err = h.Open()
return
}
func (h *HTTP) JSON() ([]byte, error) {
@ -203,21 +232,23 @@ func (h *HTTP) Apply() error {
func (h *HTTP) Load(docData []byte, f codec.Format) (err error) {
if err = f.StringDecoder(string(docData)).Decode(h); err == nil {
err = h.setParsedURI(h.Endpoint)
err = h.Common.SetURI(string(h.Endpoint))
}
return
}
func (h *HTTP) LoadReader(r io.ReadCloser, f codec.Format) (err error) {
if err = f.Decoder(r).Decode(h); err == nil {
err = h.setParsedURI(h.Endpoint)
err = h.Common.SetURI(string(h.Endpoint))
//err = h.setParsedURI(h.Endpoint)
}
return
}
func (h *HTTP) LoadString(docData string, f codec.Format) (err error) {
if err = f.StringDecoder(docData).Decode(h); err == nil {
err = h.setParsedURI(h.Endpoint)
err = h.Common.SetURI(string(h.Endpoint))
//err = h.setParsedURI(h.Endpoint)
}
return
}
@ -227,6 +258,8 @@ func (h *HTTP) LoadDecl(yamlResourceDeclaration string) error {
}
func (h *HTTP) ResolveId(ctx context.Context) string {
_ = h.Common.SetURI(h.Endpoint.String())
slog.Info("HTTP.ResolveId()", "uri", h.Endpoint.String())
return h.Endpoint.String()
}
@ -251,7 +284,7 @@ func (h *HTTP) Create(ctx context.Context) (err error) {
slog.Error("HTTP.Create()", "http", h)
var contentReader io.ReadCloser
h.writer, err = transport.NewWriterWithContext(h.parsedURI, ctx)
h.writer, err = transport.NewWriterWithContext(h.Common.parsedURI, ctx)
if err != nil {
slog.Error("HTTP.Create()", "http", h, "error", err)
//panic(err)
@ -297,30 +330,6 @@ func (h *HTTP) Create(ctx context.Context) (err error) {
h.Status = h.writer.Status()
h.StatusCode = h.writer.StatusCode()
return
/*
body := strings.NewReader(h.Content)
req, reqErr := http.NewRequest("POST", h.Endpoint, body)
if reqErr != nil {
return reqErr
}
if tokenErr := h.ReadAuthorizationTokenFromConfig(req); tokenErr != nil {
slog.Error("ReadAuthorizationTokenFromConfig()", "error", tokenErr)
}
for _,header := range h.Headers {
req.Header.Add(header.Name, header.Value)
}
resp, err := h.client.Do(req)
h.Status = resp.Status
h.StatusCode = resp.StatusCode
if err != nil {
return err
}
defer resp.Body.Close()
return err
*/
}
func (h *HTTP) Update(ctx context.Context) error {
@ -447,7 +456,7 @@ func (h *HTTP) readThru(ctx context.Context) (contentReader io.ReadCloser, err e
contentReader, err = h.contentSourceReader()
} else {
if h.reader == nil {
h.reader, err = transport.NewReaderWithContext(h.parsedURI, ctx)
h.reader, err = transport.NewReaderWithContext(h.Common.parsedURI, ctx)
h.reader.SetGzip(false)
}
contentReader = h.reader
@ -475,14 +484,7 @@ func (h *HTTP) Read(ctx context.Context) (yamlData []byte, err error) {
}
}
slog.Info("HTTP.Read()", "reader", h.reader)
/*
copyBuffer := make([]byte, 32 * 1024)
_, writeErr := io.CopyBuffer(w, h.reader, copyBuffer)
if writeErr != nil {
return nil, fmt.Errorf("Http.GetContent(): CopyBuffer failed %v %v: %w", w, contentReader, writeErr)
}
*/
slog.Info("HTTP.Read()", "reader", h.reader, "http", h)
if err = h.SetContent(contentReader); err != nil {
return
}
@ -490,40 +492,6 @@ func (h *HTTP) Read(ctx context.Context) (yamlData []byte, err error) {
h.Status = h.reader.Status()
h.StatusCode = h.reader.StatusCode()
return yaml.Marshal(h)
/*
req, reqErr := http.NewRequestWithContext(ctx, "GET", h.Endpoint, nil)
if reqErr != nil {
return nil, reqErr
}
slog.Info("HTTP.Read() ", "request", req, "err", reqErr)
tokenErr := h.ReadAuthorizationTokenFromConfig(req)
if tokenErr != nil {
slog.Error("ReadAuthorizationTokenFromConfig()", "error", tokenErr)
}
if len(h.Headers) > 0 {
for _,header := range h.Headers {
req.Header.Add(header.Name, header.Value)
}
}
resp, err := h.client.Do(req)
slog.Info("Http.Read()", "response", resp, "error", err)
h.Status = resp.Status
h.StatusCode = resp.StatusCode
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) Delete(ctx context.Context) error {

View File

@ -89,7 +89,7 @@ type PKI struct {
}
func NewPKI() *PKI {
p := &PKI{ EncodingType: EncodingTypePem, Bits: 2048, Common: &Common{ resourceType: PKITypeName } }
p := &PKI{ EncodingType: EncodingTypePem, Bits: 2048, Common: NewCommon(PKITypeName, false) }
return p
}
@ -100,7 +100,7 @@ func (k *PKI) SetResourceMapper(resources data.ResourceMapper) {
func (k *PKI) Clone() data.Resource {
return &PKI {
Common: k.Common,
Common: k.Common.Clone(),
EncodingType: k.EncodingType,
//State: k.State,
}

View File

@ -22,6 +22,9 @@ func (t *Path) ValidPath() bool {
}
func (t *Path) Create() (err error) {
if t.ValidPath() && t.Exists() {
return
}
slog.Info("tempdir.Create()", "path", string(*t))
var TempDir string
TempDir, err = os.MkdirTemp("", string(*t))
@ -68,6 +71,11 @@ func (t *Path) CreateFile(name string, content string) (err error) {
return
}
func (t *Path) Exists() (bool) {
_, statErr := os.Stat(string(*t))
return ! os.IsNotExist(statErr)
}
func (t *Path) FilePath(name string) string {
return filepath.Join(string(*t), name)
}

View File

@ -84,7 +84,7 @@ func (h *HTTPConnection) Reader() io.ReadCloser {
}
func (h *HTTPConnection) Do() (err error) {
slog.Info("transport.HTTPConnection.Do()", "connection", h)
slog.Info("transport.HTTPConnection.Do()", "connection", h, "request", h.request)
h.response, err = h.Client.Do(h.request)
return
}
@ -205,6 +205,7 @@ func (h *HTTP) Gzip() bool {
func (h *HTTP) Reader() io.ReadCloser {
if h.get == nil {
h.get = NewHTTPConnection(h.Client)
if err := h.get.NewGetRequest(h.ctx, h.uri.String()); err != nil {
panic(err)
}