From b91eb62c34686394af3d689c99d91b584d3dc030 Mon Sep 17 00:00:00 2001 From: "Joel D. Elkins" Date: Wed, 17 Aug 2022 02:24:48 -0500 Subject: [PATCH] Huge amount of linting Linting --- cmd/create.go | 2 +- cmd/execute.go | 10 ++- cmd/pull.go | 2 +- cmd/recreate.go | 6 +- cmd/restart.go | 4 +- cmd/rm.go | 4 +- cmd/root.go | 17 +++-- cmd/start.go | 4 +- cmd/stop.go | 2 +- cmd/update.go | 4 +- cmd/version.go | 6 +- internal/pkg/command/command.go | 110 +++++++++++++++++++--------- internal/pkg/config/config.go | 72 ++++++++++++++---- internal/pkg/container/container.go | 73 ++++++++++++------ 14 files changed, 214 insertions(+), 102 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 59af97e..8f5f5c9 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -38,7 +38,7 @@ var createCmd = &cobra.Command{ names or categories. Multiple arguments are supported.`, Run: func(_ *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.CreateCommands() }, 0) + execForEach(conts, func(c *container.Container) command.Set { return c.CreateCommands() }, 0) }, } diff --git a/cmd/execute.go b/cmd/execute.go index 7c1b1ed..0dad945 100644 --- a/cmd/execute.go +++ b/cmd/execute.go @@ -34,12 +34,12 @@ import ( "golang.org/x/exp/slices" ) -func execForEach(tgts []container.Container, getSet func(*container.Container) command.CommandSet, groupScale int) { +func execForEach(tgts []*container.Container, getSet func(*container.Container) command.Set, groupScale int) { runLevel := make(map[int][]*container.Container) for i := range tgts { rl := tgts[i].StartGroup * groupScale - runLevel[rl] = append(runLevel[rl], &tgts[i]) + runLevel[rl] = append(runLevel[rl], tgts[i]) } rls := maps.Keys(runLevel) @@ -58,8 +58,10 @@ func execForEach(tgts []container.Container, getSet func(*container.Container) c wg := new(sync.WaitGroup) for i := range cs { wg.Add(1) - go func(cont *container.Container, set command.CommandSet) { + cont := cs[i] + go func() { defer wg.Done() + set := getSet(cont) for _, cmd := range set.Commands { if err := cmd.Execute(output, fake, set.ID); err != nil { cont.LogEntry().WithFields(log.Fields{ @@ -69,7 +71,7 @@ func execForEach(tgts []container.Container, getSet func(*container.Container) c return } } - }(cs[i], getSet(cs[i])) + }() } wg.Wait() if r != 0 { diff --git a/cmd/pull.go b/cmd/pull.go index 0acc301..b92747e 100644 --- a/cmd/pull.go +++ b/cmd/pull.go @@ -39,7 +39,7 @@ affected: the old image will still remain, though untagged, and any defined cont will still use it.`, Run: func(cmd *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.PullCommands() }, 0) + execForEach(conts, func(c *container.Container) command.Set { return c.PullCommands() }, 0) }, } diff --git a/cmd/recreate.go b/cmd/recreate.go index 24136be..2112a50 100644 --- a/cmd/recreate.go +++ b/cmd/recreate.go @@ -38,9 +38,9 @@ var recreateCmd = &cobra.Command{ one or more container names or categories. If empty, "all" is assumed.`, Run: func(_ *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.StopCommands() }, -1) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.RecreateCommands() }, 0) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.ConditionalStartCommands() }, 1) + execForEach(conts, func(c *container.Container) command.Set { return c.StopCommands() }, -1) + execForEach(conts, func(c *container.Container) command.Set { return c.RecreateCommands() }, 0) + execForEach(conts, func(c *container.Container) command.Set { return c.ConditionalStartCommands() }, 1) }, } diff --git a/cmd/restart.go b/cmd/restart.go index 642cfa7..619d505 100644 --- a/cmd/restart.go +++ b/cmd/restart.go @@ -38,8 +38,8 @@ var restartCmd = &cobra.Command{ one or more container names or categories. If empty, "all" is assumed.`, Run: func(_ *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.StopCommands() }, -1) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.StartCommands() }, 1) + execForEach(conts, func(c *container.Container) command.Set { return c.StopCommands() }, -1) + execForEach(conts, func(c *container.Container) command.Set { return c.StartCommands() }, 1) }, } diff --git a/cmd/rm.go b/cmd/rm.go index 97b5996..967b967 100644 --- a/cmd/rm.go +++ b/cmd/rm.go @@ -39,8 +39,8 @@ var rmCmd = &cobra.Command{ If running, they will first be stopped.`, Run: func(cmd *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.StopCommands() }, -1) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.RemoveCommands() }, 0) + execForEach(conts, func(c *container.Container) command.Set { return c.StopCommands() }, -1) + execForEach(conts, func(c *container.Container) command.Set { return c.RemoveCommands() }, 0) }, } diff --git a/cmd/root.go b/cmd/root.go index 9c468f5..d7d2276 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -38,7 +38,7 @@ import ( var rootCmd = &cobra.Command{ Use: "ccl", Short: "Manage a set of pre-configured of podman containers", - Version: Version, + Version: version, 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 from a toml configuration file, and the utility uses this information to @@ -52,9 +52,9 @@ execute the necessary podman commands.`, requireConn := []string{"create", "pull", "recreate", "restart", "rm", "start", "stop", "update"} if slices.Contains(requireConn, cmd.Name()) { // connect to podman - ConnectMust() + connectMust() } else { - if err := Connect(); err != nil { + if err := connect(); err != nil { log.WithField("error", err).Warnln("Connection failed") } } @@ -84,14 +84,15 @@ func Execute() { } } -func Connect() error { +func connect() error { var err error - conn, err = bindings.NewConnection(context.WithValue(context.Background(), "output", output), socket) + key := struct{ string }{"output"} + conn, err = bindings.NewConnection(context.WithValue(context.Background(), key, output), socket) return err } -func ConnectMust() { - if err := Connect(); err != nil { +func connectMust() { + if err := connect(); err != nil { log.WithField("error", err).Errorf("Could not connect") os.Exit(1) } @@ -105,7 +106,7 @@ func init() { 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.ConfigFileDefault, "pathname of config file") 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(&incDisabled, "include-disabled", "a", false, "include category=. containers in actions") diff --git a/cmd/start.go b/cmd/start.go index 946c1a0..d5e62b2 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -36,9 +36,9 @@ var startCmd = &cobra.Command{ ValidArgsFunction: validNouns, Long: `Start configured containers. They must be created first. Arguments can be one or more container names or categories. If empty, "all" is assumed.`, - Run: func(_ *cobra.Command, args []string) { + Run: func(_ *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.StartCommands() }, 1) + execForEach(conts, func(c *container.Container) command.Set { return c.StartCommands() }, 1) }, } diff --git a/cmd/stop.go b/cmd/stop.go index cbbca0c..7329169 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -38,7 +38,7 @@ var stopCmd = &cobra.Command{ one or more container names or categories. If empty, "all" is assumed.`, Run: func(_ *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.StopCommands() }, -1) + execForEach(conts, func(c *container.Container) command.Set { return c.StopCommands() }, -1) }, } diff --git a/cmd/update.go b/cmd/update.go index db0fd10..689f50e 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -38,8 +38,8 @@ var updateCmd = &cobra.Command{ one or more container names or categories. If empty, "all" is assumed.`, Run: func(_ *cobra.Command, args []string) { conts := config.Union(args, contMask) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.UpdateCommands() }, -1) - execForEach(conts, func(c *container.Container) command.CommandSet { return c.ConditionalStartCommands() }, 1) + execForEach(conts, func(c *container.Container) command.Set { return c.UpdateCommands() }, -1) + execForEach(conts, func(c *container.Container) command.Set { return c.ConditionalStartCommands() }, 1) }, } diff --git a/cmd/version.go b/cmd/version.go index b2a0863..921ef7f 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -35,14 +35,14 @@ var versionCmd = &cobra.Command{ Short: "Show version info", Long: `Output the ccl binary's version`, Run: func(cmd *cobra.Command, _ []string) { - fmt.Println("ccl version", Version) + fmt.Println("ccl version", version) }, } var ( //go:embed version.txt - version string - Version string = strings.TrimSpace(version) + rawVersion string + version = strings.TrimSpace(rawVersion) ) func init() { diff --git a/internal/pkg/command/command.go b/internal/pkg/command/command.go index b975b5c..768a1f6 100644 --- a/internal/pkg/command/command.go +++ b/internal/pkg/command/command.go @@ -1,4 +1,7 @@ /* +Package command is used to manage the container management instructions required to carry out +tasks desired by the user, such as "create" + Copyright © 2022 Joel D. Elkins Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,46 +30,64 @@ import ( "os/exec" ) -type CommandType int +// Type is one of the Ct... constants, indicating the underlying method +// for executing a command. +type cType int +// Types of commands: +// - CtNop: No operation; an empty placeholder +// - CtShell: A shell command -- string to be fed to /bin/sh -c +// - CtFunc: A go function of type `func() error` +// - CtIndirect: A reference to another command. When executed, the underlying command is executed. +// - CtSet: An ordered list of commands +// - CtDebug: A debug message is printed upon execution, but does no other action +// - CtConditional: A function and two commands are provided. When evaluated, +// the function, having signature `func() bool`, is executed. If the result is +// true, then the first command is executed, and if false the second one. const ( - CT_NOP CommandType = iota - CT_SHELL - CT_FUNC - CT_INDIRECT - CT_SET - CT_DEBUG - CT_CONDITIONAL + ctNop cType = iota + ctShell + ctFunc + ctIndirect + ctSet + ctDebug + ctConditional ) -func (ct CommandType) String() string { +func (ct cType) String() string { switch ct { - case CT_NOP: + case ctNop: return "NOP" - case CT_SHELL: + case ctShell: return "SHELL" - case CT_FUNC: + case ctFunc: return "FUNC" - case CT_INDIRECT: + case ctIndirect: return "INDIRECT" - case CT_SET: + case ctSet: return "SET" - case CT_DEBUG: + case ctDebug: return "DEBUG" - case CT_CONDITIONAL: + case ctConditional: return "CONDITIONAL" default: return "UNKOWN" } } +// Command combines a Type with an `interface{}`, representing a task (or a set +// of tasks, in the case of CtSet) to be executed. Create a Command with one of +// the `NewXxxCommand` functions. type Command struct { - Type CommandType + Type cType Command interface{} } + +// Commands reflects an ordered grouping of `Command`s type Commands []Command -type CommandSet struct{ +// Set bundes a `Commands` object with an arbitrary ID for verbose output +type Set struct { ID string Commands } @@ -83,32 +104,46 @@ type conditional struct { ElseCmd Command } +// NewShell creates a new shell-type command from a string. Upon execution, the +// string will be fed to `/bin/sh -c` func NewShell(cmd string) Command { - return Command{CT_SHELL, cmd} + return Command{ctShell, cmd} } +// NewFunc creates a command consisting of go function of type `func() error` func NewFunc(name string, f func() error) Command { - return Command{CT_FUNC, errFunc{name, f}} + return Command{ctFunc, errFunc{name, f}} } +// NewIndirect creates a reference to another command. When executed, the underlying +// command is executed. func NewIndirect(c Command) Command { - return Command{CT_INDIRECT, c} + return Command{ctIndirect, c} } -func NewSet(cs CommandSet) Command { - return Command{CT_SET, cs.Commands} +// NewSet creates a single command containing an ordered list of commands. These +// commands will be executed in order with this set-type command is executed. +func NewSet(cs Set) Command { + return Command{ctSet, cs.Commands} } +// NewDebug returns a command that prints a debug message upon execution, but +// does no other action func NewDebug(msg string) Command { - return Command{CT_DEBUG, msg} + return Command{ctDebug, msg} } +// NewNop returns a placeholder command. When executed, nothing is done other +// than to note that it was encountered in the output. func NewNop() Command { - return Command{CT_NOP, nil} + return Command{ctNop, nil} } +// NewConditional takes a function and two commands as arguments. When evaluated, +// the function, having signature `func() bool`, is executed. If the result is +// true, then the first command is executed, and if false the second one. func NewConditional(name string, ifPart func() bool, thenPart Command, elsePart Command) Command { - return Command{CT_CONDITIONAL, conditional{ + return Command{ctConditional, conditional{ Name: name, Condition: ifPart, ThenCmd: thenPart, @@ -116,7 +151,9 @@ func NewConditional(name string, ifPart func() bool, thenPart Command, elsePart }} } -func (cmds CommandSet) Execute(output io.Writer, fake bool) error { +// Execute the Set with the privileges of the user running the process. +// Warning: in the case of a shell command, this could do anything. +func (cmds Set) Execute(output io.Writer, fake bool) error { for _, c := range cmds.Commands { if err := c.Execute(output, fake, cmds.ID); err != nil { return err @@ -125,11 +162,13 @@ func (cmds CommandSet) Execute(output io.Writer, fake bool) error { return nil } +// Execute the Command with the privileges of the user running the process. +// Warning: in the case of a shell command, this could do anything. func (c Command) Execute(output io.Writer, fake bool, commandSetID string) error { switch c.Type { - case CT_NOP: + case ctNop: fmt.Fprintf(output, "%s: %s\n", commandSetID, c.Type) - case CT_SHELL: + case ctShell: cmd := c.Command.(string) fmt.Fprintf(output, "%s: %s sh -c \"%s\"\n", commandSetID, c.Type, cmd) if !fake { @@ -137,7 +176,7 @@ func (c Command) Execute(output io.Writer, fake bool, commandSetID string) error fmt.Fprint(output, string(out)) return err } - case CT_FUNC: + case ctFunc: ef := c.Command.(errFunc) fmt.Fprintf(output, "%s: %s %s\n", commandSetID, c.Type, ef.Name) if !fake { @@ -146,10 +185,10 @@ func (c Command) Execute(output io.Writer, fake bool, commandSetID string) error } return nil } - case CT_INDIRECT: + case ctIndirect: ct := c.Command.(Command) return ct.Execute(output, fake, commandSetID) - case CT_SET: + case ctSet: cs := c.Command.(Commands) for i := range cs { err := cs[i].Execute(output, fake, commandSetID) @@ -157,7 +196,7 @@ func (c Command) Execute(output io.Writer, fake bool, commandSetID string) error return err } } - case CT_CONDITIONAL: + case ctConditional: cond := c.Command.(conditional) if fake { // in a fake setting, we don't know which branch will be followed, @@ -173,11 +212,10 @@ func (c Command) Execute(output io.Writer, fake bool, commandSetID string) error fmt.Fprintf(output, "%s: %s %s: %v\n", commandSetID, c.Type, cond.Name, branch) if branch { return cond.ThenCmd.Execute(output, fake, commandSetID) - } else { - return cond.ElseCmd.Execute(output, fake, commandSetID) } + return cond.ElseCmd.Execute(output, fake, commandSetID) } - case CT_DEBUG: + case ctDebug: msg := c.Command.(string) fmt.Fprintf(output, "%s: %s %s\n", commandSetID, c.Type, msg) } diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index c5b3b80..6b4e79f 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -1,3 +1,27 @@ +/* +Package config is used manage initialize and house the main program data +structures, as well as marshalling the configuration to and from toml. + +Copyright © 2022 Joel D. Elkins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ package config import ( @@ -18,15 +42,17 @@ import ( ) const ( - CONFIG_FILE_DEFAULT = "/etc/ccl.toml" + ConfigFileDefault = "/etc/ccl.toml" // ConfigFileDefault is default configuration file path ) var ( - ConfigFile = CONFIG_FILE_DEFAULT - Networks = []network.Network{} - Containers = []container.Container{} + ConfigFile = ConfigFileDefault // ConfigFile is the path to the configuration file to use in a particular invocation. + Networks = []*network.Network{} // Networks is the program-global set of congiured network.Networks structures + Containers = []*container.Container{} // Containers is the program-global set of configured container.Container structures. ) +// Categories returns a slice of all of the distinct categories found in the +// configured containers. func Categories() []string { categories := []string{"all"} gs := hashset.New() @@ -40,7 +66,12 @@ func Categories() []string { return categories } -func Union(ids []string, catMask []string) (conts []container.Container) { +// Union - given a list of identifiers ("all", a category, or a container +// name), as well as a category mask, will yieLd a list of configured +// containers that match the identifiers but not the mask. As envisioned, the +// default mask would be ".", i.e., the tool would normally exclude containers +// "disabled" by setting their category to a singLe period (".") +func Union(ids []string, catMask []string) (conts []*container.Container) { if len(ids) == 0 { ids = []string{"all"} } @@ -60,7 +91,7 @@ func Union(ids []string, catMask []string) (conts []container.Container) { } for _, c := range h.Values() { name := c.(string) - 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]) } if len(conts) == 0 { @@ -69,7 +100,7 @@ func Union(ids []string, catMask []string) (conts []container.Container) { "catMask": catMask, }).Warnln("No matching containers. If disabled, try adding -a") } - slices.SortFunc(conts, func(a, b container.Container) bool { + slices.SortFunc(conts, func(a, b *container.Container) bool { if a.Category < b.Category { return true } @@ -81,8 +112,9 @@ func Union(ids []string, catMask []string) (conts []container.Container) { return } -func UnionNetworks(ids []string) []network.Network { - nets := make([]network.Network, len(Networks)) +// UnionNetworks is like Union but for Networks, and also no mask. +func UnionNetworks(ids []string) []*network.Network { + nets := make([]*network.Network, len(Networks)) rejects := []int{} copy(nets, Networks) for i := range nets { @@ -101,11 +133,14 @@ func UnionNetworks(ids []string) []network.Network { return nets } +// Init will parse the configuration file, create and populate the in-memory +// data structures. Will call container.Init() on each container created in +// this way. func Init(conn context.Context) error { // A parsing convenience type parse struct { - Networks []network.Network - Containers []container.Container + Networks []*network.Network + Containers []*container.Container } f, err := os.ReadFile(ConfigFile) @@ -121,22 +156,29 @@ func Init(conn context.Context) error { for i := range Containers { Containers[i].Init(conn, Networks) } - slices.SortFunc(Containers, func(a, b container.Container) bool { + slices.SortFunc(Containers, func(a, b *container.Container) bool { return a.Name < b.Name }) return nil } +// Show will output a toml configuration of the provided identifiers ("all", +// category, or container name). The resulting string would presumably be sent +// to the terminal or a file. +// +// Other than ordering of fields and possible inclusion of some fields with +// their default values, the generated toml should be completely compatible +// with the actual configuration. Dogfood safe. func Show(ids []string, contMask []string) string { type parse struct { - Containers []container.Container `toml:"containers,omitempty"` - Networks []network.Network `toml:"networks,omitempty"` + Containers []*container.Container `toml:"containers,omitempty"` + Networks []*network.Network `toml:"networks,omitempty"` } getNet := func(name string) *network.Network { for i := range Networks { if Networks[i].Name == name { - return &Networks[i] + return Networks[i] } } return nil diff --git a/internal/pkg/container/container.go b/internal/pkg/container/container.go index 0cc13a9..2bc020c 100644 --- a/internal/pkg/container/container.go +++ b/internal/pkg/container/container.go @@ -1,4 +1,7 @@ /* +Package container encapuslates both the metadata structure and main +operations to be presented to the user in the `cmd` module. + Copyright © 2022 Joel D. Elkins Permission is hereby granted, free of charge, to any person obtaining a copy @@ -19,7 +22,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - package container import ( @@ -41,6 +43,9 @@ import ( "gopkg.in/guregu/null.v4" ) +// Container houses the metadata that may be specified by this utility when +// creating a container. A couple of fields (Name and Image) are mandatory to +// specify, but the rest will use the libpod or otherwise sensible defaults. type Container struct { Category string `toml:"category"` Name string `toml:"name"` @@ -54,9 +59,9 @@ type Container struct { Restart string `toml:"restart,omitempty"` Umask null.Int `toml:"umask,omitempty"` User string `toml:"user,omitempty"` - ExposeTcp []uint16 `toml:"expose_tcp,omitempty"` - ExposeUdp []uint16 `toml:"expose_udp,omitempty"` - PortsTcp map[uint16]uint16 `toml:"ports,omitempty"` + ExposeTCP []uint16 `toml:"expose_tcp,omitempty"` + ExposeUDP []uint16 `toml:"expose_udp,omitempty"` + PortsTCP map[uint16]uint16 `toml:"ports,omitempty"` NetNS string `toml:"netns,omitempty"` StartGroup int `toml:"group,omitempty"` @@ -66,13 +71,15 @@ type Container struct { wasRunning bool } -func (c *Container) Init(conn context.Context, nets []network.Network) error { +// Init will initialize a new container structure by filling in network details +// and by querying other metadata from libpod, if possible. +func (c *Container) Init(conn context.Context, nets []*network.Network) error { // initialize user-provided definitions for i := range c.Networks { var n *network.Network for j := range nets { if nets[j].Name == c.Networks[i].Name { - n = &nets[j] + n = nets[j] } } if n == nil { @@ -115,9 +122,12 @@ func (c *Container) Init(conn context.Context, nets []network.Network) error { return err } +// LogEntry will return a *logrus.LogEntry, with some basic fields populated +// for this container. The idea is that the calling code would add other fields +// (optionally) and do something with the error. func (c *Container) LogEntry() *log.Entry { f := log.Fields{ - "container": c.Name, + "container": c.Name, "wasRunning": c.wasRunning, } if c.cdata != nil && c.cdata.ID != "" { @@ -134,14 +144,16 @@ func (c *Container) pull() error { return err } -func (c *Container) newCommandSet(op string, cmds cmd.Commands) cmd.CommandSet { - return cmd.CommandSet{ +func (c *Container) newCommandSet(op string, cmds cmd.Commands) cmd.Set { + return cmd.Set{ ID: fmt.Sprintf("%s-%s", op, c.Name), Commands: cmds, } } -func (c *Container) PullCommands() cmd.CommandSet { +// PullCommands will return a cmd.Set to pull the specified image using the +// libpod API +func (c *Container) PullCommands() cmd.Set { return c.newCommandSet("PULL", cmd.Commands{ cmd.NewFunc("do_pull", func() error { return c.pull() @@ -149,7 +161,9 @@ func (c *Container) PullCommands() cmd.CommandSet { }) } -func (c *Container) CreateCommands() cmd.CommandSet { +// CreateCommands returns a cmd.Set that will create a container from the +// configured metadata. The container should not exist. +func (c *Container) CreateCommands() cmd.Set { if c.Image == "" { return c.newCommandSet("CREATE", cmd.Commands{ cmd.NewFunc("image_error", func() error { @@ -179,15 +193,15 @@ func (c *Container) CreateCommands() cmd.CommandSet { } expose := map[uint16]string{} - for _, p := range c.ExposeTcp { + for _, p := range c.ExposeTCP { expose[p] = "tcp" } - for _, p := range c.ExposeUdp { + for _, p := range c.ExposeUDP { expose[p] = "udp" } portMappings := []types.PortMapping{} - for ph, pc := range c.PortsTcp { + for ph, pc := range c.PortsTCP { portMappings = append(portMappings, types.PortMapping{ HostPort: ph, ContainerPort: pc, @@ -256,15 +270,17 @@ func (c *Container) CreateCommands() cmd.CommandSet { }) } -func (c *Container) RecreateCommands() cmd.CommandSet { +// RecreateCommands will stop (if running), remove (if exists), (re)create, and restart (if +// it was initially running) a container. The image is not pulled. +func (c *Container) RecreateCommands() cmd.Set { return c.newCommandSet("RECREATE", cmd.Commands{ cmd.NewSet(c.RemoveCommands()), cmd.NewSet(c.CreateCommands()), }) } -// unexported version just removes the container without attempting a stop. -func (c *Container) RemoveCommands() cmd.CommandSet { +// RemoveCommands removes a container (as if by `podman rm -f`) +func (c *Container) RemoveCommands() cmd.Set { return c.newCommandSet("remove", cmd.Commands{ cmd.NewFunc("remove_if_exists", func() error { c.cdataLock.Lock() @@ -281,7 +297,8 @@ func (c *Container) RemoveCommands() cmd.CommandSet { }) } -func (c *Container) StartCommands() cmd.CommandSet { +// StartCommands will start a container if it's not already running. +func (c *Container) StartCommands() cmd.Set { return c.newCommandSet("START", cmd.Commands{ cmd.NewFunc("start_container", func() error { c.cdataLock.Lock() @@ -318,6 +335,8 @@ func (c *Container) StartCommands() cmd.CommandSet { }) } +// IsRunning returns true if libpod reports the container status is running, or +// false otherwise. If an error happens, the default value is false. func (c *Container) IsRunning() bool { if c.cdata != nil && c.cdata.State != nil { return c.cdata.State.Running @@ -325,6 +344,7 @@ func (c *Container) IsRunning() bool { return false } +// IsCreated tests whether libpod sees the container as being created (running or not) func (c *Container) IsCreated() bool { if c.cdata == nil || c.cdata.ID == "" { return false @@ -332,7 +352,9 @@ func (c *Container) IsCreated() bool { return true } -func (c *Container) UpdateCommands() cmd.CommandSet { +// UpdateCommands will pull the image (to force updates) and then recreate the +// container. It will be stopped first. +func (c *Container) UpdateCommands() cmd.Set { return c.newCommandSet("UPDATE", cmd.Commands{ cmd.NewSet(c.PullCommands()), cmd.NewSet(c.StopCommands()), @@ -341,7 +363,10 @@ func (c *Container) UpdateCommands() cmd.CommandSet { }) } -func (c *Container) ConditionalStartCommands() cmd.CommandSet { +// ConditionalStartCommands - several of the other command sets would leave the +// container in the stopped state. This set will restart a container if it was +// running when this container was first initialized. +func (c *Container) ConditionalStartCommands() cmd.Set { if c.wasRunning { return c.StartCommands() } @@ -350,7 +375,9 @@ func (c *Container) ConditionalStartCommands() cmd.CommandSet { }) } -func (c *Container) StopCommands() cmd.CommandSet { +// StopCommands will stop a container if it is running, defining a 10 second +// timeout before the processes are killed by lippod +func (c *Container) StopCommands() cmd.Set { return c.newCommandSet("STOP", cmd.Commands{ cmd.NewFunc("stop_if_running", func() error { c.cdataLock.Lock() @@ -385,6 +412,8 @@ func (c *Container) populateCData() error { return err } +// Pid will return the host process id of the main container process (pid +// 1 inside the container) func (c *Container) Pid() int { if c.cdata != nil && c.cdata.State != nil { return c.cdata.State.Pid @@ -394,7 +423,7 @@ func (c *Container) Pid() int { func (c *Container) assureNetNS() error { if nil == c.cdata || nil == c.cdata.NetworkSettings { - return fmt.Errorf("Network namespace not available!") + return fmt.Errorf("network namespace not available") } netns := c.cdata.NetworkSettings.SandboxKey if err := exec.Command("rm", "-f", "/var/run/netns/"+c.Name).Run(); err != nil {