2024-05-06 00:48:54 +00:00
|
|
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
|
|
|
|
|
|
|
package target
|
|
|
|
|
|
|
|
import (
|
|
|
|
_ "context"
|
|
|
|
_ "encoding/json"
|
|
|
|
_ "fmt"
|
|
|
|
_ "gopkg.in/yaml.v3"
|
|
|
|
"net/url"
|
|
|
|
"path/filepath"
|
|
|
|
"decl/internal/resource"
|
2024-05-14 18:26:05 +00:00
|
|
|
"decl/internal/codec"
|
2024-05-06 00:48:54 +00:00
|
|
|
"os"
|
|
|
|
"compress/gzip"
|
|
|
|
"io"
|
|
|
|
_ "errors"
|
|
|
|
"log/slog"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
FormatYaml = "yaml"
|
|
|
|
FormatJson = "json"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DeclFile struct {
|
|
|
|
Path string `yaml:"path" json:"path"`
|
|
|
|
Gzip bool `yaml:"gzip,omitempty" json:"gzip,omitempty"`
|
|
|
|
Format string `yaml:"format,omitempty" json:"format,omitempty"`
|
2024-05-14 18:26:05 +00:00
|
|
|
encoder codec.Encoder `yaml:"-" json:"-"`
|
2024-05-09 08:50:56 +00:00
|
|
|
closer func() error `yaml:"-" json:"-"`
|
2024-05-06 00:48:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewDeclFile() *DeclFile {
|
2024-05-09 08:50:56 +00:00
|
|
|
return &DeclFile{ Gzip: false, closer: func() error { return nil } }
|
2024-05-06 00:48:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewFileDocTarget(u *url.URL, format string, gzip bool, fileUri bool) DocTarget {
|
|
|
|
t := NewDeclFile()
|
|
|
|
t.Format = format
|
|
|
|
t.Gzip = gzip
|
|
|
|
if fileUri {
|
|
|
|
fileAbsolutePath, _ := filepath.Abs(filepath.Join(u.Hostname(), u.RequestURI()))
|
|
|
|
t.Path = fileAbsolutePath
|
|
|
|
} else {
|
|
|
|
t.Path = filepath.Join(u.Hostname(), u.Path)
|
|
|
|
}
|
2024-05-13 05:41:12 +00:00
|
|
|
if e := t.Open(); e != nil {
|
|
|
|
// open target
|
|
|
|
}
|
2024-05-06 00:48:54 +00:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
TargetTypes.Register([]string{"decl", "file"}, func(u *url.URL) DocTarget {
|
|
|
|
t := NewDeclFile()
|
|
|
|
if u.Path != "-" {
|
|
|
|
t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.Path))
|
|
|
|
} else {
|
|
|
|
t.Path = "-"
|
|
|
|
}
|
|
|
|
if _,ok := u.Query()["gzip"]; ok {
|
|
|
|
t.Gzip = true
|
|
|
|
}
|
|
|
|
if format,ok := u.Query()["format"]; ok {
|
|
|
|
switch format[0] {
|
|
|
|
case string(FormatYaml):
|
|
|
|
t.Format = FormatYaml
|
|
|
|
case string(FormatJson):
|
|
|
|
t.Format = FormatJson
|
|
|
|
}
|
|
|
|
}
|
2024-05-13 05:41:12 +00:00
|
|
|
if e := t.Open(); e != nil {
|
|
|
|
// open target
|
|
|
|
}
|
2024-05-06 00:48:54 +00:00
|
|
|
return t
|
|
|
|
})
|
|
|
|
|
|
|
|
TargetTypes.Register([]string{"yaml.gz","yml.gz"}, func(u *url.URL) DocTarget {
|
|
|
|
switch u.Scheme {
|
|
|
|
case "yaml", "yml", "file":
|
|
|
|
return NewFileDocTarget(u, FormatYaml, true, false)
|
|
|
|
}
|
|
|
|
return NewFileDocTarget(u, FormatYaml, true, false)
|
|
|
|
})
|
|
|
|
|
|
|
|
TargetTypes.Register([]string{"json.gz"}, func(u *url.URL) DocTarget {
|
|
|
|
switch u.Scheme {
|
|
|
|
case "json", "file":
|
|
|
|
return NewFileDocTarget(u, FormatJson, true, false)
|
|
|
|
}
|
|
|
|
return NewFileDocTarget(u, FormatJson, true, false)
|
|
|
|
})
|
|
|
|
|
|
|
|
TargetTypes.Register([]string{"yaml","yml"}, func(u *url.URL) DocTarget {
|
|
|
|
switch u.Scheme {
|
|
|
|
case "yaml", "yml", "file":
|
|
|
|
return NewFileDocTarget(u, FormatYaml, false, false)
|
|
|
|
}
|
|
|
|
return NewFileDocTarget(u, FormatYaml, false, false)
|
|
|
|
})
|
|
|
|
|
|
|
|
TargetTypes.Register([]string{"json"}, func(u *url.URL) DocTarget {
|
|
|
|
switch u.Scheme {
|
|
|
|
case "json", "file":
|
|
|
|
return NewFileDocTarget(u, FormatJson, false, false)
|
|
|
|
}
|
|
|
|
return NewFileDocTarget(u, FormatJson, false, false)
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-05-09 08:50:56 +00:00
|
|
|
func (d *DeclFile) Open() error {
|
2024-05-06 00:48:54 +00:00
|
|
|
var file *os.File
|
|
|
|
var fileErr error
|
2024-05-09 08:50:56 +00:00
|
|
|
var fileWriter io.WriteCloser
|
2024-05-06 00:48:54 +00:00
|
|
|
if d.Path == "" || d.Path == "-" {
|
|
|
|
file = os.Stdout
|
|
|
|
} else {
|
|
|
|
file, fileErr = os.Open(d.Path)
|
|
|
|
if fileErr != nil {
|
|
|
|
return fileErr
|
|
|
|
}
|
2024-05-09 08:50:56 +00:00
|
|
|
d.closer = func() error {
|
|
|
|
d.encoder.Close()
|
|
|
|
fileWriter.Close()
|
|
|
|
if file != fileWriter {
|
2024-05-06 00:48:54 +00:00
|
|
|
file.Close()
|
2024-05-09 08:50:56 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2024-05-06 00:48:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if d.Gzip {
|
|
|
|
fileWriter = gzip.NewWriter(file)
|
|
|
|
} else {
|
|
|
|
fileWriter = file
|
|
|
|
}
|
|
|
|
|
|
|
|
switch d.Format {
|
|
|
|
case FormatJson:
|
2024-05-14 18:26:05 +00:00
|
|
|
d.encoder = codec.NewJSONEncoder(fileWriter)
|
2024-05-06 00:48:54 +00:00
|
|
|
case FormatYaml:
|
|
|
|
fallthrough
|
|
|
|
default:
|
2024-05-14 18:26:05 +00:00
|
|
|
d.encoder = codec.NewYAMLEncoder(fileWriter)
|
2024-05-06 00:48:54 +00:00
|
|
|
}
|
|
|
|
|
2024-05-09 08:50:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DeclFile) Close() error {
|
|
|
|
return d.closer()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DeclFile) Type() string { return "decl" }
|
|
|
|
|
|
|
|
func (d *DeclFile) EmitResources(documents []*resource.Document, filter resource.ResourceSelector) (error) {
|
2024-05-06 00:48:54 +00:00
|
|
|
for _, doc := range documents {
|
|
|
|
emitDoc := resource.NewDocument()
|
|
|
|
if validationErr := doc.Validate(); validationErr != nil {
|
|
|
|
return validationErr
|
|
|
|
}
|
|
|
|
for _, declaration := range doc.Filter(filter) {
|
|
|
|
emitDoc.ResourceDecls = append(emitDoc.ResourceDecls, *declaration)
|
|
|
|
}
|
|
|
|
slog.Info("EmitResources", "doctarget", d, "encoder", d.encoder, "emit", emitDoc)
|
|
|
|
if documentErr := d.encoder.Encode(emitDoc); documentErr != nil {
|
|
|
|
slog.Info("EmitResources", "err", documentErr)
|
|
|
|
return documentErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|