// Copyright 2024 Matthew Rich . All rights reserved. package resource import ( "context" "fmt" "gopkg.in/yaml.v3" "encoding/json" _ "log" "net/url" _ "os" _ "os/exec" _ "strings" "io" "gitea.rosskeen.house/rosskeen.house/machine" "decl/internal/codec" "decl/internal/command" "decl/internal/data" "decl/internal/folio" "errors" "log/slog" ) const ( ExecTypeName TypeName = "exec" ) type Exec struct { *Common `yaml:",inline" json:",inline"` stater machine.Stater `yaml:"-" json:"-"` Id string `yaml:"id,omitempty" json:"id,omitempty"` CreateTemplate *command.Command `yaml:"create,omitempty" json:"create,omitempty"` ReadTemplate *command.Command `yaml:"read,omitempty" json:"read,omitempty"` UpdateTemplate *command.Command `yaml:"update,omitempty" json:"update,omitempty"` DeleteTemplate *command.Command `yaml:"delete,omitempty" json:"delete,omitempty"` Resources data.ResourceMapper `yaml:"-" json:"-"` } func init() { folio.DocumentRegistry.ResourceTypes.Register([]string{"exec"}, func(u *url.URL) (res data.Resource) { x := NewExec() res = x if u != nil { uri := folio.CastParsedURI(u) if ri, ok := res.(data.ResourceInitializer); ok { if err := ri.Init(uri); err != nil { panic(err) } } else { if err := x.SetParsedURI(uri); err != nil { panic(err) } } } return }) } func NewExec() *Exec { e := &Exec{ Common: NewCommon(ExecTypeName, true) } return e } func (x *Exec) SetResourceMapper(resources data.ResourceMapper) { x.Resources = resources } func (x *Exec) Clone() data.Resource { return &Exec { Common: x.Common.Clone(), Id: x.Id, CreateTemplate: x.CreateTemplate, ReadTemplate: x.ReadTemplate, UpdateTemplate: x.UpdateTemplate, DeleteTemplate: x.DeleteTemplate, } } func (x *Exec) StateMachine() machine.Stater { if x.stater == nil { x.stater = ProcessMachine(x) } return x.stater } func (x *Exec) URI() string { return fmt.Sprintf("exec://%s", x.Id) } func (x *Exec) Init(u data.URIParser) (err error) { if u == nil { u = folio.URI(x.URI()).Parse() } err = x.SetParsedURI(u) x.Id = x.Common.Path return } func (x *Exec) SetParsedURI(uri data.URIParser) (err error) { err = x.Common.SetParsedURI(uri) x.Id = x.Common.Path return } func (x *Exec) Validate() (err error) { var execJson []byte if execJson, err = x.JSON(); err == nil { s := NewSchema(x.Type()) err = s.Validate(string(execJson)) } return err } func (x *Exec) Apply() error { return nil } func (x *Exec) Notify(m *machine.EventMessage) { ctx := context.Background() slog.Info("Notify()", "exec", x, "m", m) switch m.On { case machine.ENTERSTATEEVENT: switch m.Dest { case "start_read": if _, readErr := x.Read(ctx); readErr == nil { if triggerErr := x.StateMachine().Trigger("state_read"); triggerErr == nil { return } else { x.State = "absent" panic(triggerErr) } } else { x.State = "absent" if ! errors.Is(readErr, ErrResourceStateAbsent) { panic(readErr) } } case "start_create": if e := x.Create(ctx); e == nil { if triggerErr := x.stater.Trigger("created"); triggerErr == nil { return } } case "start_delete": if deleteErr := x.Delete(ctx); deleteErr == nil { if triggerErr := x.StateMachine().Trigger("deleted"); triggerErr == nil { return } else { x.State = "present" panic(triggerErr) } } else { x.State = "present" panic(deleteErr) } case "absent": x.State = "absent" case "present", "created", "read": x.State = "present" } case machine.EXITSTATEEVENT: } } func (x *Exec) Load(docData []byte, f codec.Format) (err error) { err = f.StringDecoder(string(docData)).Decode(x) return } func (x *Exec) LoadReader(r io.ReadCloser, f codec.Format) (err error) { err = f.Decoder(r).Decode(x) return } func (x *Exec) LoadString(docData string, f codec.Format) (err error) { err = f.StringDecoder(docData).Decode(x) return } func (x *Exec) LoadDecl(yamlResourceDeclaration string) error { return x.LoadString(yamlResourceDeclaration, codec.FormatYaml) } func (x *Exec) JSON() ([]byte, error) { return json.Marshal(x) } func (x *Exec) Type() string { return "exec" } func (x *Exec) Create(ctx context.Context) (err error) { x.CreateTemplate.Defaults() _, err = x.CreateTemplate.Execute(x) slog.Info("Exec.Create()", "resource", x, "error", err) return err } func (x *Exec) Read(ctx context.Context) ([]byte, error) { if x.ReadTemplate != nil { x.ReadTemplate.Defaults() } return yaml.Marshal(x) } func (x *Exec) Update(ctx context.Context) (err error) { return } func (x *Exec) Delete(ctx context.Context) (err error) { return }