Clean up error handling, logging, and completion

This commit is contained in:
Joel Elkins 2022-07-26 15:22:38 -05:00
parent 16cf7fdf41
commit c5dc8ba3ed
10 changed files with 70 additions and 65 deletions

View File

@ -36,17 +36,16 @@ var createCmd = &cobra.Command{
ValidArgsFunction: validNouns, ValidArgsFunction: validNouns,
Long: `Create containers specified by the arguments. Arguments can be either container Long: `Create containers specified by the arguments. Arguments can be either container
names or categories. Multiple arguments are supported.`, names or categories. Multiple arguments are supported.`,
RunE: func(_ *cobra.Command, args []string) error { Run: func(_ *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "CREATE", c.Name) fmt.Fprintln(output, "CREATE", cont.Name)
for _, c := range c.CreateCommands() { for _, c := range cont.CreateCommands() {
if err := c.Execute(output, fake); err != nil { if err := c.Execute(output, fake); err != nil {
return err cont.LogEntry().WithField("error", err).Errorln("Create failed")
} }
} }
} }
return nil
}, },
} }

View File

@ -36,17 +36,16 @@ var recreateCmd = &cobra.Command{
ValidArgsFunction: validNouns, ValidArgsFunction: validNouns,
Long: `Recreate container images, stopping and restarting if necessary. Arguments can be Long: `Recreate container images, stopping and restarting if necessary. Arguments can be
one or more container names or categories. If empty, "all" is assumed.`, one or more container names or categories. If empty, "all" is assumed.`,
RunE: func(_ *cobra.Command, args []string) error { Run: func(_ *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "RECREATE", c.Name) fmt.Fprintln(output, "RECREATE", cont.Name)
for _, c := range c.RecreateCommands() { for _, c := range cont.RecreateCommands() {
if err := c.Execute(output, fake); err != nil { if err := c.Execute(output, fake); err != nil {
return err cont.LogEntry().WithField("error", err).Errorln("Recreation failed")
} }
} }
} }
return nil
}, },
} }

View File

@ -36,17 +36,16 @@ var restartCmd = &cobra.Command{
ValidArgsFunction: validNouns, ValidArgsFunction: validNouns,
Long: `Stop configured containers (if running), then restart them. Arguments can be Long: `Stop configured containers (if running), then restart them. Arguments can be
one or more container names or categories. If empty, "all" is assumed.`, one or more container names or categories. If empty, "all" is assumed.`,
RunE: func(_ *cobra.Command, args []string) error { Run: func(_ *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "RESTART", c.Name) fmt.Fprintln(output, "RESTART", cont.Name)
for _, c := range c.RestartCommands() { for _, c := range cont.RestartCommands() {
if err := c.Execute(output, fake); err != nil { if err := c.Execute(output, fake); err != nil {
return err cont.LogEntry().WithField("error", err).Errorln("Restart failed")
} }
} }
} }
return nil
}, },
} }

View File

