add cli sub commands
Some checks failed
Lint / golangci-lint (push) Failing after 9m49s
Declarative Tests / test (push) Failing after 1m15s

This commit is contained in:
Matthew Rich 2024-04-19 00:52:10 -07:00
parent efa3a47f76
commit a6ea2e8c8c
19 changed files with 612 additions and 139 deletions

View File

@ -8,13 +8,47 @@ import (
"os/exec"
"testing"
"errors"
"log/slog"
"net/http"
"net/http/httptest"
"fmt"
)
func TestCli(t *testing.T) {
if _, e := os.Stat("./decl"); errors.Is(e, os.ErrNotExist) {
t.Skip("cli not built")
}
yaml, cliErr := exec.Command("./decl", "-import-resource", "file://decl").Output()
yaml, cliErr := exec.Command("./decl", "-import-resource", "file://COPYRIGHT").Output()
slog.Info("TestCli", "err", cliErr)
assert.Nil(t, cliErr)
assert.NotEqual(t, "", string(yaml))
assert.Greater(t, len(yaml), 0)
}
func TestCliHTTPSource(t *testing.T) {
if _, e := os.Stat("./decl"); errors.Is(e, os.ErrNotExist) {
t.Skip("cli not built")
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, `
resources:
- type: file
attributes:
path: foo.txt
owner: nobody
group: nobody
mode: 0644
content: |
test file
content
state: present
`)
}))
defer ts.Close()
yaml, cliErr := exec.Command("./decl", "-resource-file", ts.URL, "-apply=false").Output()
slog.Info("TestCliHTTPSource", "err", cliErr)
assert.Nil(t, cliErr)
assert.NotEqual(t, "", string(yaml))
assert.Greater(t, len(yaml), 0)
}

View File

