mirror of
https://gitea.elkins.co/Networking/ccl.git
synced 2025-03-09 04:31:38 -05:00
Huge amount of linting
Linting
This commit is contained in:
parent
9c27d0e6f5
commit
b91eb62c34
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
17
cmd/root.go
17
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")
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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 <joel@elkins.co>
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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 <joel@elkins.co>
|
||||
|
||||
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
|
||||
|
@ -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 <joel@elkins.co>
|
||||
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user