jx/cmd/cli/main.go

333 lines
8.1 KiB
Go
Raw Normal View History

2024-03-20 19:23:31 +00:00
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
2024-03-20 19:23:31 +00:00
package main
import (
2024-04-19 07:52:10 +00:00
"context"
"io"
2024-03-20 19:23:31 +00:00
"os"
"flag"
2024-04-03 18:47:55 +00:00
"log/slog"
2024-04-19 07:52:10 +00:00
_ "errors"
"fmt"
2024-03-20 19:23:31 +00:00
_ "gopkg.in/yaml.v3"
"decl/internal/resource"
"decl/internal/source"
"decl/internal/target"
"decl/internal/codec"
"net/url"
2024-03-20 19:23:31 +00:00
)
2024-04-19 07:52:10 +00:00
const (
FormatYaml = "yaml"
FormatJson = "json"
)
2024-03-20 19:23:31 +00:00
2024-04-23 22:35:08 +00:00
var (
version string
commit string
date string
)
2024-04-19 07:52:10 +00:00
var GlobalOformat *string
2024-04-25 07:45:05 +00:00
var GlobalOutput string
2024-04-19 07:52:10 +00:00
var GlobalQuiet *bool
2024-04-19 07:52:10 +00:00
var ImportMerge *bool
2024-04-21 06:13:17 +00:00
var ImportResource *string
2024-04-03 18:47:55 +00:00
2024-05-24 05:11:51 +00:00
var ApplyDelete *bool
2024-04-19 07:52:10 +00:00
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,
},
}
2024-04-23 22:35:08 +00:00
func VersionUsage() {
fmt.Println("jx")
fmt.Printf("version: %s\n", version)
fmt.Printf("commit: %s\n", commit)
fmt.Printf("date: %s\n", date)
}
2024-04-19 07:52:10 +00:00
func LoggerConfig() {
var programLevel = new(slog.LevelVar)
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel}))
slog.SetDefault(logger)
2024-05-24 05:11:51 +00:00
if debugLogging,ok := os.LookupEnv("JX_DEBUG"); ok && debugLogging != "" {
2024-04-19 07:52:10 +00:00
programLevel.Set(slog.LevelDebug)
} else {
programLevel.Set(slog.LevelError)
}
}
2024-04-19 07:52:10 +00:00
func LoadSourceURI(uri string) []*resource.Document {
slog.Info("loading ", "uri", uri)
if uri != "" {
ds, err := source.SourceTypes.New(uri)
if err != nil {
2024-05-26 09:30:31 +00:00
slog.Error("Failed loading document from source", "error", err)
2024-04-19 07:52:10 +00:00
}
extractDocuments, extractErr := ds.ExtractResources(nil)
if extractErr != nil {
2024-05-26 09:30:31 +00:00
slog.Error("Failed loading resources from source", "error", extractErr)
2024-04-19 07:52:10 +00:00
}
return extractDocuments
}
return []*resource.Document{ resource.NewDocument() }
}
func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
2024-04-21 06:13:17 +00:00
ImportResource = cmd.String("resource", "", "(uri) Add a resource to the document.")
2024-04-19 07:52:10 +00:00
ImportMerge = cmd.Bool("merge", false, "Merge resources into a single document.")
2024-04-22 06:11:17 +00:00
e := cmd.Parse(os.Args[2:])
if e != nil { // returns ErrHelp
return e
}
2024-04-19 07:52:10 +00:00
merged := resource.NewDocument()
documents := make([]*resource.Document, 0, 100)
for _,source := range cmd.Args() {
2024-04-21 06:13:17 +00:00
loaded := LoadSourceURI(source)
if loaded != nil {
documents = append(documents, loaded...)
}
2024-04-19 07:52:10 +00:00
}
2024-04-23 22:35:08 +00:00
2024-04-25 07:45:05 +00:00
/*
2024-04-19 07:52:10 +00:00
switch *GlobalOformat {
case FormatYaml:
encoder = resource.NewYAMLEncoder(output)
case FormatJson:
encoder = resource.NewJSONEncoder(output)
}
2024-04-25 07:45:05 +00:00
*/
2024-04-19 07:52:10 +00:00
2024-04-25 07:45:05 +00:00
slog.Info("main.ImportResource", "args", os.Args, "output", GlobalOutput)
outputTarget, err := target.TargetTypes.New(GlobalOutput)
if err != nil {
2024-05-26 09:30:31 +00:00
slog.Error("Failed opening target", "error", err)
2024-04-23 22:35:08 +00:00
}
2024-05-09 08:50:56 +00:00
defer outputTarget.Close()
2024-04-23 22:35:08 +00:00
2024-04-21 06:13:17 +00:00
if len(documents) == 0 {
documents = append(documents, resource.NewDocument())
}
2024-04-19 07:52:10 +00:00
for _,d := range documents {
if d != nil {
2024-04-21 06:13:17 +00:00
if *ImportResource != "" {
slog.Info("ImportResource", "resource", ImportResource)
resourceUri, uriErr := url.Parse(*ImportResource)
if uriErr != nil {
slog.Error("Failed adding resource", "error", uriErr)
}
if resourceUri.Scheme == "" {
resourceUri.Scheme = "file"
}
if addResourceErr := d.AddResource(resourceUri.String()); addResourceErr != nil {
2024-05-26 09:30:31 +00:00
slog.Error("Failed adding resource", "error", addResourceErr)
2024-04-21 06:13:17 +00:00
}
}
2024-04-19 07:52:10 +00:00
if *GlobalQuiet {
for _, dr := range d.Resources() {
2024-04-22 06:11:17 +00:00
if _,e := output.Write([]byte(dr.Resource().URI())); e != nil {
return e
}
2024-04-19 07:52:10 +00:00
}
} else {
if *ImportMerge {
merged.ResourceDecls = append(merged.ResourceDecls, d.ResourceDecls...)
} else {
2024-04-25 07:45:05 +00:00
slog.Info("main.ImportResource", "outputTarget", outputTarget, "type", outputTarget.Type())
if outputErr := outputTarget.EmitResources([]*resource.Document{d}, nil); outputErr != nil {
return outputErr
2024-04-19 07:52:10 +00:00
}
}
}
2024-04-19 07:52:10 +00:00
}
}
if *ImportMerge {
2024-04-25 07:45:05 +00:00
if outputErr := outputTarget.EmitResources([]*resource.Document{merged}, nil); outputErr != nil {
return outputErr
2024-04-19 07:52:10 +00:00
}
}
return err
}
func ApplySubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
2024-05-24 05:11:51 +00:00
ApplyDelete = cmd.Bool("delete", false, "Delete resources defined in the available documents.")
2024-04-22 06:11:17 +00:00
if e := cmd.Parse(os.Args[2:]); e != nil {
return e
}
var encoder codec.Encoder
2024-04-19 07:52:10 +00:00
documents := make([]*resource.Document, 0, 100)
for _,source := range cmd.Args() {
2024-04-21 06:13:17 +00:00
loaded := LoadSourceURI(source)
if loaded != nil {
documents = append(documents, loaded...)
}
2024-04-19 07:52:10 +00:00
}
2024-04-21 06:13:17 +00:00
slog.Info("main.Apply()", "documents", documents)
2024-04-19 07:52:10 +00:00
for _,d := range documents {
2024-05-24 05:11:51 +00:00
slog.Info("main.Apply()", "doc", d)
var overrideState string = ""
if *ApplyDelete {
overrideState = "delete"
}
d.ResolveIds(ctx)
if e := d.Apply(overrideState); e != nil {
slog.Info("main.Apply() error", "error", e)
2024-04-19 07:52:10 +00:00
return e
}
switch *GlobalOformat {
case FormatYaml:
encoder = codec.NewYAMLEncoder(output)
2024-04-19 07:52:10 +00:00
case FormatJson:
encoder = codec.NewJSONEncoder(output)
2024-04-19 07:52:10 +00:00
}
if *GlobalQuiet {
for _, dr := range d.Resources() {
2024-04-22 06:11:17 +00:00
if _,e := output.Write([]byte(dr.Resource().URI())); e != nil {
return e
}
2024-04-19 07:52:10 +00:00
}
} else {
if documentGenerateErr := encoder.Encode(d); documentGenerateErr != nil {
return documentGenerateErr
}
2024-04-19 07:52:10 +00:00
}
}
return err
}
func DiffSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
2024-04-22 06:11:17 +00:00
if e := cmd.Parse(os.Args[2:]); e != nil {
return e
}
2024-04-19 07:52:10 +00:00
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)
if rightSource == "" {
2024-05-06 00:48:54 +00:00
rightDocuments = append(rightDocuments, LoadSourceURI(leftSource)...)
slog.Info("jx diff clone", "docs", rightDocuments)
for i, doc := range rightDocuments {
2024-04-19 07:52:10 +00:00
if doc != nil {
2024-05-06 00:48:54 +00:00
leftDocuments = append(leftDocuments, doc.Clone())
2024-04-19 07:52:10 +00:00
for _,resourceDeclaration := range leftDocuments[i].Resources() {
if _, e := resourceDeclaration.Resource().Read(ctx); e != nil {
2024-05-06 00:48:54 +00:00
slog.Info("jx diff ", "err", e)
//return e
2024-04-19 07:52:10 +00:00
}
}
}
2024-04-19 07:52:10 +00:00
}
} else {
2024-05-06 00:48:54 +00:00
leftDocuments = append(leftDocuments, LoadSourceURI(leftSource)...)
2024-04-19 07:52:10 +00:00
rightDocuments = append(rightDocuments, LoadSourceURI(rightSource)...)
}
2024-05-06 00:48:54 +00:00
slog.Info("jx diff ", "right", rightDocuments, "left", leftDocuments)
2024-04-19 07:52:10 +00:00
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++
2024-04-19 07:52:10 +00:00
continue
2024-04-03 18:47:55 +00:00
}
2024-04-19 07:52:10 +00:00
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++
2024-04-03 18:47:55 +00:00
}
2024-04-19 07:52:10 +00:00
return err
}
2024-03-20 19:23:31 +00:00
2024-04-19 07:52:10 +00:00
func main() {
LoggerConfig()
if len(os.Args) < 2 {
fmt.Println("expected subcommands: diff, apply, import")
os.Exit(1)
}
for _,subCmd := range jxSubCommands {
cmdFlagSet := flag.NewFlagSet(subCmd.Name, flag.ExitOnError)
GlobalOformat = cmdFlagSet.String("oformat", "yaml", "Output serialization format")
2024-04-25 07:45:05 +00:00
cmdFlagSet.StringVar(&GlobalOutput, "output", "-", "Output target (default stdout)")
cmdFlagSet.StringVar(&GlobalOutput, "o", "-", "Output target (default stdout)")
2024-04-19 07:52:10 +00:00
GlobalQuiet = cmdFlagSet.Bool("quiet", false, "Generate terse output.")
switch subCmd.Name {
case "diff":
cmdFlagSet.Usage = func() {
fmt.Println("jx diff source [source2]")
cmdFlagSet.PrintDefaults()
2024-04-23 22:35:08 +00:00
VersionUsage()
}
2024-04-19 07:52:10 +00:00
case "apply":
cmdFlagSet.Usage = func() {
fmt.Println("jx diff source [source2]")
cmdFlagSet.PrintDefaults()
2024-04-23 22:35:08 +00:00
VersionUsage()
}
2024-04-19 07:52:10 +00:00
case "import":
cmdFlagSet.Usage = func() {
2024-04-25 07:45:05 +00:00
fmt.Println("jx import source...")
2024-04-19 07:52:10 +00:00
cmdFlagSet.PrintDefaults()
2024-04-23 22:35:08 +00:00
VersionUsage()
2024-04-19 07:52:10 +00:00
}
}
if os.Args[1] == subCmd.Name {
2024-04-22 06:43:58 +00:00
if e := subCmd.Run(cmdFlagSet, os.Stdout); e != nil {
2024-05-24 05:11:51 +00:00
slog.Error("Failed running command", "command", os.Args[1], "error", e)
2024-04-22 06:43:58 +00:00
}
2024-04-19 07:52:10 +00:00
return
}
2024-04-03 19:27:16 +00:00
}
2024-04-25 07:45:05 +00:00
flag.PrintDefaults()
VersionUsage()
os.Exit(1)
2024-03-20 19:23:31 +00:00
}