@ -25,7 +25,6 @@ import (
"fmt" "fmt"
"gitea.elkins.co/Networking/ccl/internal/pkg/config" "gitea.elkins.co/Networking/ccl/internal/pkg/config"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -40,14 +39,11 @@ var rmCmd = &cobra.Command{
If running, they will first be stopped.`, If running, they will first be stopped.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "REMOVE", c.Name) fmt.Fprintln(output, "REMOVE", cont.Name)
for _, cmd := range c.DestroyCommands() { for _, cmd := range cont.DestroyCommands() {
if err := cmd.Execute(output, fake); err != nil { if err := cmd.Execute(output, fake); err != nil {
log.WithFields(log.Fields{ cont.LogEntry().WithField("error", err).Errorln("Remove failed")
"container": c.Name,
"cmd": cmd,
}).Errorln("Remove failed")
} }
} }
} }

View File

@ -34,8 +34,9 @@ import (
// rootCmd represents the base command when called without any subcommands // rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "ccl", Use: "ccl",
Short: "Manage a set of pre-configured of podman containers", Short: "Manage a set of pre-configured of podman containers",
Version: Version,
Long: `ccl is a utility to manage a set of podman containers. Use with various subcommands Long: `ccl is a utility to manage a set of podman containers. Use with various subcommands
to define, start, stop, or update the container images. Configuration is read to define, start, stop, or update the container images. Configuration is read
from a toml configuration file, and the utility uses this information to from a toml configuration file, and the utility uses this information to
@ -44,10 +45,10 @@ execute the necessary podman commands.`,
} }
var ( var (
output io.Writer output io.Writer
verbose bool verbose bool
fake bool fake bool
socket string socket string
) )
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.
@ -61,6 +62,12 @@ func Execute() {
func init() { func init() {
cobra.OnInitialize(func() { cobra.OnInitialize(func() {
if verbose {
output = os.Stdout
} else {
output = io.Discard
}
// connect to podman // connect to podman
conn, err := bindings.NewConnection(context.Background(), socket) conn, err := bindings.NewConnection(context.Background(), socket)
if err != nil { if err != nil {
@ -72,15 +79,8 @@ func init() {
fmt.Fprintln(os.Stderr, "Warning: Could not initialize configuration:", err) fmt.Fprintln(os.Stderr, "Warning: Could not initialize configuration:", err)
} }
}) })
cobra.OnInitialize(func() {
if verbose {
output = os.Stdout
} else {
output = io.Discard
}
})
rootCmd.PersistentFlags().StringVarP(&config.ConfigFile, "config", "c", config.CONFIG_FILE_DEFAULT, "pathname of config file") rootCmd.PersistentFlags().StringVarP(&config.ConfigFile, "config", "c", config.CONFIG_FILE_DEFAULT, "pathname of config file")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show additional info from command execution") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "show additional info from command execution")
rootCmd.PersistentFlags().BoolVarP(&fake, "no-action", "n", false, "do not actually execute commands") rootCmd.PersistentFlags().BoolVarP(&fake, "no-action", "n", false, "do not actually execute commands")
rootCmd.PersistentFlags().StringVarP(&socket, "socket", "k", "unix:/run/podman/podman.sock", "socket address to which to connect") rootCmd.PersistentFlags().StringVarP(&socket, "socket", "k", "unix:///run/podman/podman.sock", "socket address to which to connect")
} }

View File

@ -36,17 +36,16 @@ var startCmd = &cobra.Command{
ValidArgsFunction: validNouns, ValidArgsFunction: validNouns,
Long: `Start configured containers. They must be created first. Arguments can be Long: `Start configured containers. They must be created first. Arguments can be
one or more container names or categories. If empty, "all" is assumed.`, one or more container names or categories. If empty, "all" is assumed.`,
RunE: func(_ *cobra.Command, args []string) error { Run: func(_ *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "START", c.Name) fmt.Fprintln(output, "START", cont.Name)
for _, c := range c.StartCommands() { for _, c := range cont.StartCommands() {
if err := c.Execute(output, fake); err != nil { if err := c.Execute(output, fake); err != nil {
return err cont.LogEntry().WithField("error", err).Errorln("Start failed")
} }
} }
} }
return nil
}, },
} }

View File

@ -36,17 +36,16 @@ var stopCmd = &cobra.Command{
ValidArgsFunction: validNouns, ValidArgsFunction: validNouns,
Long: `Stop configured containers if running. Arguments can be Long: `Stop configured containers if running. Arguments can be
one or more container names or categories. If empty, "all" is assumed.`, one or more container names or categories. If empty, "all" is assumed.`,
RunE: func(_ *cobra.Command, args []string) error { Run: func(_ *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "STOP", c.Name) fmt.Fprintln(output, "STOP", cont.Name)
for _, c := range c.StopCommands() { for _, c := range cont.StopCommands() {
if err := c.Execute(output, fake); err != nil { if err := c.Execute(output, fake); err != nil {
return err cont.LogEntry().WithField("error", err).Errorln("Stop failed")
} }
} }
} }
return nil
}, },
} }

