313 lines
7.8 KiB
Go
313 lines
7.8 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/containers/storage/pkg/ioutils"
|
|
"github.com/containers/storage/pkg/lockfile"
|
|
)
|
|
|
|
const connectionsFile = "podman-connections.json"
|
|
|
|
// connectionsConfigFile returns the path to the rw connections config file
|
|
func connectionsConfigFile() (string, error) {
|
|
if path, found := os.LookupEnv("PODMAN_CONNECTIONS_CONF"); found {
|
|
return path, nil
|
|
}
|
|
path, err := userConfigPath()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// file is stored next to containers.conf
|
|
return filepath.Join(filepath.Dir(path), connectionsFile), nil
|
|
}
|
|
|
|
type ConnectionConfig struct {
|
|
Default string `json:",omitempty"`
|
|
Connections map[string]Destination `json:",omitempty"`
|
|
}
|
|
|
|
type ConnectionsFile struct {
|
|
Connection ConnectionConfig `json:",omitempty"`
|
|
Farm FarmConfig `json:",omitempty"`
|
|
}
|
|
|
|
type Connection struct {
|
|
// Name of the connection
|
|
Name string
|
|
|
|
// Destination for this connection
|
|
Destination
|
|
|
|
// Default if this connection is the default
|
|
Default bool
|
|
|
|
// ReadWrite if true the connection is stored in the connections file
|
|
ReadWrite bool
|
|
}
|
|
|
|
type Farm struct {
|
|
// Name of the farm
|
|
Name string
|
|
|
|
// Connections
|
|
Connections []string
|
|
|
|
// Default if this is the default farm
|
|
Default bool
|
|
|
|
// ReadWrite if true the farm is stored in the connections file
|
|
ReadWrite bool
|
|
}
|
|
|
|
func readConnectionConf(path string) (*ConnectionsFile, error) {
|
|
conf := new(ConnectionsFile)
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
// return empty config if file does not exists
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
return conf, nil
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
err = json.NewDecoder(f).Decode(conf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse %q: %w", path, err)
|
|
}
|
|
return conf, nil
|
|
}
|
|
|
|
func writeConnectionConf(path string, conf *ConnectionsFile) error {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return err
|
|
}
|
|
|
|
opts := &ioutils.AtomicFileWriterOptions{ExplicitCommit: true}
|
|
configFile, err := ioutils.NewAtomicFileWriterWithOpts(path, 0o644, opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer configFile.Close()
|
|
|
|
err = json.NewEncoder(configFile).Encode(conf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If no errors commit the changes to the config file
|
|
return configFile.Commit()
|
|
}
|
|
|
|
// EditConnectionConfig must be used to edit the connections config.
|
|
// The function will read and write the file automatically and the
|
|
// callback function just needs to modify the cfg as needed.
|
|
func EditConnectionConfig(callback func(cfg *ConnectionsFile) error) error {
|
|
path, err := connectionsConfigFile()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
lockPath := path + ".lock"
|
|
lock, err := lockfile.GetLockFile(lockPath)
|
|
if err != nil {
|
|
return fmt.Errorf("obtain lock file: %w", err)
|
|
}
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
conf, err := readConnectionConf(path)
|
|
if err != nil {
|
|
return fmt.Errorf("read connections file: %w", err)
|
|
}
|
|
if conf.Farm.List == nil {
|
|
conf.Farm.List = make(map[string][]string)
|
|
}
|
|
|
|
if err := callback(conf); err != nil {
|
|
return err
|
|
}
|
|
|
|
return writeConnectionConf(path, conf)
|
|
}
|
|
|
|
func makeConnection(name string, dst Destination, def, readWrite bool) *Connection {
|
|
return &Connection{
|
|
Name: name,
|
|
Destination: dst,
|
|
Default: def,
|
|
ReadWrite: readWrite,
|
|
}
|
|
}
|
|
|
|
// GetConnection return the connection for the given name or if def is set to true then return the default connection.
|
|
func (c *Config) GetConnection(name string, def bool) (*Connection, error) {
|
|
path, err := connectionsConfigFile()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conConf, err := readConnectionConf(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defaultCon := conConf.Connection.Default
|
|
if defaultCon == "" {
|
|
defaultCon = c.Engine.ActiveService
|
|
}
|
|
if def {
|
|
if defaultCon == "" {
|
|
return nil, errors.New("no default connection found")
|
|
}
|
|
name = defaultCon
|
|
} else {
|
|
def = defaultCon == name
|
|
}
|
|
|
|
if dst, ok := conConf.Connection.Connections[name]; ok {
|
|
return makeConnection(name, dst, def, true), nil
|
|
}
|
|
if dst, ok := c.Engine.ServiceDestinations[name]; ok {
|
|
return makeConnection(name, dst, def, false), nil
|
|
}
|
|
return nil, fmt.Errorf("connection %q not found", name)
|
|
}
|
|
|
|
// GetAllConnections return all configured connections
|
|
func (c *Config) GetAllConnections() ([]Connection, error) {
|
|
path, err := connectionsConfigFile()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conConf, err := readConnectionConf(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defaultCon := conConf.Connection.Default
|
|
if defaultCon == "" {
|
|
defaultCon = c.Engine.ActiveService
|
|
}
|
|
|
|
connections := make([]Connection, 0, len(conConf.Connection.Connections))
|
|
for name, dst := range conConf.Connection.Connections {
|
|
def := defaultCon == name
|
|
connections = append(connections, *makeConnection(name, dst, def, true))
|
|
}
|
|
for name, dst := range c.Engine.ServiceDestinations {
|
|
if _, ok := conConf.Connection.Connections[name]; ok {
|
|
// connection name is overwritten by connections file
|
|
continue
|
|
}
|
|
def := defaultCon == name
|
|
connections = append(connections, *makeConnection(name, dst, def, false))
|
|
}
|
|
|
|
return connections, nil
|
|
}
|
|
|
|
func getConnections(cons []string, dests map[string]Destination) ([]Connection, error) {
|
|
connections := make([]Connection, 0, len(cons))
|
|
for _, name := range cons {
|
|
if dst, ok := dests[name]; ok {
|
|
connections = append(connections, *makeConnection(name, dst, false, false))
|
|
} else {
|
|
return nil, fmt.Errorf("connection %q not found", name)
|
|
}
|
|
}
|
|
return connections, nil
|
|
}
|
|
|
|
// GetFarmConnections return all the connections for the given farm.
|
|
func (c *Config) GetFarmConnections(name string) ([]Connection, error) {
|
|
_, cons, err := c.getFarmConnections(name, false)
|
|
return cons, err
|
|
}
|
|
|
|
// GetDefaultFarmConnections returns the name of the default farm
|
|
// and the connections.
|
|
func (c *Config) GetDefaultFarmConnections() (string, []Connection, error) {
|
|
return c.getFarmConnections("", true)
|
|
}
|
|
|
|
// getFarmConnections returns all connections for the given farm,
|
|
// if def is true it will use the default farm instead of the name.
|
|
// Returns the name of the farm and the connections for it.
|
|
func (c *Config) getFarmConnections(name string, def bool) (string, []Connection, error) {
|
|
path, err := connectionsConfigFile()
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
conConf, err := readConnectionConf(path)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defaultFarm := conConf.Farm.Default
|
|
if defaultFarm == "" {
|
|
defaultFarm = c.Farms.Default
|
|
}
|
|
if def {
|
|
if defaultFarm == "" {
|
|
return "", nil, errors.New("no default farm found")
|
|
}
|
|
name = defaultFarm
|
|
}
|
|
|
|
if cons, ok := conConf.Farm.List[name]; ok {
|
|
cons, err := getConnections(cons, conConf.Connection.Connections)
|
|
return name, cons, err
|
|
}
|
|
if cons, ok := c.Farms.List[name]; ok {
|
|
cons, err := getConnections(cons, c.Engine.ServiceDestinations)
|
|
return name, cons, err
|
|
}
|
|
return "", nil, fmt.Errorf("farm %q not found", name)
|
|
}
|
|
|
|
func makeFarm(name string, cons []string, def, readWrite bool) Farm {
|
|
return Farm{
|
|
Name: name,
|
|
Connections: cons,
|
|
Default: def,
|
|
ReadWrite: readWrite,
|
|
}
|
|
}
|
|
|
|
// GetAllFarms returns all configured farms
|
|
func (c *Config) GetAllFarms() ([]Farm, error) {
|
|
path, err := connectionsConfigFile()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conConf, err := readConnectionConf(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defaultFarm := conConf.Farm.Default
|
|
if defaultFarm == "" {
|
|
defaultFarm = c.Farms.Default
|
|
}
|
|
|
|
farms := make([]Farm, 0, len(conConf.Farm.List))
|
|
for name, cons := range conConf.Farm.List {
|
|
def := defaultFarm == name
|
|
farms = append(farms, makeFarm(name, cons, def, true))
|
|
}
|
|
for name, cons := range c.Farms.List {
|
|
if _, ok := conConf.Farm.List[name]; ok {
|
|
// farm name is overwritten by connections file
|
|
continue
|
|
}
|
|
def := defaultFarm == name
|
|
farms = append(farms, makeFarm(name, cons, def, false))
|
|
}
|
|
|
|
return farms, nil
|
|
}
|