mirror of
https://gitea.elkins.co/Networking/ccl.git
synced 2025-03-09 12:41:40 -05:00
Migrate to podman API rather than cli
Large change greatly expanding the linked footprint of this little utility, but it is much faster and adds a couple of very nice features: - set container sysctl settings to disable router advertisements as part of container definition. this means we no longer need to do this via `ip netns exec <container> sysctl -w ...` (followed by `ip netns exec ip -6 a flush...`). Major win. - able to very quickly ascertain creation and run state of defined containers.
This commit is contained in:
parent
c2ae18fed9
commit
b758f04a50
@ -31,8 +31,6 @@ func validNouns(*cobra.Command, []string, string) ([]string, cobra.ShellCompDire
|
|||||||
for _, c := range config.Union([]string{}) {
|
for _, c := range config.Union([]string{}) {
|
||||||
validArgs = append(validArgs, c.Name)
|
validArgs = append(validArgs, c.Name)
|
||||||
}
|
}
|
||||||
for _, c := range config.Categories() {
|
validArgs = append(validArgs, config.Categories()...)
|
||||||
validArgs = append(validArgs, c)
|
|
||||||
}
|
|
||||||
return validArgs, cobra.ShellCompDirectiveNoFileComp
|
return validArgs, cobra.ShellCompDirectiveNoFileComp
|
||||||
}
|
}
|
||||||
|
12
cmd/root.go
12
cmd/root.go
@ -22,11 +22,13 @@ THE SOFTWARE.
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitea.elkins.co/Networking/ccl/internal/pkg/config"
|
"gitea.elkins.co/Networking/ccl/internal/pkg/config"
|
||||||
|
"github.com/containers/podman/v4/pkg/bindings"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,6 +47,7 @@ var (
|
|||||||
output io.Writer
|
output io.Writer
|
||||||
verbose bool
|
verbose bool
|
||||||
fake bool
|
fake bool
|
||||||
|
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.
|
||||||
@ -58,7 +61,13 @@ func Execute() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cobra.OnInitialize(func() {
|
cobra.OnInitialize(func() {
|
||||||
err := config.Init()
|
// connect to podman
|
||||||
|
conn, err := bindings.NewConnection(context.Background(), socket)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Could not connect:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = config.Init(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Warning: Could not initialize configuration:", err)
|
fmt.Fprintln(os.Stderr, "Warning: Could not initialize configuration:", err)
|
||||||
}
|
}
|
||||||
@ -73,4 +82,5 @@ func init() {
|
|||||||
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")
|
||||||
}
|
}
|
||||||
|
99
go.mod
99
go.mod
@ -3,13 +3,112 @@ module gitea.elkins.co/Networking/ccl
|
|||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/containers/podman/v4 v4.1.1
|
||||||
github.com/emirpasic/gods v1.18.1
|
github.com/emirpasic/gods v1.18.1
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75
|
golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.1.0 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||||
|
github.com/Microsoft/hcsshim v0.9.2 // indirect
|
||||||
|
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||||
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
|
github.com/containerd/cgroups v1.0.3 // indirect
|
||||||
|
github.com/containerd/containerd v1.6.4 // indirect
|
||||||
|
github.com/containerd/stargz-snapshotter/estargz v0.11.4 // indirect
|
||||||
|
github.com/containers/buildah v1.26.1 // indirect
|
||||||
|
github.com/containers/common v0.48.0
|
||||||
|
github.com/containers/image/v5 v5.21.1 // indirect
|
||||||
|
github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a // indirect
|
||||||
|
github.com/containers/ocicrypt v1.1.4 // indirect
|
||||||
|
github.com/containers/psgo v1.7.2 // indirect
|
||||||
|
github.com/containers/storage v1.40.2 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||||
|
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||||
|
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||||
|
github.com/docker/docker v20.10.14+incompatible // indirect
|
||||||
|
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||||
|
github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 // indirect
|
||||||
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/google/go-intervals v0.0.2 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/gorilla/schema v1.2.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/copier v0.3.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.15.2 // indirect
|
||||||
|
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||||
|
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||||
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
|
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
|
||||||
|
github.com/moby/sys/mountinfo v0.6.1 // indirect
|
||||||
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
|
||||||
|
github.com/opencontainers/runc v1.1.1 // indirect
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab
|
||||||
|
github.com/opencontainers/runtime-tools v0.9.1-0.20220110225228-7e2d60f1e41f // indirect
|
||||||
|
github.com/opencontainers/selinux v1.10.1 // indirect
|
||||||
|
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/proglottis/gpgme v0.1.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.11.1 // indirect
|
||||||
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
|
github.com/prometheus/common v0.30.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
|
||||||
|
github.com/sylabs/sif/v2 v2.7.0 // indirect
|
||||||
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||||
|
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
|
||||||
|
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||||
|
github.com/vbatts/tar-split v0.11.2 // indirect
|
||||||
|
github.com/vbauerster/mpb/v7 v7.4.1 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
go.etcd.io/bbolt v1.3.6 // indirect
|
||||||
|
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect
|
||||||
|
go.opencensus.io v0.23.0 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8 // indirect
|
||||||
|
google.golang.org/grpc v1.44.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
|
gopkg.in/guregu/null.v4 v4.0.0
|
||||||
|
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
@ -25,6 +25,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandType int
|
type CommandType int
|
||||||
@ -32,7 +34,8 @@ type CommandType int
|
|||||||
const (
|
const (
|
||||||
CT_NOP CommandType = iota
|
CT_NOP CommandType = iota
|
||||||
CT_SH
|
CT_SH
|
||||||
CT_REF
|
CT_STRFUNC
|
||||||
|
CT_FUNC
|
||||||
CT_INDIRECT
|
CT_INDIRECT
|
||||||
CT_SET
|
CT_SET
|
||||||
CT_DEBUG
|
CT_DEBUG
|
||||||
@ -45,7 +48,9 @@ func (ct CommandType) String() string {
|
|||||||
return "NOP"
|
return "NOP"
|
||||||
case CT_SH:
|
case CT_SH:
|
||||||
return "SHELL"
|
return "SHELL"
|
||||||
case CT_REF:
|
case CT_STRFUNC:
|
||||||
|
return "STRFUNC"
|
||||||
|
case CT_FUNC:
|
||||||
return "FUNC"
|
return "FUNC"
|
||||||
case CT_INDIRECT:
|
case CT_INDIRECT:
|
||||||
return "INDIRECT"
|
return "INDIRECT"
|
||||||
@ -70,6 +75,11 @@ type namedFunc struct {
|
|||||||
Func func() string
|
Func func() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errFunc struct {
|
||||||
|
Name string
|
||||||
|
Func func() error
|
||||||
|
}
|
||||||
|
|
||||||
type conditional struct {
|
type conditional struct {
|
||||||
Name string
|
Name string
|
||||||
Condition func() bool
|
Condition func() bool
|
||||||
@ -81,12 +91,20 @@ func (f namedFunc) String() string {
|
|||||||
return f.Name + "()"
|
return f.Name + "()"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f errFunc) String() string {
|
||||||
|
return f.Name + "()"
|
||||||
|
}
|
||||||
|
|
||||||
func NewShell(cmd string) Command {
|
func NewShell(cmd string) Command {
|
||||||
return Command{CT_SH, cmd}
|
return Command{CT_SH, cmd}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFunc(name string, f func() string) Command {
|
func NewFunc(name string, f func() string) Command {
|
||||||
return Command{CT_REF, namedFunc{name, f}}
|
return Command{CT_STRFUNC, namedFunc{name, f}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrFunc(name string, f func() error) Command {
|
||||||
|
return Command{CT_FUNC, errFunc{name, f}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIndirect(c Command) Command {
|
func NewIndirect(c Command) Command {
|
||||||
@ -122,13 +140,13 @@ func (c Command) GetShell() (string, error) {
|
|||||||
return s, fmt.Errorf("Type error. Requested = %s, Type = %s", CT_SH, c.Type)
|
return s, fmt.Errorf("Type error. Requested = %s, Type = %s", CT_SH, c.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Command) CallRef() (string, error) {
|
func (c Command) CallStrFunc() (string, error) {
|
||||||
f, ok := c.Command.(namedFunc)
|
f, ok := c.Command.(namedFunc)
|
||||||
if ok {
|
if ok {
|
||||||
s := f.Func()
|
s := f.Func()
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("Type error. Requested = %s, Type = %s", CT_REF, c.Type)
|
return "", fmt.Errorf("Type error. Requested = %s, Type = %s", CT_STRFUNC, c.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Command) Execute(output io.Writer, fake bool) error {
|
func (c Command) Execute(output io.Writer, fake bool) error {
|
||||||
@ -146,13 +164,27 @@ func (c Command) Execute(output io.Writer, fake bool) error {
|
|||||||
fmt.Fprint(output, string(out))
|
fmt.Fprint(output, string(out))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case CT_REF:
|
case CT_STRFUNC:
|
||||||
fmt.Fprintln(output, c.Type, c.Command)
|
fmt.Fprintln(output, c.Type, c.Command)
|
||||||
if !fake {
|
if !fake {
|
||||||
s, err := c.CallRef()
|
s, err := c.CallStrFunc()
|
||||||
fmt.Fprintln(output, s)
|
fmt.Fprintln(output, s)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case CT_FUNC:
|
||||||
|
ef := c.Command.(errFunc)
|
||||||
|
msg := log.WithFields(log.Fields{
|
||||||
|
"type": CT_FUNC.String(),
|
||||||
|
"func": ef.Name,
|
||||||
|
})
|
||||||
|
msg.Debugf("Calling")
|
||||||
|
fmt.Fprintln(output, c.Type, ef.Name)
|
||||||
|
if !fake {
|
||||||
|
if err := ef.Func(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
case CT_INDIRECT:
|
case CT_INDIRECT:
|
||||||
ct, ok := c.Command.(Command)
|
ct, ok := c.Command.(Command)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitea.elkins.co/Networking/ccl/internal/pkg/container"
|
"gitea.elkins.co/Networking/ccl/internal/pkg/container"
|
||||||
@ -65,7 +65,7 @@ type parse struct {
|
|||||||
Containers []container.Container
|
Containers []container.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init() error {
|
func Init(conn context.Context) error {
|
||||||
f, err := os.ReadFile(ConfigFile)
|
f, err := os.ReadFile(ConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -77,28 +77,7 @@ func Init() error {
|
|||||||
}
|
}
|
||||||
containers, networks = &p.Containers, &p.Networks
|
containers, networks = &p.Containers, &p.Networks
|
||||||
for i := range p.Containers {
|
for i := range p.Containers {
|
||||||
p.Containers[i].Init(networks)
|
p.Containers[i].Init(conn, networks)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NetworkDefaults(name string) (net *network.Network) {
|
|
||||||
for _, n := range *networks {
|
|
||||||
if n.Name == name {
|
|
||||||
net = &n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrintCreate(ct *container.Container) {
|
|
||||||
for _, c := range ct.CreateCommands() {
|
|
||||||
fmt.Println(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrintStart(ct *container.Container) {
|
|
||||||
for _, c := range ct.StartCommands() {
|
|
||||||
fmt.Println(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -22,153 +22,42 @@ THE SOFTWARE.
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.elkins.co/Networking/ccl/internal/pkg/command"
|
"gitea.elkins.co/Networking/ccl/internal/pkg/command"
|
||||||
"gitea.elkins.co/Networking/ccl/internal/pkg/network"
|
"gitea.elkins.co/Networking/ccl/internal/pkg/network"
|
||||||
|
"github.com/containers/common/libnetwork/types"
|
||||||
|
"github.com/containers/podman/v4/libpod/define"
|
||||||
|
"github.com/containers/podman/v4/pkg/bindings/containers"
|
||||||
|
"github.com/containers/podman/v4/pkg/bindings/images"
|
||||||
|
"github.com/containers/podman/v4/pkg/specgen"
|
||||||
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
Category string
|
Category string
|
||||||
Name string
|
Name string
|
||||||
Image string
|
Image string
|
||||||
Hostname string
|
Hostname string
|
||||||
Command string
|
Command []string
|
||||||
Arguments string
|
Arguments string
|
||||||
Networks []network.Network
|
Networks []network.Network
|
||||||
createCommands []command.Command
|
Env map[string]string
|
||||||
upCommands []command.Command
|
Mounts []spec.Mount
|
||||||
pid int
|
Restart string
|
||||||
|
Umask uint
|
||||||
|
User string
|
||||||
|
|
||||||
|
conn context.Context
|
||||||
|
cdata *define.InspectContainerData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) flushIPv6Commands() []command.Command {
|
func (c *Container) Init(conn context.Context, nets *[]network.Network) error {
|
||||||
cmds := []command.Command{}
|
// initialize user-provided definitions
|
||||||
flush6Routes := false
|
|
||||||
if len(c.Networks) > 0 && !*c.Networks[0].IPv6 {
|
|
||||||
cmds = append(cmds, command.NewShell(fmt.Sprintf("ip netns exec %s sysctl -w net.ipv6.conf.default.accept_ra=0", c.Name)))
|
|
||||||
cmds = append(cmds, command.NewShell(fmt.Sprintf("ip netns exec %s sysctl -w net.ipv6.conf.all.accept_ra=0", c.Name)))
|
|
||||||
flush6Routes = true
|
|
||||||
}
|
|
||||||
for i, n := range c.Networks {
|
|
||||||
// Note, n.IPv6 should never be nil, since we forcibly create the
|
|
||||||
// value in Init() if not specified in the config file
|
|
||||||
if !*n.IPv6 {
|
|
||||||
cmds = append(cmds, command.NewShell(fmt.Sprintf("ip netns exec %s sysctl -w net.ipv6.conf.eth%d.accept_ra=0", c.Name, i)))
|
|
||||||
cmds = append(cmds, command.NewShell(fmt.Sprintf("ip netns exec %s ip -6 address flush dev eth%d scope global", c.Name, i)))
|
|
||||||
flush6Routes = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flush6Routes {
|
|
||||||
cmds = append(cmds, command.NewShell(fmt.Sprintf("ip netns exec %s ip -6 route flush proto ra", c.Name)))
|
|
||||||
}
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) CreateCommands() []command.Command {
|
|
||||||
if len(c.createCommands) == 0 {
|
|
||||||
c.initCommands()
|
|
||||||
}
|
|
||||||
if c.Arguments == "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "Warning: Container %s does not have arguments defined! That's unusual", c.Name)
|
|
||||||
}
|
|
||||||
if c.Image == "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: Container %s does not have an image defined!", c.Name)
|
|
||||||
}
|
|
||||||
return c.createCommands
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) RecreateCommands() []command.Command {
|
|
||||||
wasRunning := false
|
|
||||||
return []command.Command{
|
|
||||||
command.NewFunc("stash_run_state", func() string {
|
|
||||||
wasRunning = c.IsRunning()
|
|
||||||
runMsg := "not running. Will not start it after recreating."
|
|
||||||
if wasRunning {
|
|
||||||
runMsg = "running. Will restart after recreating."
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Container %s is %s.", c.Name, runMsg)
|
|
||||||
}),
|
|
||||||
command.NewSet(c.DestroyCommands()),
|
|
||||||
command.NewSet(c.CreateCommands()),
|
|
||||||
command.NewConditional("start_if_was_running",
|
|
||||||
func() bool { return wasRunning },
|
|
||||||
command.NewSet(c.StartCommands()),
|
|
||||||
command.NewNop(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) DestroyCommands() []command.Command {
|
|
||||||
cmds := c.StopCommands()
|
|
||||||
cmds = append(cmds, command.NewShell("podman rm "+c.Name))
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) StartCommands() []command.Command {
|
|
||||||
if len(c.upCommands) == 0 {
|
|
||||||
c.initCommands()
|
|
||||||
}
|
|
||||||
return []command.Command{
|
|
||||||
command.NewConditional("start_unless_running",
|
|
||||||
c.IsRunning,
|
|
||||||
command.NewNop(),
|
|
||||||
command.NewSet(c.upCommands),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) RestartCommands() []command.Command {
|
|
||||||
return []command.Command{
|
|
||||||
command.NewSet(c.StopCommands()),
|
|
||||||
command.NewShell("sleep 1"),
|
|
||||||
command.NewSet(c.StartCommands()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) IsRunning() bool {
|
|
||||||
pid, err := c.Pid()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return pid > 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) UpdateCommands() []command.Command {
|
|
||||||
wasRunning := false
|
|
||||||
return []command.Command{
|
|
||||||
command.NewFunc("stash_run_state", func() string {
|
|
||||||
wasRunning = c.IsRunning()
|
|
||||||
runMsg := "not running"
|
|
||||||
if wasRunning {
|
|
||||||
runMsg = "running"
|
|
||||||
}
|
|
||||||
return "Container " + c.Name + " is " + runMsg
|
|
||||||
}),
|
|
||||||
command.NewShell("podman pull " + c.Image),
|
|
||||||
command.NewSet(c.RecreateCommands()),
|
|
||||||
command.NewConditional("restart_if_was_running",
|
|
||||||
func() bool { return wasRunning },
|
|
||||||
command.NewSet(c.StartCommands()),
|
|
||||||
command.NewNop(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) StopCommands() []command.Command {
|
|
||||||
return []command.Command{
|
|
||||||
command.NewConditional("stop_if_running",
|
|
||||||
c.IsRunning,
|
|
||||||
command.NewShell("podman stop "+c.Name),
|
|
||||||
command.NewNop(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) Init(nets *[]network.Network) {
|
|
||||||
for i := range c.Networks {
|
for i := range c.Networks {
|
||||||
var n *network.Network
|
var n *network.Network
|
||||||
for j := range *nets {
|
for j := range *nets {
|
||||||
@ -182,85 +71,260 @@ func (c *Container) Init(nets *[]network.Network) {
|
|||||||
if len(c.Networks[i].DNS) == 0 {
|
if len(c.Networks[i].DNS) == 0 {
|
||||||
c.Networks[i].DNS = n.DNS
|
c.Networks[i].DNS = n.DNS
|
||||||
}
|
}
|
||||||
if c.Networks[i].IPv6 == nil {
|
if !c.Networks[i].IPv6.Valid {
|
||||||
if n.IPv6 != nil {
|
if n.IPv6.Valid {
|
||||||
c.Networks[i].IPv6 = n.IPv6
|
c.Networks[i].IPv6 = n.IPv6
|
||||||
} else {
|
} else {
|
||||||
yes := true
|
c.Networks[i].IPv6.SetValid(true)
|
||||||
c.Networks[i].IPv6 = &yes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i := range c.Mounts {
|
||||||
|
if c.Mounts[i].Type == "" {
|
||||||
|
c.Mounts[i].Type = "bind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.conn = conn
|
||||||
|
if c.Umask == 0 {
|
||||||
|
c.Umask = 0o022
|
||||||
|
}
|
||||||
|
return c.populateCData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Pid() (pid int, err error) {
|
func (c *Container) CreateCommands() []command.Command {
|
||||||
pid_s, err := exec.Command("podman", "inspect", "-f", "{{.State.Pid}}", c.Name).CombinedOutput()
|
if c.Image == "" {
|
||||||
if err != nil {
|
log.WithField("container", c.Name).Error("Image not defined")
|
||||||
return
|
return []command.Command{command.NewNop()}
|
||||||
}
|
}
|
||||||
c.pid, err = strconv.Atoi(strings.TrimSuffix(string(pid_s), "\n"))
|
sysctl := map[string]string{}
|
||||||
return c.pid, err
|
nets := map[string]types.PerNetworkOptions{}
|
||||||
}
|
dns := []net.IP{}
|
||||||
|
for i := range c.Networks {
|
||||||
func (c *Container) initCommands() {
|
if !c.Networks[i].IPv6.Bool {
|
||||||
c.createCommands = []command.Command{
|
sysctl["net.ipv6.conf."+c.Networks[i].Name+".accept_ra"] = "0"
|
||||||
command.NewShell("podman create --name %s%s%s%s%s %s%s"),
|
|
||||||
}
|
|
||||||
hostname := ""
|
|
||||||
if c.Hostname != "" {
|
|
||||||
hostname = fmt.Sprintf(" --hostname %s", c.Hostname)
|
|
||||||
}
|
|
||||||
net := ""
|
|
||||||
dns := ""
|
|
||||||
if len(c.Networks) > 0 {
|
|
||||||
net = " --net " + c.Networks[0].ToArgs()
|
|
||||||
if len(c.Networks[0].DNS) > 0 {
|
|
||||||
dns = " --dns " + strings.Join(c.Networks[0].DNS, ",")
|
|
||||||
}
|
}
|
||||||
}
|
ips := []net.IP{}
|
||||||
args := ""
|
if c.Networks[i].IPv4Address != nil {
|
||||||
if c.Arguments != "" {
|
ips = append(ips, c.Networks[i].IPv4Address)
|
||||||
args = " " + c.Arguments
|
|
||||||
}
|
|
||||||
entry := ""
|
|
||||||
if c.Command != "" {
|
|
||||||
entry = " " + c.Command
|
|
||||||
}
|
|
||||||
t, _ := c.createCommands[0].GetShell()
|
|
||||||
c.createCommands[0] = command.NewShell(fmt.Sprintf(t, c.Name, hostname, net, dns, args, c.Image, entry))
|
|
||||||
|
|
||||||
if len(c.Networks) > 1 {
|
|
||||||
for i := 1; i < len(c.Networks); i++ {
|
|
||||||
n := c.Networks[i]
|
|
||||||
s := fmt.Sprintf("podman network connect %s %s", n.ToArgs(), c.Name)
|
|
||||||
c.createCommands = append(c.createCommands, command.NewShell(s))
|
|
||||||
}
|
}
|
||||||
|
if c.Networks[i].IPv6Address != nil {
|
||||||
|
ips = append(ips, c.Networks[i].IPv6Address)
|
||||||
|
}
|
||||||
|
nets[c.Networks[i].Name] = types.PerNetworkOptions{
|
||||||
|
StaticIPs: ips,
|
||||||
|
InterfaceName: c.Networks[i].Name,
|
||||||
|
}
|
||||||
|
dns = append(dns, c.Networks[i].DNS...)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.upCommands = []command.Command{
|
spec := specgen.SpecGenerator{
|
||||||
command.NewShell("podman start " + c.Name),
|
ContainerBasicConfig: specgen.ContainerBasicConfig{
|
||||||
command.NewFunc("assure_netns", func() string {
|
Name: c.Name,
|
||||||
netns_b, err := exec.Command("podman", "inspect", "-f", "{{.NetworkSettings.SandboxKey}}", c.Name).CombinedOutput()
|
UtsNS: specgen.Namespace{NSMode: specgen.Private},
|
||||||
|
Hostname: c.Hostname,
|
||||||
|
RawImageName: c.Image,
|
||||||
|
RestartPolicy: c.Restart,
|
||||||
|
Sysctl: sysctl,
|
||||||
|
Env: c.Env,
|
||||||
|
Command: c.Command,
|
||||||
|
},
|
||||||
|
ContainerStorageConfig: specgen.ContainerStorageConfig{
|
||||||
|
Image: c.Image,
|
||||||
|
Mounts: c.Mounts,
|
||||||
|
},
|
||||||
|
ContainerNetworkConfig: specgen.ContainerNetworkConfig{
|
||||||
|
Networks: nets,
|
||||||
|
DNSServers: dns,
|
||||||
|
},
|
||||||
|
ContainerSecurityConfig: specgen.ContainerSecurityConfig{
|
||||||
|
User: c.User,
|
||||||
|
Umask: fmt.Sprintf("%#o", c.Umask),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := spec.Validate(); err != nil {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"container": c.Name,
|
||||||
|
"error": err,
|
||||||
|
}).Warn("Specgen does not validate")
|
||||||
|
}
|
||||||
|
return []command.Command{
|
||||||
|
command.NewErrFunc("do_create", func() error {
|
||||||
|
_, err := containers.CreateWithSpec(c.conn, &spec, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("%s is not running", c.Name)
|
return err
|
||||||
}
|
}
|
||||||
netns := strings.TrimRight(string(netns_b), "\n")
|
return c.populateCData()
|
||||||
err = exec.Command("rm", "-f", "/var/run/netns/"+c.Name).Run()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("Error: %s", err)
|
|
||||||
}
|
|
||||||
err = exec.Command("ln", "-sf", netns, "/var/run/netns/"+c.Name).Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "Warning: could not establish network namespace", err)
|
|
||||||
return fmt.Sprintf("Error: %s", err)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Created /var/run/netns/%s", c.Name)
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
if len(c.Networks) > 0 && !*c.Networks[0].IPv6 {
|
}
|
||||||
c.upCommands = append(c.upCommands, command.NewShell("sleep 1"))
|
|
||||||
for _, k := range c.flushIPv6Commands() {
|
func (c *Container) RecreateCommands() []command.Command {
|
||||||
c.upCommands = append(c.upCommands, k)
|
wasRunning := false
|
||||||
}
|
return []command.Command{
|
||||||
|
command.NewFunc("stash_run_state", func() string {
|
||||||
|
wasRunning = c.IsRunning()
|
||||||
|
runMsg := "not running. Will not start it after recreating."
|
||||||
|
if wasRunning {
|
||||||
|
runMsg = "running. Will restart after recreating."
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Container %s is %s", c.Name, runMsg)
|
||||||
|
}),
|
||||||
|
command.NewSet(c.DestroyCommands()),
|
||||||
|
command.NewSet(c.CreateCommands()),
|
||||||
|
command.NewConditional("start_if_was_running",
|
||||||
|
func() bool { return wasRunning },
|
||||||
|
command.NewSet(c.StartCommands()),
|
||||||
|
command.NewNop(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) DestroyCommands() []command.Command {
|
||||||
|
cmds := c.StopCommands()
|
||||||
|
cmds = append(cmds, command.NewErrFunc("remove_if_exists", func() error {
|
||||||
|
if c.cdata.ID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
yes := true
|
||||||
|
_, err := containers.Remove(c.conn, c.cdata.ID, &containers.RemoveOptions{Force: &yes})
|
||||||
|
return err
|
||||||
|
}))
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) StartCommands() []command.Command {
|
||||||
|
return []command.Command{
|
||||||
|
command.NewErrFunc("start_container", func() error {
|
||||||
|
if c.cdata.State != nil && c.cdata.State.Running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := containers.Start(c.conn, c.cdata.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = containers.Wait(c.conn, c.cdata.ID, &containers.WaitOptions{Condition: []define.ContainerStatus{define.ContainerStateRunning}})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.populateCData()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.assureNetNS()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) RestartCommands() []command.Command {
|
||||||
|
return []command.Command{
|
||||||
|
command.NewSet(c.StopCommands()),
|
||||||
|
command.NewSet(c.StartCommands()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) IsRunning() bool {
|
||||||
|
if c.cdata != nil && c.cdata.State != nil {
|
||||||
|
return c.cdata.State.Running
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) IsCreated() bool {
|
||||||
|
if c.cdata == nil || c.cdata.ID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) UpdateCommands() []command.Command {
|
||||||
|
wasRunning := false
|
||||||
|
return []command.Command{
|
||||||
|
command.NewErrFunc("do_update_and_stop", func() error {
|
||||||
|
_, err := images.Pull(c.conn, c.Image, &images.PullOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wasRunning = (c.cdata.State != nil) && c.cdata.State.Running
|
||||||
|
if wasRunning {
|
||||||
|
var timeout uint = 10
|
||||||
|
err := containers.Stop(c.conn, c.cdata.ID, &containers.StopOptions{Timeout: &timeout})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = containers.Remove(c.conn, c.cdata.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.cdata = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
command.NewSet(c.CreateCommands()),
|
||||||
|
command.NewConditional("restart_if_was_running",
|
||||||
|
func() bool { return wasRunning },
|
||||||
|
command.NewSet(c.StartCommands()),
|
||||||
|
command.NewNop(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) StopCommands() []command.Command {
|
||||||
|
return []command.Command{
|
||||||
|
command.NewErrFunc("do_stop", func() error {
|
||||||
|
if c.IsRunning() {
|
||||||
|
var timeout uint = 10
|
||||||
|
err := containers.Stop(c.conn, c.cdata.ID, &containers.StopOptions{Timeout: &timeout})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = containers.Wait(c.conn, c.cdata.ID, &containers.WaitOptions{Condition: []define.ContainerStatus{define.ContainerStateExited}})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.populateCData()
|
||||||
|
}
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"container": c.Name,
|
||||||
|
"id": c.cdata.ID,
|
||||||
|
}).Debugf("Container stopped but wasn't running. Not a problem.")
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) populateCData() error {
|
||||||
|
// TODO: locking
|
||||||
|
var err error
|
||||||
|
size := false
|
||||||
|
c.cdata, err = containers.Inspect(c.conn, c.Name, &containers.InspectOptions{Size: &size})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Pid() int {
|
||||||
|
if c.cdata != nil && c.cdata.State != nil {
|
||||||
|
return c.cdata.State.Pid
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) assureNetNS() error {
|
||||||
|
if nil == c.cdata || nil == c.cdata.NetworkSettings {
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := exec.Command("ln", "-sf", netns, "/var/run/netns/"+c.Name).Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -21,26 +21,16 @@ THE SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"gopkg.in/guregu/null.v4"
|
||||||
|
)
|
||||||
|
|
||||||
type Network struct {
|
type Network struct {
|
||||||
Name string
|
Name string
|
||||||
DNS []string
|
DNS []net.IP
|
||||||
IPv6 *bool `toml:"ipv6"`
|
IPv6 null.Bool `toml:"ipv6"`
|
||||||
IPv4Address string `toml:"ipv4_address"`
|
IPv4Address net.IP `toml:"ipv4_address"`
|
||||||
IPv6Address string `toml:"ipv6_address"`
|
IPv6Address net.IP `toml:"ipv6_address"`
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Network) ToArgs() string {
|
|
||||||
net := "%s%s%s"
|
|
||||||
ipv4 := ""
|
|
||||||
if n.IPv4Address != "" {
|
|
||||||
ipv4 = fmt.Sprintf(" --ip %s", n.IPv4Address)
|
|
||||||
}
|
|
||||||
ipv6 := ""
|
|
||||||
if n.IPv6Address != "" {
|
|
||||||
ipv6 = fmt.Sprintf(" --ip6 %s", n.IPv6Address)
|
|
||||||
}
|
|
||||||
net = fmt.Sprintf(net, n.Name, ipv4, ipv6)
|
|
||||||
return net
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user