add cli sub commands
This commit is contained in:
parent
efa3a47f76
commit
a6ea2e8c8c
36
cli_test.go
36
cli_test.go
@ -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)
|
||||
}
|
||||
|
292
cmd/cli/main.go
292
cmd/cli/main.go
@ -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")
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("expected subcommands: diff, apply, import")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
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.")
|
||||
|
||||
var resourceFile *os.File
|
||||
var inputFileErr error
|
||||
|
||||
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
29
go.mod
@ -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
79
go.sum
@ -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=
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
14
internal/signature/ident_test.go
Normal file
14
internal/signature/ident_test.go
Normal 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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 },
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user