@ -3,97 +3,245 @@
package main
import (
"context"
"io"
"os"
"flag"
"log"
"log/slog"
"errors"
_ "fmt"
_ "errors"
"fmt"
_ "gopkg.in/yaml.v3"
"decl/internal/resource"
"decl/internal/source"
)
const (
FormatYaml = "yaml"
FormatJson = "json"
)
var GlobalOformat *string
var GlobalQuiet *bool
var ImportMerge *bool
var ctx context.Context = context.Background()
type RunCommand func(cmd *flag.FlagSet, output io.Writer) error
type SubCommand struct {
Name string
Run RunCommand
}
var jxSubCommands = []SubCommand {
{
Name: "diff",
Run: DiffSubCommand,
},
{
Name: "apply",
Run: ApplySubCommand,
},
{
Name: "import",
Run: ImportSubCommand,
},
}
func LoggerConfig() {
var programLevel = new(slog.LevelVar)
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel}))
slog.SetDefault(logger)
if debugLogging,ok := os.LookupEnv("DECL_DEBUG"); ok && debugLogging != "" {
programLevel.Set(slog.LevelDebug)
} else {
programLevel.Set(slog.LevelError)
}
}
func LoadSourceURI(uri string) []*resource.Document {
slog.Info("loading ", "uri", uri)
if uri != "" {
ds, err := source.SourceTypes.New(uri)
if err != nil {
log.Fatal(err)
}
extractDocuments, extractErr := ds.ExtractResources(nil)
if extractErr != nil {
log.Fatal(extractErr)
}
return extractDocuments
}
return []*resource.Document{ resource.NewDocument() }
}
func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
ImportMerge = cmd.Bool("merge", false, "Merge resources into a single document.")
cmd.Parse(os.Args[2:])
var encoder resource.Encoder
merged := resource.NewDocument()
documents := make([]*resource.Document, 0, 100)
for _,source := range cmd.Args() {
documents = append(documents, LoadSourceURI(source)...)
}
switch *GlobalOformat {
case FormatYaml:
encoder = resource.NewYAMLEncoder(output)
case FormatJson:
encoder = resource.NewJSONEncoder(output)
}
for _,d := range documents {
if d != nil {
if *GlobalQuiet {
for _, dr := range d.Resources() {
output.Write([]byte(dr.Resource().URI()))
}
} else {
if *ImportMerge {
merged.ResourceDecls = append(merged.ResourceDecls, d.ResourceDecls...)
slog.Info("merging", "doc", merged.ResourceDecls, "src", d.ResourceDecls)
} else {
if documentGenerateErr := encoder.Encode(d); documentGenerateErr != nil {
return documentGenerateErr
}
}
}
}
}
if *ImportMerge {
if documentGenerateErr := encoder.Encode(merged); documentGenerateErr != nil {
return documentGenerateErr
}
}
return err
}
func ApplySubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
cmd.Parse(os.Args[2:])
var encoder resource.Encoder
documents := make([]*resource.Document, 0, 100)
for _,source := range cmd.Args() {
documents = append(documents, LoadSourceURI(source)...)
}
for _,d := range documents {
if e := d.Apply(); e != nil {
return e
}
switch *GlobalOformat {
case FormatYaml:
encoder = resource.NewYAMLEncoder(output)
case FormatJson:
encoder = resource.NewJSONEncoder(output)
}
if *GlobalQuiet {
for _, dr := range d.Resources() {
output.Write([]byte(dr.Resource().URI()))
}
} else {
if documentGenerateErr := encoder.Encode(d); documentGenerateErr != nil {
return documentGenerateErr
}
}
}
return err
}
func DiffSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
cmd.Parse(os.Args[2:])
leftSource := cmd.Arg(0)
rightSource := cmd.Arg(1)
leftDocuments := make([]*resource.Document, 0, 100)
rightDocuments := make([]*resource.Document, 0, 100)
slog.Info("jx diff subcommand", "left", leftSource, "right", rightSource, "flagset", cmd)
leftDocuments = append(leftDocuments, LoadSourceURI(leftSource)...)
if rightSource == "" {
slog.Info("jx diff clone", "docs", leftDocuments)
for i, doc := range leftDocuments {
if doc != nil {
rightDocuments = append(rightDocuments, doc.Clone())
for _,resourceDeclaration := range leftDocuments[i].Resources() {
if _, e := resourceDeclaration.Resource().Read(ctx); e != nil {
return e
}
}
}
}
} else {
rightDocuments = append(rightDocuments, LoadSourceURI(rightSource)...)
}
index := 0
for {
if index >= len(rightDocuments) && index >= len(leftDocuments) {
break
}
if index >= len(rightDocuments) {
if _,e := leftDocuments[index].Diff(resource.NewDocument(), output); e != nil {
return e
}
index++
continue
}
if index >= len(leftDocuments) {
if _,e := resource.NewDocument().Diff(rightDocuments[index], output); e != nil {
return e
}
index++
continue
}
if _,e := leftDocuments[index].Diff(rightDocuments[index], output); e != nil {
return e
}
index++
}
return err
}
func main() {
var programLevel = new(slog.LevelVar)
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel}))
slog.SetDefault(logger)
if debugLogging,ok := os.LookupEnv("DECL_DEBUG"); ok && debugLogging != "" {
programLevel.Set(slog.LevelDebug)
} else {
programLevel.Set(slog.LevelError)
}
LoggerConfig()
file := flag.String("resource-file", "", "Resource file path")
resourceUri := flag.String("import-resource", "", "Add an existing resource")
flag.Parse()
if len(os.Args) < 2 {
fmt.Println("expected subcommands: diff, apply, import")
os.Exit(1)
}
var resourceFile *os.File
var inputFileErr error
for _,subCmd := range jxSubCommands {
cmdFlagSet := flag.NewFlagSet(subCmd.Name, flag.ExitOnError)
GlobalOformat = cmdFlagSet.String("oformat", "yaml", "Output serialization format")
GlobalQuiet = cmdFlagSet.Bool("quiet", false, "Generate terse output.")
slog.Info("args", "resource-file", *file, "import-resource", *resourceUri)
if *file == "-" {
if stdinInfo, stdinErr := os.Stdin.Stat(); stdinErr == nil {
if (stdinInfo.Mode() & os.ModeCharDevice) == 0 {
resourceFile = os.Stdin
}
} else {
inputFileErr = stdinErr
}
} else if *file != "" {
resourceFile,inputFileErr = os.Open(*file)
}
if inputFileErr != nil {
log.Fatal(inputFileErr)
}
documents := make([]*resource.Document, 0, 100)
decoder := resource.NewYAMLDecoder(resourceFile)
encoder := resource.NewYAMLEncoder(os.Stdout)
index := 0
slog.Info("loading resource document", "file", resourceFile)
documents = append(documents, resource.NewDocument())
if resourceFile != nil {
for {
d := documents[index]
e := decoder.Decode(d)
if errors.Is(e, io.EOF) {
if len(documents) > 1 {
documents[index] = nil
}
break
switch subCmd.Name {
case "diff":
cmdFlagSet.Usage = func() {
fmt.Println("jx diff source [source2]")
cmdFlagSet.PrintDefaults()
}
if e != nil {
log.Fatal(e)
case "apply":
cmdFlagSet.Usage = func() {
fmt.Println("jx diff source [source2]")
cmdFlagSet.PrintDefaults()
}
if validationErr := d.Validate(); validationErr != nil {
log.Fatal(validationErr)
case "import":
cmdFlagSet.Usage = func() {
fmt.Println("jx import source [source2]")
cmdFlagSet.PrintDefaults()
}
if applyErr := d.Apply(); applyErr != nil {
log.Fatal(applyErr)
}
documents = append(documents, resource.NewDocument())
index++
}
slog.Info("command", "command", subCmd)
if os.Args[1] == subCmd.Name {
subCmd.Run(cmdFlagSet, os.Stdout)
return
}
}
for _,document := range documents {
if document != nil {
if *resourceUri != "" {
slog.Info("importing resource", "resource", *resourceUri)
if addResourceErr := document.AddResource(*resourceUri); addResourceErr != nil {
log.Fatal(addResourceErr)
}
}
if documentGenerateErr := encoder.Encode(document); documentGenerateErr != nil {
log.Fatal(documentGenerateErr)
}
}
}
}

29
go.mod
View File

@ -2,13 +2,20 @@ module decl
go 1.21.1
require github.com/stretchr/testify v1.9.0
require (
github.com/docker/docker v25.0.5+incompatible
github.com/opencontainers/image-spec v1.1.0
github.com/sters/yaml-diff v1.3.2
github.com/stretchr/testify v1.9.0
github.com/xeipuuv/gojsonschema v1.2.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/docker v25.0.5+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/fatih/color v1.16.0 // indirect
@ -19,19 +26,21 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sters/yaml-diff v1.3.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/sys v0.17.0 // indirect
go.opentelemetry.io/otel v1.25.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
go.opentelemetry.io/otel/sdk v1.25.0 // indirect
go.opentelemetry.io/otel/trace v1.25.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
)

79
go.sum
View File

@ -1,12 +1,16 @@
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@ -22,37 +26,56 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sters/yaml-diff v1.3.2 h1:99Ke50QYFQYZjKMOiePxwyuQ+WeCvNy6cRooqdLs/ZE=
github.com/sters/yaml-diff v1.3.2/go.mod h1:86usbNZiUqke5wYjMxDVEjmvGjmY2FkMwOwe0A5zf68=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
@ -65,21 +88,33 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8=
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -89,13 +124,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -106,8 +143,18 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=

View File

@ -95,6 +95,38 @@ func NewContainer(containerClientApi ContainerClient) *Container {
}
}
func (c *Container) Clone() Resource {
return &Container {
Id: c.Id,
Name: c.Name,
Path: c.Path,
Cmd: c.Cmd,
Entrypoint: c.Entrypoint,
Args: c.Args,
Environment: c.Environment,
Image: c.Image,
ResolvConfPath: c.ResolvConfPath,
HostnamePath: c.HostnamePath,
HostsPath: c.HostsPath,
LogPath: c.LogPath,
Created: c.Created,
ContainerState: c.ContainerState,
RestartCount: c.RestartCount,
Driver: c.Driver,
Platform: c.Platform,
MountLabel: c.MountLabel,
ProcessLabel: c.ProcessLabel,
AppArmorProfile: c.AppArmorProfile,
ExecIDs: c.ExecIDs,
HostConfig: c.HostConfig,
GraphDriver: c.GraphDriver,
SizeRw: c.SizeRw,
SizeRootFs: c.SizeRootFs,
State: c.State,
apiClient: c.apiClient,
}
}
func (c *Container) URI() string {
return fmt.Sprintf("container://%s", c.Id)
}

View File

@ -41,6 +41,13 @@ func NewDeclaration() *Declaration {
return &Declaration{}
}
func (d *Declaration) Clone() *Declaration {
return &Declaration {
Type: d.Type,
Attributes: d.Attributes.Clone(),
}
}
func (d *Declaration) Load(r io.Reader) error {
c := NewYAMLDecoder(r)
return c.Decode(d)

View File

@ -4,11 +4,13 @@ package resource
import (
"encoding/json"
_ "fmt"
"fmt"
"gopkg.in/yaml.v3"
"io"
"log/slog"
_ "net/url"
"github.com/sters/yaml-diff/yamldiff"
"strings"
)
type Document struct {
@ -19,6 +21,15 @@ func NewDocument() *Document {
return &Document{}
}
func (d *Document) Clone() *Document {
clone := NewDocument()
clone.ResourceDecls = make([]Declaration, len(d.ResourceDecls))
for i, res := range d.ResourceDecls {
clone.ResourceDecls[i] = *res.Clone()
}
return clone
}
func (d *Document) Load(r io.Reader) error {
c := NewYAMLDecoder(r)
return c.Decode(d);
@ -94,3 +105,39 @@ func (d *Document) JSON() ([]byte, error) {
func (d *Document) YAML() ([]byte, error) {
return yaml.Marshal(d)
}
func (d *Document) Diff(with *Document, output io.Writer) (string, error) {
opts := []yamldiff.DoOptionFunc{}
if output == nil {
output = &strings.Builder{}
}
ydata, yerr := d.YAML()
if yerr != nil {
return "", yerr
}
yamlDiff,yamlDiffErr := yamldiff.Load(string(ydata))
if yamlDiffErr != nil {
return "", yamlDiffErr
}
wdata,werr := with.YAML()
if werr != nil {
return "", werr
}
withDiff,withDiffErr := yamldiff.Load(string(wdata))
if withDiffErr != nil {
return "", withDiffErr
}
for _,diff := range yamldiff.Do(yamlDiff, withDiff, opts...) {
slog.Info("Diff()", "diff", diff)
fmt.Printf("yaml %s with %s\n", yamlDiff, withDiff)
output.Write([]byte(diff.Dump()))
}
slog.Info("Document.Diff() ", "document.yaml", ydata, "with.yaml", wdata)
if stringOutput, ok := output.(*strings.Builder); ok {
return stringOutput.String(), nil
}
return "", nil
}

View File

@ -37,6 +37,17 @@ func NewExec() *Exec {
return &Exec{loader: YamlLoadDecl}
}
func (x *Exec) Clone() Resource {
return &Exec {
Id: x.Id,
CreateTemplate: x.CreateTemplate,
ReadTemplate: x.ReadTemplate,
UpdateTemplate: x.UpdateTemplate,
DeleteTemplate: x.DeleteTemplate,
State: x.State,
}
}
func (x *Exec) URI() string {
return fmt.Sprintf("exec://%s", x.Id)
}

View File

@ -31,6 +31,7 @@ const (
)
var ErrInvalidResourceURI error = errors.New("Invalid resource URI")
var ErrInvalidFileInfo error = errors.New("Invalid FileInfo")
func init() {
ResourceTypes.Register("file", func(u *url.URL) Resource {
@ -65,6 +66,22 @@ func NewFile() *File {
return f
}
func (f *File) Clone() Resource {
return &File {
Path: f.Path,
Owner: f.Owner,
Group: f.Group,
Mode: f.Mode,
Atime: f.Atime,
Ctime: f.Ctime,
Mtime: f.Mtime,
Content: f.Content,
Target: f.Target,
FileType: f.FileType,
State: f.State,
}
}
func (f *File) URI() string {
return fmt.Sprintf("file://%s", f.Path)
}
@ -176,6 +193,37 @@ func (f *File) NormalizePath() error {
return fileAbsErr
}
func (f *File) UpdateAttributesFromFileInfo(info os.FileInfo) error {
if info != nil {
f.Mtime = info.ModTime()
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
f.Atime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
f.Ctime = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
userId := strconv.Itoa(int(stat.Uid))
groupId := strconv.Itoa(int(stat.Gid))
fileUser, userErr := user.LookupId(userId)
if userErr != nil { //UnknownUserIdError
//panic(userErr)
f.Owner = userId
} else {
f.Owner = fileUser.Username
}
fileGroup, groupErr := user.LookupGroupId(groupId)
if groupErr != nil {
//panic(groupErr)
f.Group = groupId
} else {
f.Group = fileGroup.Name
}
}
f.Mode = fmt.Sprintf("%04o", info.Mode().Perm())
f.FileType.SetMode(info.Mode())
return nil
}
return ErrInvalidFileInfo
}
func (f *File) ReadStat() error {
info, e := os.Lstat(f.Path)
if e != nil {
@ -183,32 +231,7 @@ func (f *File) ReadStat() error {
return e
}
f.Mtime = info.ModTime()
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
f.Atime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
f.Ctime = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec))
userId := strconv.Itoa(int(stat.Uid))
groupId := strconv.Itoa(int(stat.Gid))
fileUser, userErr := user.LookupId(userId)
if userErr != nil { //UnknownUserIdError
//panic(userErr)
f.Owner = userId
} else {
f.Owner = fileUser.Username
}
fileGroup, groupErr := user.LookupGroupId(groupId)
if groupErr != nil {
//panic(groupErr)
f.Group = groupId
} else {
f.Group = fileGroup.Name
}
}
f.Mode = fmt.Sprintf("%04o", info.Mode().Perm())
f.FileType.SetMode(info.Mode())
return nil
return f.UpdateAttributesFromFileInfo(info)
}
func (f *File) Read(ctx context.Context) ([]byte, error) {

View File

@ -207,6 +207,17 @@ func TestFileNormalizePath(t *testing.T) {
assert.Equal(t, absFile, f.Path)
}
func TestFileUpdateAttributesFromFileInfo(t *testing.T) {
f := NewFile()
assert.NotNil(t, f)
info, e := os.Lstat(TempDir)
assert.Nil(t, e)
f.UpdateAttributesFromFileInfo(info)
assert.Equal(t, DirectoryFile, f.FileType)
}
func TestFileReadStat(t *testing.T) {
ctx := context.Background()
link := filepath.Join(TempDir, "link.txt")

View File

@ -45,6 +45,16 @@ func NewHTTP() *HTTP {
return &HTTP{ client: &http.Client{} }
}
func (h *HTTP) Clone() Resource {
return &HTTP {
client: h.client,
Endpoint: h.Endpoint,
Headers: h.Headers,
Body: h.Body,
State: h.State,
}
}
func (h *HTTP) URI() string {
return h.Endpoint
}

View File

@ -123,6 +123,21 @@ func NewNetworkRoute() *NetworkRoute {
return &NetworkRoute{Rtid: NetworkRouteTableMain}
}
func (n *NetworkRoute) Clone() Resource {
return &NetworkRoute {
Id: n.Id,
To: n.To,
Interface: n.Interface,
Gateway: n.Gateway,
Metric: n.Metric,
Rtid: n.Rtid,
RouteType: n.RouteType,
Scope: n.Scope,
Proto: n.Proto,
State: n.State,
}
}
func (n *NetworkRoute) URI() string {
return fmt.Sprintf("route://%s", n.Id)
}

View File

@ -82,6 +82,18 @@ func NewPackage() *Package {
return &Package{}
}
func (p *Package) Clone() Resource {
newp := &Package {
Name: p.Name,
Required: p.Required,
Version: p.Version,
PackageType: p.PackageType,
State: p.State,
}
newp.CreateCommand, newp.ReadCommand, newp.UpdateCommand, newp.DeleteCommand = newp.PackageType.NewCRUD()
return newp
}
func (p *Package) URI() string {
return fmt.Sprintf("package://%s?version=%s&type=%s", p.Name, p.Version, p.PackageType)
}
@ -301,18 +313,26 @@ func NewAptReadCommand() *Command {
value := strings.TrimSpace(fieldKeyValue[1])
switch key {
case "Package":
if value == p.Name {
p.State = "present"
} else {
if value != p.Name {
p.State = "absent"
return nil
}
case "Status":
statusFields := strings.SplitN(value, " ", 3)
if len(statusFields) > 1 {
if statusFields[2] == "installed" {
p.State = "present"
} else {
p.State = "absent"
}
}
case "Version":
p.Version = value
}
}
}
}
slog.Info("Extract()", "package", p)
return nil
}
return c

View File

@ -14,12 +14,13 @@ import (
type Resource interface {
Type() string
URI() string
//SetURI(string) error
SetURI(string) error
ResolveId(context.Context) string
ResourceLoader
StateTransformer
ResourceReader
ResourceValidator
Clone() Resource
}
type ResourceValidator interface {

View File

@ -42,6 +42,32 @@ func init() {
})
}
func (u *User) Clone() Resource {
return &User {
Name: u.Name,
UID: u.UID,
Group: u.Group,
Groups: u.Groups,
Gecos: u.Gecos,
Home: u.Home,
CreateHome: u.CreateHome,
Shell: u.Shell,
State: u.State,
}
}
func (u *User) SetURI(uri string) error {
resourceUri, e := url.Parse(uri)
if e == nil {
if resourceUri.Scheme == "user" {
u.Name = resourceUri.Hostname()
} else {
e = fmt.Errorf("%w: %s is not a user", ErrInvalidResourceURI, uri)
}
}
return e
}
func (u *User) URI() string {
return fmt.Sprintf("user://%s", u.Name)
}

View File

@ -0,0 +1,14 @@
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
package signature
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestNewIdent(t *testing.T) {
i := NewIdent()
assert.NotNil(t, i)
}

View File

@ -8,8 +8,26 @@ _ "fmt"
"github.com/stretchr/testify/assert"
_ "log"
"testing"
"os"
"log"
)
var TempDir string
func TestMain(m *testing.M) {
var err error
TempDir, err = os.MkdirTemp("", "testdocsourcefile")
if err != nil || TempDir == "" {
log.Fatal(err)
}
rc := m.Run()
os.RemoveAll(TempDir)
os.Exit(rc)
}
func TestNewDocSource(t *testing.T) {
resourceUri := "tar://foo"
testFile := NewDocSource(resourceUri)

View File

@ -46,9 +46,9 @@ func (h *HTTP) ExtractResources(filter ResourceSelector) ([]*resource.Document,
return documents, err
}
defer resp.Body.Close()
signature := resp.Header.Get("Signature")
documentSignature := resp.Header.Get("Signature")
hash := sha256.New()
sumReadData := iofilter.New(resp.Body, func(p []byte, readn int, readerr error) (n int, err error) {
sumReadData := iofilter.NewReader(resp.Body, func(p []byte, readn int, readerr error) (n int, err error) {
hash.Write(p)
return
})
@ -79,9 +79,9 @@ func (h *HTTP) ExtractResources(filter ResourceSelector) ([]*resource.Document,
index++
}
if signature != "" {
sig := signature.&Ident{}
sig.VerifySum(hash.Sum(nil), signature)
if documentSignature != "" {
sig := &signature.Ident{}
sig.VerifySum(hash.Sum(nil), []byte(documentSignature))
}
return documents, nil

View File

@ -13,23 +13,23 @@ _ "context"
type MockDocSource struct {
InjectType func() string
InjectExtractResources func(uri string, filter ResourceSelector) ([]*resource.Document, error)
InjectExtractResources func(filter ResourceSelector) ([]*resource.Document, error)
}
func (m *MockDocSource) Type() string { return m.InjectType() }
func (m *MockDocSource) ExtractResources(uri string, filter ResourceSelector) ([]*resource.Document, error) { return m.InjectExtractResources(uri, filter) }
func (m *MockDocSource) ExtractResources(filter ResourceSelector) ([]*resource.Document, error) { return m.InjectExtractResources(filter) }
func NewFooDocSource() DocSource {
return &MockDocSource{
InjectType: func() string { return "foo" },
InjectExtractResources: func(uri string, filter ResourceSelector) ([]*resource.Document, error) { return nil,nil },
InjectExtractResources: func(filter ResourceSelector) ([]*resource.Document, error) { return nil,nil },
}
}
func NewFileDocSource() DocSource {
return &MockDocSource{
InjectType: func() string { return "file" },
InjectExtractResources: func(uri string, filter ResourceSelector) ([]*resource.Document, error) { return nil,nil },
InjectExtractResources: func(filter ResourceSelector) ([]*resource.Document, error) { return nil,nil },
}
}