View File

@ -36,17 +36,16 @@ var updateCmd = &cobra.Command{
ValidArgsFunction: validNouns, ValidArgsFunction: validNouns,
Long: `Update container images, stopping and restarting if necessary. Arguments can be Long: `Update container images, stopping and restarting if necessary. Arguments can be
one or more container names or categories. If empty, "all" is assumed.`, one or more container names or categories. If empty, "all" is assumed.`,
RunE: func(_ *cobra.Command, args []string) error { Run: func(_ *cobra.Command, args []string) {
conts := config.Union(args) conts := config.Union(args)
for _, c := range conts { for _, cont := range conts {
fmt.Fprintln(output, "UPDATE", c.Name) fmt.Fprintln(output, "UPDATE", cont.Name)
for _, c := range c.UpdateCommands() { for _, c := range cont.UpdateCommands() {
if err := c.Execute(output, fake); err != nil { if err := c.Execute(output, fake); err != nil {
return err cont.LogEntry().WithField("error", err).Errorln("Update failed")
} }
} }
} }
return nil
}, },
} }

View File

@ -8,6 +8,7 @@ import (
"gitea.elkins.co/Networking/ccl/internal/pkg/network" "gitea.elkins.co/Networking/ccl/internal/pkg/network"
"github.com/emirpasic/gods/sets/hashset" "github.com/emirpasic/gods/sets/hashset"
toml "github.com/pelletier/go-toml" toml "github.com/pelletier/go-toml"
log "github.com/sirupsen/logrus"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@ -21,22 +22,18 @@ var (
ConfigFile string = CONFIG_FILE_DEFAULT ConfigFile string = CONFIG_FILE_DEFAULT
networks = &[]network.Network{} networks = &[]network.Network{}
containers = &[]container.Container{} containers = &[]container.Container{}
categories = &[]string{}
) )
func Categories() []string { func Categories() []string {
if categories != nil { categories := []string{"all"}
return *categories
}
categories = &[]string{"all"}
gs := hashset.New() gs := hashset.New()
for _, c := range *containers { for _, c := range *containers {
gs.Add(c.Category) gs.Add(c.Category)
} }
for _, c := range gs.Values() { for _, c := range gs.Values() {
*categories = append(*categories, c.(string)) categories = append(categories, c.(string))
} }
return *categories return categories
} }
func Union(ids []string) (conts []container.Container) { func Union(ids []string) (conts []container.Container) {
@ -56,6 +53,11 @@ func Union(ids []string) (conts []container.Container) {
match := slices.IndexFunc(*containers, func(c container.Container) bool { return c.Name == name }) match := slices.IndexFunc(*containers, func(c container.Container) bool { return c.Name == name })
conts = append(conts, (*containers)[match]) conts = append(conts, (*containers)[match])
} }
if len(conts) == 0 {
log.WithFields(log.Fields{
"ids": ids,
}).Warnln("No matching containers")
}
return return
} }

View File

@ -92,6 +92,19 @@ func (c *Container) Init(conn context.Context, nets *[]network.Network) error {
return c.populateCData() return c.populateCData()
} }
func (c *Container) LogEntry() *log.Entry {
f := log.Fields{
"container": c.Name,
}
if c.cdata != nil {
f["id"] = c.cdata.ID
}
if c.cdata.State != nil {
f["state"] = c.cdata.State.Status
}
return log.WithFields(f)
}
func (c *Container) CreateCommands() []command.Command { func (c *Container) CreateCommands() []command.Command {
if c.Image == "" { if c.Image == "" {
log.WithField("container", c.Name).Error("Image not defined") log.WithField("container", c.Name).Error("Image not defined")