Merge pull request #21384 from Luap99/connections

rework system connection and farm storage
This commit is contained in:
openshift-merge-bot[bot] 2024-01-31 19:29:44 +00:00 committed by GitHub
commit 1a8cb15aa6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1092 additions and 1090 deletions

View File

@ -837,15 +837,15 @@ func AutoCompleteFarms(cmd *cobra.Command, args []string, toComplete string) ([]
if !validCurrentCmdLine(cmd, args, toComplete) { if !validCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
suggestions := []string{} farms, err := podmanConfig.ContainersConfDefaultsRO.GetAllFarms()
cfg, err := config.ReadCustomConfig()
if err != nil { if err != nil {
cobra.CompErrorln(err.Error()) cobra.CompErrorln(err.Error())
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
for k := range cfg.Farms.List { suggestions := make([]string, 0, len(farms))
suggestions = append(suggestions, k) for _, farm := range farms {
suggestions = append(suggestions, farm.Name)
} }
return suggestions, cobra.ShellCompDirectiveNoFileComp return suggestions, cobra.ShellCompDirectiveNoFileComp
@ -856,16 +856,17 @@ func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete
if !validCurrentCmdLine(cmd, args, toComplete) { if !validCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
suggestions := []string{}
cfg, err := config.ReadCustomConfig() cons, err := podmanConfig.ContainersConfDefaultsRO.GetAllConnections()
if err != nil { if err != nil {
cobra.CompErrorln(err.Error()) cobra.CompErrorln(err.Error())
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
for k, v := range cfg.Engine.ServiceDestinations { suggestions := make([]string, 0, len(cons))
for _, con := range cons {
// the URI will be show as description in shells like zsh // the URI will be show as description in shells like zsh
suggestions = append(suggestions, k+"\t"+v.URI) suggestions = append(suggestions, con.Name+"\t"+con.URI)
} }
return suggestions, cobra.ShellCompDirectiveNoFileComp return suggestions, cobra.ShellCompDirectiveNoFileComp

View File

@ -14,7 +14,6 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
@ -114,15 +113,10 @@ func composeDockerHost() (string, error) {
return registry.DefaultAPIAddress(), nil return registry.DefaultAPIAddress(), nil
} }
cfg, err := config.ReadCustomConfig() // TODO need to add support for --connection and --url
connection, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
if err != nil { if err != nil {
return "", err logrus.Info(err)
}
// NOTE: podman --connection=foo and --url=... are injected
// into the default connection below in `root.go`.
defaultConnection := cfg.Engine.ActiveService
if defaultConnection == "" {
switch runtime.GOOS { switch runtime.GOOS {
// If no default connection is set on Linux or FreeBSD, // If no default connection is set on Linux or FreeBSD,
// we just use the local socket by default - just as // we just use the local socket by default - just as
@ -137,10 +131,6 @@ func composeDockerHost() (string, error) {
} }
} }
connection, ok := cfg.Engine.ServiceDestinations[defaultConnection]
if !ok {
return "", fmt.Errorf("internal error: default connection %q not found in database", defaultConnection)
}
parsedConnection, err := url.Parse(connection.URI) parsedConnection, err := url.Parse(connection.URI)
if err != nil { if err != nil {
return "", fmt.Errorf("preparing connection to remote machine: %w", err) return "", fmt.Errorf("preparing connection to remote machine: %w", err)

View File

@ -7,7 +7,6 @@ import (
"strings" "strings"
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils" "github.com/containers/podman/v4/cmd/podman/utils"
@ -50,14 +49,8 @@ func init() {
cleanupFlag := "cleanup" cleanupFlag := "cleanup"
flags.BoolVar(&buildOpts.buildOptions.Cleanup, cleanupFlag, false, "Remove built images from farm nodes on success") flags.BoolVar(&buildOpts.buildOptions.Cleanup, cleanupFlag, false, "Remove built images from farm nodes on success")
podmanConfig := registry.PodmanConfig()
farmFlagName := "farm" farmFlagName := "farm"
// If remote, don't read the client's containers.conf file flags.StringVar(&buildOpts.farm, farmFlagName, "", "Farm to use for builds")
defaultFarm := ""
if !registry.IsRemote() {
defaultFarm = podmanConfig.ContainersConfDefaultsRO.Farms.Default
}
flags.StringVar(&buildOpts.farm, farmFlagName, defaultFarm, "Farm to use for builds")
_ = buildCommand.RegisterFlagCompletionFunc(farmFlagName, common.AutoCompleteFarms) _ = buildCommand.RegisterFlagCompletionFunc(farmFlagName, common.AutoCompleteFarms)
localFlagName := "local" localFlagName := "local"
@ -122,23 +115,9 @@ func build(cmd *cobra.Command, args []string) error {
} }
opts.SkipTLSVerify = !tlsVerify opts.SkipTLSVerify = !tlsVerify
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
defaultFarm := cfg.Farms.Default
if cmd.Flags().Changed("farm") {
f, err := cmd.Flags().GetString("farm")
if err != nil {
return err
}
defaultFarm = f
}
localEngine := registry.ImageEngine() localEngine := registry.ImageEngine()
ctx := registry.Context() ctx := registry.Context()
farm, err := farm.NewFarm(ctx, defaultFarm, localEngine, buildOpts.local) farm, err := farm.NewFarm(ctx, buildOpts.farm, localEngine, buildOpts.local)
if err != nil { if err != nil {
return fmt.Errorf("initializing: %w", err) return fmt.Errorf("initializing: %w", err)
} }

View File

@ -41,43 +41,39 @@ func create(cmd *cobra.Command, args []string) error {
farmName := args[0] farmName := args[0]
connections := args[1:] connections := args[1:]
cfg, err := config.ReadCustomConfig() err := config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if err != nil { if _, ok := cfg.Farm.List[farmName]; ok {
return err
}
if _, ok := cfg.Farms.List[farmName]; ok {
// if farm exists return an error // if farm exists return an error
return fmt.Errorf("farm with name %q already exists", farmName) return fmt.Errorf("farm with name %q already exists", farmName)
} }
// Can create an empty farm without any connections // Can create an empty farm without any connections
if len(connections) == 0 { if len(connections) == 0 {
cfg.Farms.List[farmName] = []string{} cfg.Farm.List[farmName] = []string{}
} }
for _, c := range connections { for _, c := range connections {
if _, ok := cfg.Engine.ServiceDestinations[c]; ok { if _, ok := cfg.Connection.Connections[c]; ok {
if slices.Contains(cfg.Farms.List[farmName], c) { if slices.Contains(cfg.Farm.List[farmName], c) {
// Don't add duplicate connections to a farm // Don't add duplicate connections to a farm
continue continue
} }
cfg.Farms.List[farmName] = append(cfg.Farms.List[farmName], c) cfg.Farm.List[farmName] = append(cfg.Farm.List[farmName], c)
} else { } else {
return fmt.Errorf("cannot create farm, %q is not a system connection", c) return fmt.Errorf("cannot create farm, %q is not a system connection", c)
} }
} }
// If this is the first farm being created, set it as the default farm // If this is the first farm being created, set it as the default farm
if len(cfg.Farms.List) == 1 { if len(cfg.Farm.List) == 1 {
cfg.Farms.Default = farmName cfg.Farm.Default = farmName
} }
err = cfg.Write() return nil
})
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Farm %q created\n", farmName) fmt.Printf("Farm %q created\n", farmName)
return nil return nil
} }

View File

@ -46,50 +46,29 @@ func init() {
formatFlagName := "format" formatFlagName := "format"
flags.StringVar(&lsOpts.Format, formatFlagName, "", "Format farm output using Go template") flags.StringVar(&lsOpts.Format, formatFlagName, "", "Format farm output using Go template")
_ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&farmOut{})) _ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&config.Farm{}))
}
type farmOut struct {
Name string
Connections []string
Default bool
} }
func list(cmd *cobra.Command, args []string) error { func list(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
format := lsOpts.Format format := lsOpts.Format
if format == "" && len(args) > 0 { if format == "" && len(args) > 0 {
format = "json" format = "json"
} }
rows := make([]farmOut, 0) farms, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetAllFarms()
for k, v := range cfg.Farms.List { if err != nil {
defaultFarm := false return err
if k == cfg.Farms.Default {
defaultFarm = true
} }
r := farmOut{ sort.Slice(farms, func(i, j int) bool {
Name: k, return farms[i].Name < farms[j].Name
Connections: v,
Default: defaultFarm,
}
rows = append(rows, r)
}
sort.Slice(rows, func(i, j int) bool {
return rows[i].Name < rows[j].Name
}) })
rpt := report.New(os.Stdout, cmd.Name()) rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush() defer rpt.Flush()
if report.IsJSON(format) { if report.IsJSON(format) {
buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ") buf, err := registry.JSONLibrary().MarshalIndent(farms, "", " ")
if err == nil { if err == nil {
fmt.Println(string(buf)) fmt.Println(string(buf))
} }
@ -100,7 +79,7 @@ func list(cmd *cobra.Command, args []string) error {
rpt, err = rpt.Parse(report.OriginUser, format) rpt, err = rpt.Parse(report.OriginUser, format)
} else { } else {
rpt, err = rpt.Parse(report.OriginPodman, rpt, err = rpt.Parse(report.OriginPodman,
"{{range .}}{{.Name}}\t{{.Connections}}\t{{.Default}}\n{{end -}}") "{{range .}}{{.Name}}\t{{.Connections}}\t{{.Default}}\t{{.ReadWrite}}\n{{end -}}")
} }
if err != nil { if err != nil {
return err return err
@ -111,11 +90,12 @@ func list(cmd *cobra.Command, args []string) error {
"Default": "Default", "Default": "Default",
"Connections": "Connections", "Connections": "Connections",
"Name": "Name", "Name": "Name",
"ReadWrite": "ReadWrite",
}}) }})
if err != nil { if err != nil {
return err return err
} }
} }
return rpt.Execute(rows) return rpt.Execute(farms)
} }

View File

@ -43,18 +43,11 @@ func init() {
} }
func rm(cmd *cobra.Command, args []string) error { func rm(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig() deletedFarms := []string{}
if err != nil { err := config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
return err
}
if rmOpts.All { if rmOpts.All {
cfg.Farms.List = make(map[string][]string) cfg.Farm.List = make(map[string][]string)
cfg.Farms.Default = "" cfg.Farm.Default = ""
if err := cfg.Write(); err != nil {
return err
}
fmt.Println("All farms have been deleted")
return nil return nil
} }
@ -63,20 +56,19 @@ func rm(cmd *cobra.Command, args []string) error {
return errors.New("requires at lease 1 arg(s), received 0") return errors.New("requires at lease 1 arg(s), received 0")
} }
if len(cfg.Farms.List) == 0 { if len(cfg.Farm.List) == 0 {
return errors.New("no existing farms; nothing to remove") return errors.New("no existing farms; nothing to remove")
} }
deletedFarms := []string{}
for _, k := range args { for _, k := range args {
if _, ok := cfg.Farms.List[k]; !ok { if _, ok := cfg.Farm.List[k]; !ok {
logrus.Warnf("farm %q doesn't exist; nothing to remove", k) logrus.Warnf("farm %q doesn't exist; nothing to remove", k)
continue continue
} }
delete(cfg.Farms.List, k) delete(cfg.Farm.List, k)
deletedFarms = append(deletedFarms, k) deletedFarms = append(deletedFarms, k)
if k == cfg.Farms.Default { if k == cfg.Farm.Default {
cfg.Farms.Default = "" cfg.Farm.Default = ""
} }
} }
// Return error if none of the given farms were deleted // Return error if none of the given farms were deleted
@ -85,15 +77,21 @@ func rm(cmd *cobra.Command, args []string) error {
} }
// Set a new default farm if the current default farm has been removed // Set a new default farm if the current default farm has been removed
if cfg.Farms.Default == "" && cfg.Farms.List != nil { if cfg.Farm.Default == "" && cfg.Farm.List != nil {
for k := range cfg.Farms.List { for k := range cfg.Farm.List {
cfg.Farms.Default = k cfg.Farm.Default = k
break break
} }
} }
if err := cfg.Write(); err != nil { return nil
})
if err != nil {
return err return err
} }
if rmOpts.All {
fmt.Println("All farms have been deleted")
return nil
}
for _, k := range deletedFarms { for _, k := range deletedFarms {
fmt.Printf("Farm %q deleted\n", k) fmt.Printf("Farm %q deleted\n", k)

View File

@ -63,37 +63,33 @@ func farmUpdate(cmd *cobra.Command, args []string) error {
return fmt.Errorf("nothing to update for farm %q, please use the --add, --remove, or --default flags to update a farm", farmName) return fmt.Errorf("nothing to update for farm %q, please use the --add, --remove, or --default flags to update a farm", farmName)
} }
cfg, err := config.ReadCustomConfig() err := config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if err != nil { if len(cfg.Farm.List) == 0 {
return err
}
if len(cfg.Farms.List) == 0 {
return errors.New("no farms are created at this time, there is nothing to update") return errors.New("no farms are created at this time, there is nothing to update")
} }
if _, ok := cfg.Farms.List[farmName]; !ok { if _, ok := cfg.Farm.List[farmName]; !ok {
return fmt.Errorf("cannot update farm, %q farm doesn't exist", farmName) return fmt.Errorf("cannot update farm, %q farm doesn't exist", farmName)
} }
if defChanged { if defChanged {
// Change the default to the given farm if --default=true // Change the default to the given farm if --default=true
if updateOpts.Default { if updateOpts.Default {
cfg.Farms.Default = farmName cfg.Farm.Default = farmName
} else { } else {
// if --default=false, user doesn't want any farms to be default so clear the DefaultFarm // if --default=false, user doesn't want any farms to be default so clear the DefaultFarm
cfg.Farms.Default = "" cfg.Farm.Default = ""
} }
} }
if val, ok := cfg.Farms.List[farmName]; ok { if val, ok := cfg.Farm.List[farmName]; ok {
cMap := make(map[string]int) cMap := make(map[string]int)
for _, c := range val { for _, c := range val {
cMap[c] = 0 cMap[c] = 0
} }
for _, cRemove := range updateOpts.Remove { for _, cRemove := range updateOpts.Remove {
connections := cfg.Farms.List[farmName] connections := cfg.Farm.List[farmName]
if slices.Contains(connections, cRemove) { if slices.Contains(connections, cRemove) {
delete(cMap, cRemove) delete(cMap, cRemove)
} else { } else {
@ -102,7 +98,7 @@ func farmUpdate(cmd *cobra.Command, args []string) error {
} }
for _, cAdd := range updateOpts.Add { for _, cAdd := range updateOpts.Add {
if _, ok := cfg.Engine.ServiceDestinations[cAdd]; ok { if _, ok := cfg.Connection.Connections[cAdd]; ok {
if _, ok := cMap[cAdd]; !ok { if _, ok := cMap[cAdd]; !ok {
cMap[cAdd] = 0 cMap[cAdd] = 0
} }
@ -115,10 +111,11 @@ func farmUpdate(cmd *cobra.Command, args []string) error {
for k := range cMap { for k := range cMap {
updatedConnections = append(updatedConnections, k) updatedConnections = append(updatedConnections, k)
} }
cfg.Farms.List[farmName] = updatedConnections cfg.Farm.List[farmName] = updatedConnections
} }
return nil
if err := cfg.Write(); err != nil { })
if err != nil {
return err return err
} }
fmt.Printf("Farm %q updated\n", farmName) fmt.Printf("Farm %q updated\n", farmName)

View File

@ -8,7 +8,6 @@ import (
"runtime" "runtime"
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/report" "github.com/containers/common/pkg/report"
"github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
@ -108,16 +107,18 @@ func hostInfo() (*entities.MachineHostInfo, error) {
host.NumberOfMachines = len(listResponse) host.NumberOfMachines = len(listResponse)
cfg, err := config.ReadCustomConfig() defaultCon := ""
if err != nil { con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
return nil, err if err == nil {
// ignore the error here we only want to know if we have a default connection to show it in list
defaultCon = con.Name
} }
// Default state of machine is stopped // Default state of machine is stopped
host.MachineState = "Stopped" host.MachineState = "Stopped"
for _, vm := range listResponse { for _, vm := range listResponse {
// Set default machine if found // Set default machine if found
if vm.Name == cfg.Engine.ActiveService { if vm.Name == defaultCon {
host.DefaultMachine = vm.Name host.DefaultMachine = vm.Name
} }
// If machine is running or starting, it is automatically the current machine // If machine is running or starting, it is automatically the current machine

View File

@ -7,7 +7,6 @@ import (
"os" "os"
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
@ -150,17 +149,20 @@ func initMachine(cmd *cobra.Command, args []string) error {
return fmt.Errorf("%s: %w", initOpts.Name, machine.ErrVMAlreadyExists) return fmt.Errorf("%s: %w", initOpts.Name, machine.ErrVMAlreadyExists)
} }
cfg, err := config.ReadCustomConfig() // check if a system connection already exists
cons, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetAllConnections()
if err != nil { if err != nil {
return err return err
} }
for _, con := range cons {
// check if a system connection already exists if con.ReadWrite {
for _, connection := range []string{initOpts.Name, fmt.Sprintf("%s-root", initOpts.Name)} { for _, connection := range []string{initOpts.Name, fmt.Sprintf("%s-root", initOpts.Name)} {
if _, valueFound := cfg.Engine.ServiceDestinations[connection]; valueFound { if con.Name == connection {
return fmt.Errorf("system connection %q already exists. consider a different machine name or remove the connection with `podman system connection rm`", connection) return fmt.Errorf("system connection %q already exists. consider a different machine name or remove the connection with `podman system connection rm`", connection)
} }
} }
}
}
for idx, vol := range initOpts.Volumes { for idx, vol := range initOpts.Volumes {
initOpts.Volumes[idx] = os.ExpandEnv(vol) initOpts.Volumes[idx] = os.ExpandEnv(vol)

View File

@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/report" "github.com/containers/common/pkg/report"
"github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
@ -79,12 +78,15 @@ func list(cmd *cobra.Command, args []string) error {
return listResponse[i].Running return listResponse[i].Running
}) })
if report.IsJSON(listFlag.format) { defaultCon := ""
machineReporter, err := toMachineFormat(listResponse) con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
if err != nil { if err == nil {
return err // ignore the error here we only want to know if we have a default connection to show it in list
defaultCon = con.Name
} }
if report.IsJSON(listFlag.format) {
machineReporter := toMachineFormat(listResponse, defaultCon)
b, err := json.MarshalIndent(machineReporter, "", " ") b, err := json.MarshalIndent(machineReporter, "", " ")
if err != nil { if err != nil {
return err return err
@ -93,11 +95,7 @@ func list(cmd *cobra.Command, args []string) error {
return nil return nil
} }
machineReporter, err := toHumanFormat(listResponse) machineReporter := toHumanFormat(listResponse, defaultCon)
if err != nil {
return err
}
return outputTemplate(cmd, machineReporter) return outputTemplate(cmd, machineReporter)
} }
@ -153,16 +151,11 @@ func streamName(imageStream string) string {
return imageStream return imageStream
} }
func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) { func toMachineFormat(vms []*machine.ListResponse, defaultCon string) []*entities.ListReporter {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
machineResponses := make([]*entities.ListReporter, 0, len(vms)) machineResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms { for _, vm := range vms {
response := new(entities.ListReporter) response := new(entities.ListReporter)
response.Default = vm.Name == cfg.Engine.ActiveService response.Default = vm.Name == defaultCon
response.Name = vm.Name response.Name = vm.Name
response.Running = vm.Running response.Running = vm.Running
response.LastUp = strTime(vm.LastUp) response.LastUp = strTime(vm.LastUp)
@ -180,19 +173,14 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, err
machineResponses = append(machineResponses, response) machineResponses = append(machineResponses, response)
} }
return machineResponses, nil return machineResponses
} }
func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) { func toHumanFormat(vms []*machine.ListResponse, defaultCon string) []*entities.ListReporter {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
humanResponses := make([]*entities.ListReporter, 0, len(vms)) humanResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms { for _, vm := range vms {
response := new(entities.ListReporter) response := new(entities.ListReporter)
if vm.Name == cfg.Engine.ActiveService { if vm.Name == defaultCon {
response.Name = vm.Name + "*" response.Name = vm.Name + "*"
response.Default = true response.Default = true
} else { } else {
@ -218,5 +206,5 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error
humanResponses = append(humanResponses, response) humanResponses = append(humanResponses, response)
} }
return humanResponses, nil return humanResponses
} }

View File

@ -7,7 +7,6 @@ import (
"net/url" "net/url"
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils" "github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
@ -93,12 +92,12 @@ func ssh(cmd *cobra.Command, args []string) error {
} }
func remoteConnectionUsername() (string, error) { func remoteConnectionUsername() (string, error) {
cfg, err := config.ReadCustomConfig() con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
if err != nil { if err != nil {
return "", err return "", err
} }
dest := cfg.Engine.ServiceDestinations[cfg.Engine.ActiveService].URI
uri, err := url.Parse(dest) uri, err := url.Parse(con.URI)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -143,7 +143,7 @@ func Execute() {
} }
// readRemoteCliFlags reads cli flags related to operating podman remotely // readRemoteCliFlags reads cli flags related to operating podman remotely
func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) (err error) { func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) error {
conf := podmanConfig.ContainersConfDefaultsRO conf := podmanConfig.ContainersConfDefaultsRO
contextConn, host := cmd.Root().LocalFlags().Lookup("context"), cmd.Root().LocalFlags().Lookup("host") contextConn, host := cmd.Root().LocalFlags().Lookup("context"), cmd.Root().LocalFlags().Lookup("host")
conn, url := cmd.Root().LocalFlags().Lookup("connection"), cmd.Root().LocalFlags().Lookup("url") conn, url := cmd.Root().LocalFlags().Lookup("connection"), cmd.Root().LocalFlags().Lookup("url")
@ -151,35 +151,32 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig)
switch { switch {
case conn != nil && conn.Changed: case conn != nil && conn.Changed:
if contextConn != nil && contextConn.Changed { if contextConn != nil && contextConn.Changed {
err = fmt.Errorf("use of --connection and --context at the same time is not allowed") return fmt.Errorf("use of --connection and --context at the same time is not allowed")
return
} }
if dest, ok := conf.Engine.ServiceDestinations[conn.Value.String()]; ok { con, err := conf.GetConnection(conn.Value.String(), false)
podmanConfig.URI = dest.URI if err != nil {
podmanConfig.Identity = dest.Identity return err
podmanConfig.MachineMode = dest.IsMachine
return
} }
err = fmt.Errorf("connection %q not found", conn.Value.String()) podmanConfig.URI = con.URI
return podmanConfig.Identity = con.Identity
podmanConfig.MachineMode = con.IsMachine
case url.Changed: case url.Changed:
podmanConfig.URI = url.Value.String() podmanConfig.URI = url.Value.String()
return
case contextConn != nil && contextConn.Changed: case contextConn != nil && contextConn.Changed:
service := contextConn.Value.String() service := contextConn.Value.String()
if service != "default" { if service != "default" {
if dest, ok := conf.Engine.ServiceDestinations[contextConn.Value.String()]; ok { con, err := conf.GetConnection(service, false)
podmanConfig.URI = dest.URI if err != nil {
podmanConfig.Identity = dest.Identity return err
podmanConfig.MachineMode = dest.IsMachine
return
} }
return fmt.Errorf("connection %q not found", service) podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.MachineMode = con.IsMachine
} }
case host.Changed: case host.Changed:
podmanConfig.URI = host.Value.String() podmanConfig.URI = host.Value.String()
} }
return return nil
} }
// setupRemoteConnection returns information about the active service destination // setupRemoteConnection returns information about the active service destination
@ -191,29 +188,31 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig)
func setupRemoteConnection(podmanConfig *entities.PodmanConfig) error { func setupRemoteConnection(podmanConfig *entities.PodmanConfig) error {
conf := podmanConfig.ContainersConfDefaultsRO conf := podmanConfig.ContainersConfDefaultsRO
connEnv, hostEnv, sshkeyEnv := os.Getenv("CONTAINER_CONNECTION"), os.Getenv("CONTAINER_HOST"), os.Getenv("CONTAINER_SSHKEY") connEnv, hostEnv, sshkeyEnv := os.Getenv("CONTAINER_CONNECTION"), os.Getenv("CONTAINER_HOST"), os.Getenv("CONTAINER_SSHKEY")
dest, destFound := conf.Engine.ServiceDestinations[conf.Engine.ActiveService]
switch { switch {
case connEnv != "": case connEnv != "":
if ConnEnvDest, ok := conf.Engine.ServiceDestinations[connEnv]; ok { con, err := conf.GetConnection(connEnv, false)
podmanConfig.URI = ConnEnvDest.URI if err != nil {
podmanConfig.Identity = ConnEnvDest.Identity return err
podmanConfig.MachineMode = ConnEnvDest.IsMachine
return nil
} }
return fmt.Errorf("connection %q not found", connEnv) podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.MachineMode = con.IsMachine
case hostEnv != "": case hostEnv != "":
if sshkeyEnv != "" { if sshkeyEnv != "" {
podmanConfig.Identity = sshkeyEnv podmanConfig.Identity = sshkeyEnv
} }
podmanConfig.URI = hostEnv podmanConfig.URI = hostEnv
case destFound:
podmanConfig.URI = dest.URI
podmanConfig.Identity = dest.Identity
podmanConfig.MachineMode = dest.IsMachine
default: default:
con, err := conf.GetConnection("", true)
if err == nil {
podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.MachineMode = con.IsMachine
} else {
podmanConfig.URI = registry.DefaultAPIAddress() podmanConfig.URI = registry.DefaultAPIAddress()
} }
}
return nil return nil
} }

View File

@ -176,49 +176,39 @@ func add(cmd *cobra.Command, args []string) error {
logrus.Warnf("%q unknown scheme, no validation provided", uri.Scheme) logrus.Warnf("%q unknown scheme, no validation provided", uri.Scheme)
} }
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
if cmd.Flags().Changed("default") {
if cOpts.Default {
cfg.Engine.ActiveService = args[0]
}
}
dst := config.Destination{ dst := config.Destination{
URI: uri.String(), URI: uri.String(),
Identity: cOpts.Identity,
} }
if cmd.Flags().Changed("identity") { connection := args[0]
dst.Identity = cOpts.Identity return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if cOpts.Default {
cfg.Connection.Default = connection
} }
if cfg.Engine.ServiceDestinations == nil { if cfg.Connection.Connections == nil {
cfg.Engine.ServiceDestinations = map[string]config.Destination{ cfg.Connection.Connections = map[string]config.Destination{
args[0]: dst, connection: dst,
} }
cfg.Engine.ActiveService = args[0] cfg.Connection.Default = connection
} else { } else {
cfg.Engine.ServiceDestinations[args[0]] = dst cfg.Connection.Connections[connection] = dst
} }
// Create or update an existing farm with the connection being added
if cOpts.Farm != "" { if cOpts.Farm != "" {
if cfg.Farms.List == nil { if len(cfg.Farm.List) == 0 {
cfg.Farms.List = map[string][]string{ cfg.Farm.Default = cOpts.Farm
cOpts.Farm: {args[0]},
} }
cfg.Farms.Default = cOpts.Farm if val, ok := cfg.Farm.List[cOpts.Farm]; ok {
cfg.Farm.List[cOpts.Farm] = append(val, connection)
} else { } else {
if val, ok := cfg.Farms.List[cOpts.Farm]; ok { cfg.Farm.List[cOpts.Farm] = []string{connection}
cfg.Farms.List[cOpts.Farm] = append(val, args[0])
} else {
cfg.Farms.List[cOpts.Farm] = []string{args[0]}
} }
} }
} return nil
return cfg.Write() })
} }
func create(cmd *cobra.Command, args []string) error { func create(cmd *cobra.Command, args []string) error {
@ -237,24 +227,21 @@ func create(cmd *cobra.Command, args []string) error {
return err return err
} }
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
dst := config.Destination{ dst := config.Destination{
URI: uri.String(), URI: uri.String(),
} }
if cfg.Engine.ServiceDestinations == nil { return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
cfg.Engine.ServiceDestinations = map[string]config.Destination{ if cfg.Connection.Connections == nil {
cfg.Connection.Connections = map[string]config.Destination{
args[0]: dst, args[0]: dst,
} }
cfg.Engine.ActiveService = args[0] cfg.Connection.Default = args[0]
} else { } else {
cfg.Engine.ServiceDestinations[args[0]] = dst cfg.Connection.Connections[args[0]] = dst
} }
return cfg.Write() return nil
})
} }
func translateDest(path string) (string, error) { func translateDest(path string) (string, error) {

View File

@ -45,15 +45,13 @@ func init() {
} }
func defaultRunE(cmd *cobra.Command, args []string) error { func defaultRunE(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig() connection := args[0]
if err != nil { return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
return err if _, found := cfg.Connection.Connections[connection]; !found {
return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", connection)
} }
if _, found := cfg.Engine.ServiceDestinations[args[0]]; !found { cfg.Connection.Default = connection
return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0]) return nil
} })
cfg.Engine.ActiveService = args[0]
return cfg.Write()
} }

View File

@ -41,7 +41,7 @@ var (
func init() { func init() {
initFlags := func(cmd *cobra.Command) { initFlags := func(cmd *cobra.Command) {
cmd.Flags().StringP("format", "f", "", "Custom Go template for printing connections") cmd.Flags().StringP("format", "f", "", "Custom Go template for printing connections")
_ = cmd.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(&namedDestination{})) _ = cmd.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(&config.Connection{}))
cmd.Flags().BoolP("quiet", "q", false, "Custom Go template for printing connections") cmd.Flags().BoolP("quiet", "q", false, "Custom Go template for printing connections")
} }
@ -62,22 +62,11 @@ func init() {
initFlags(inspectCmd) initFlags(inspectCmd)
} }
type namedDestination struct {
Name string
config.Destination
Default bool
}
func list(cmd *cobra.Command, _ []string) error { func list(cmd *cobra.Command, _ []string) error {
return inspect(cmd, nil) return inspect(cmd, nil)
} }
func inspect(cmd *cobra.Command, args []string) error { func inspect(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
format := cmd.Flag("format").Value.String() format := cmd.Flag("format").Value.String()
if format == "" && args != nil { if format == "" && args != nil {
format = "json" format = "json"
@ -87,31 +76,23 @@ func inspect(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
rows := make([]namedDestination, 0)
for k, v := range cfg.Engine.ServiceDestinations { cons, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetAllConnections()
if args != nil && !slices.Contains(args, k) { if err != nil {
return err
}
rows := make([]config.Connection, 0, len(cons))
for _, con := range cons {
if args != nil && !slices.Contains(args, con.Name) {
continue continue
} }
if quiet { if quiet {
fmt.Println(k) fmt.Println(con.Name)
continue continue
} }
def := false
if k == cfg.Engine.ActiveService {
def = true
}
r := namedDestination{ rows = append(rows, con)
Name: k,
Destination: config.Destination{
Identity: v.Identity,
URI: v.URI,
IsMachine: v.IsMachine,
},
Default: def,
}
rows = append(rows, r)
} }
if quiet { if quiet {
@ -137,7 +118,7 @@ func inspect(cmd *cobra.Command, args []string) error {
rpt, err = rpt.Parse(report.OriginUser, format) rpt, err = rpt.Parse(report.OriginUser, format)
} else { } else {
rpt, err = rpt.Parse(report.OriginPodman, rpt, err = rpt.Parse(report.OriginPodman,
"{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n{{end -}}") "{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\t{{.ReadWrite}}\n{{end -}}")
} }
if err != nil { if err != nil {
return err return err
@ -149,6 +130,7 @@ func inspect(cmd *cobra.Command, args []string) error {
"Identity": "Identity", "Identity": "Identity",
"Name": "Name", "Name": "Name",
"URI": "URI", "URI": "URI",
"ReadWrite": "ReadWrite",
}}) }})
if err != nil { if err != nil {
return err return err

View File

@ -48,43 +48,35 @@ func init() {
} }
func rm(cmd *cobra.Command, args []string) error { func rm(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig() return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if err != nil {
return err
}
if rmOpts.All { if rmOpts.All {
for k := range cfg.Engine.ServiceDestinations { cfg.Connection.Connections = nil
delete(cfg.Engine.ServiceDestinations, k) cfg.Connection.Default = ""
}
cfg.Engine.ActiveService = ""
// Clear all the connections in any existing farms // Clear all the connections in any existing farms
for k := range cfg.Farms.List { for k := range cfg.Farm.List {
cfg.Farms.List[k] = []string{} cfg.Farm.List[k] = []string{}
} }
return cfg.Write() return nil
} }
if len(args) != 1 { if len(args) != 1 {
return errors.New("accepts 1 arg(s), received 0") return errors.New("accepts 1 arg(s), received 0")
} }
if cfg.Engine.ServiceDestinations != nil { delete(cfg.Connection.Connections, args[0])
delete(cfg.Engine.ServiceDestinations, args[0]) if cfg.Connection.Default == args[0] {
} cfg.Connection.Default = ""
if cfg.Engine.ActiveService == args[0] {
cfg.Engine.ActiveService = ""
} }
// If there are existing farm, remove the deleted connection that might be part of a farm // If there are existing farm, remove the deleted connection that might be part of a farm
for k, v := range cfg.Farms.List { for k, v := range cfg.Farm.List {
index := slices.Index(v, args[0]) index := slices.Index(v, args[0])
if index > -1 { if index > -1 {
cfg.Farms.List[k] = append(v[:index], v[index+1:]...) cfg.Farm.List[k] = append(v[:index], v[index+1:]...)
} }
} }
return cfg.Write() return nil
})
} }

View File

@ -33,21 +33,18 @@ func init() {
} }
func rename(cmd *cobra.Command, args []string) error { func rename(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig() return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if err != nil { if _, found := cfg.Connection.Connections[args[0]]; !found {
return err
}
if _, found := cfg.Engine.ServiceDestinations[args[0]]; !found {
return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0]) return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0])
} }
cfg.Engine.ServiceDestinations[args[1]] = cfg.Engine.ServiceDestinations[args[0]] cfg.Connection.Connections[args[1]] = cfg.Connection.Connections[args[0]]
delete(cfg.Engine.ServiceDestinations, args[0]) delete(cfg.Connection.Connections, args[0])
if cfg.Engine.ActiveService == args[0] { if cfg.Connection.Default == args[0] {
cfg.Engine.ActiveService = args[1] cfg.Connection.Default = args[1]
} }
return cfg.Write() return nil
})
} }

View File

@ -19,17 +19,18 @@ Change the default output format. This can be of a supported type like 'json' o
Valid placeholders for the Go template listed below: Valid placeholders for the Go template listed below:
| **Placeholder** | **Description** | | **Placeholder** | **Description** |
| --------------- | ------------------------------------------ | | --------------- | --------------------------------------------------------------------- |
| .Connections | List of all system connections in the farm | | .Connections | List of all system connections in the farm |
| .Default | Indicates whether farm is the default | | .Default | Indicates whether farm is the default |
| .Name | Farm name | | .Name | Farm name |
| .ReadWrite | Indicates if this farm can be modified using the podman farm commands |
## EXAMPLE ## EXAMPLE
``` ```
$ podman farm list $ podman farm list
Name Connections Default Name Connections Default ReadWrite
farm1 [f38 f37] false farm1 [f38 f37] false true
farm2 [f37] true farm2 [f37] true true
``` ```
## SEE ALSO ## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-farm(1)](podman-farm.1.md)** **[podman(1)](podman.1.md)**, **[podman-farm(1)](podman-farm.1.md)**

View File

@ -23,6 +23,7 @@ Valid placeholders for the Go template listed below:
| .Default | Indicates whether connection is the default | | .Default | Indicates whether connection is the default |
| .Identity | Path to file containing SSH identity | | .Identity | Path to file containing SSH identity |
| .Name | Connection Name/Identifier | | .Name | Connection Name/Identifier |
| .ReadWrite | Indicates if this connection can be modified using the system connection commands |
| .URI | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] | | .URI | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] |
#### **--quiet**, **-q** #### **--quiet**, **-q**
@ -32,9 +33,9 @@ Only show connection names
## EXAMPLE ## EXAMPLE
``` ```
$ podman system connection list $ podman system connection list
Name URI Identity Default Name URI Identity Default ReadWrite
devl ssh://root@example.com:/run/podman/podman.sock ~/.ssh/id_rsa True deva ssh://root@example.com:/run/podman/podman.sock ~/.ssh/id_rsa true true
devl ssh://user@example.com:/run/user/1000/podman/podman.sock ~/.ssh/id_rsa False devb ssh://user@example.com:/run/user/1000/podman/podman.sock ~/.ssh/id_rsa false true
``` ```
## SEE ALSO ## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)** **[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**

View File

@ -252,6 +252,11 @@ Set default `--url` value to access Podman service. Automatically enables --remo
Set default `--identity` path to ssh key file value used to access Podman service. Set default `--identity` path to ssh key file value used to access Podman service.
#### **PODMAN_CONNECTIONS_CONF**
The path to the file where the system connections and farms created with `podman system connection add`
and `podman farm add` are stored, by default it uses `~/.config/containers/podman-connections.conf`.
#### **STORAGE_DRIVER** #### **STORAGE_DRIVER**
Set default `--storage-driver` value. Set default `--storage-driver` value.

6
go.mod
View File

@ -11,14 +11,14 @@ require (
github.com/checkpoint-restore/go-criu/v7 v7.0.0 github.com/checkpoint-restore/go-criu/v7 v7.0.0
github.com/containernetworking/plugins v1.4.0 github.com/containernetworking/plugins v1.4.0
github.com/containers/buildah v1.33.2-0.20231121195905-d1a1c53c8e1c github.com/containers/buildah v1.33.2-0.20231121195905-d1a1c53c8e1c
github.com/containers/common v0.57.1-0.20240129201029-3310a75e3608 github.com/containers/common v0.57.1-0.20240130143645-b26099256b92
github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon v2.0.20+incompatible
github.com/containers/gvisor-tap-vsock v0.7.2 github.com/containers/gvisor-tap-vsock v0.7.2
github.com/containers/image/v5 v5.29.2-0.20240129204525-816800b5daf7 github.com/containers/image/v5 v5.29.2-0.20240130233108-e66a1ade2efc
github.com/containers/libhvee v0.6.0 github.com/containers/libhvee v0.6.0
github.com/containers/ocicrypt v1.1.9 github.com/containers/ocicrypt v1.1.9
github.com/containers/psgo v1.8.0 github.com/containers/psgo v1.8.0
github.com/containers/storage v1.52.1-0.20240129173630-7a525ce0e2bc github.com/containers/storage v1.52.1-0.20240130205044-62997abeaf2f
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09 github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09
github.com/coreos/stream-metadata-go v0.4.4 github.com/coreos/stream-metadata-go v0.4.4
github.com/crc-org/vfkit v0.5.0 github.com/crc-org/vfkit v0.5.0

12
go.sum
View File

@ -257,14 +257,14 @@ github.com/containernetworking/plugins v1.4.0 h1:+w22VPYgk7nQHw7KT92lsRmuToHvb7w
github.com/containernetworking/plugins v1.4.0/go.mod h1:UYhcOyjefnrQvKvmmyEKsUA+M9Nfn7tqULPpH0Pkcj0= github.com/containernetworking/plugins v1.4.0/go.mod h1:UYhcOyjefnrQvKvmmyEKsUA+M9Nfn7tqULPpH0Pkcj0=
github.com/containers/buildah v1.33.2-0.20231121195905-d1a1c53c8e1c h1:E7nxvH3N3kpyson0waJv1X+eY9hAs+x2zQswsK+//yY= github.com/containers/buildah v1.33.2-0.20231121195905-d1a1c53c8e1c h1:E7nxvH3N3kpyson0waJv1X+eY9hAs+x2zQswsK+//yY=
github.com/containers/buildah v1.33.2-0.20231121195905-d1a1c53c8e1c/go.mod h1:oMNfVrZGEfWVOxXTNOYPMdZzDfSo2umURK/TO0d8TRk= github.com/containers/buildah v1.33.2-0.20231121195905-d1a1c53c8e1c/go.mod h1:oMNfVrZGEfWVOxXTNOYPMdZzDfSo2umURK/TO0d8TRk=
github.com/containers/common v0.57.1-0.20240129201029-3310a75e3608 h1:P+3HLEBeuFr/XcFtyZIS8uk5zdbcvtrHKCSzHlUUbdY= github.com/containers/common v0.57.1-0.20240130143645-b26099256b92 h1:Q60+ofGhDjVxY5lvYmmcVN8aeS9gtQ6pAn/pyLh7rRM=
github.com/containers/common v0.57.1-0.20240129201029-3310a75e3608/go.mod h1:Na7hGh5WnmB0RdGkKyb6JQb6DtKrs5qoIGrPucuR8t0= github.com/containers/common v0.57.1-0.20240130143645-b26099256b92/go.mod h1:Na7hGh5WnmB0RdGkKyb6JQb6DtKrs5qoIGrPucuR8t0=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/gvisor-tap-vsock v0.7.2 h1:6CyU5D85C0/DciRRd7W0bPljK4FAS+DPrrHEQMHfZKY= github.com/containers/gvisor-tap-vsock v0.7.2 h1:6CyU5D85C0/DciRRd7W0bPljK4FAS+DPrrHEQMHfZKY=
github.com/containers/gvisor-tap-vsock v0.7.2/go.mod h1:6NiTxh2GCVxZQLPzfuEB78/Osp2Usd9uf6nLdd6PiUY= github.com/containers/gvisor-tap-vsock v0.7.2/go.mod h1:6NiTxh2GCVxZQLPzfuEB78/Osp2Usd9uf6nLdd6PiUY=
github.com/containers/image/v5 v5.29.2-0.20240129204525-816800b5daf7 h1:xnnFpYdUjDn2MZYXmvcopGEvWVO/J0V8OexEnlnIGf0= github.com/containers/image/v5 v5.29.2-0.20240130233108-e66a1ade2efc h1:3I5+mrrG7Fuv4aA13t1hAMQcjN3rTAQInfbxa5P+XH4=
github.com/containers/image/v5 v5.29.2-0.20240129204525-816800b5daf7/go.mod h1:op1w+ftmdbE3UNW/6as8mruL1i5AMq6nH+btNOykMcU= github.com/containers/image/v5 v5.29.2-0.20240130233108-e66a1ade2efc/go.mod h1:oMMRA6avp1Na54lVPCj/OvcfXDMLlzfy3H7xeRiWmmI=
github.com/containers/libhvee v0.6.0 h1:tUzwSz8R0GjR6IctgDnkTMjdtCk5Mxhpai4Vyv6UeF4= github.com/containers/libhvee v0.6.0 h1:tUzwSz8R0GjR6IctgDnkTMjdtCk5Mxhpai4Vyv6UeF4=
github.com/containers/libhvee v0.6.0/go.mod h1:f/q1wCdQqOLiK3IZqqBfOD7exMZYBU5pDYsrMa/pSFg= github.com/containers/libhvee v0.6.0/go.mod h1:f/q1wCdQqOLiK3IZqqBfOD7exMZYBU5pDYsrMa/pSFg=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
@ -279,8 +279,8 @@ github.com/containers/ocicrypt v1.1.9/go.mod h1:dTKx1918d8TDkxXvarscpNVY+lyPakPN
github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY= github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY=
github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc= github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc=
github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s= github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s=
github.com/containers/storage v1.52.1-0.20240129173630-7a525ce0e2bc h1:Cfv5hg2OtNZnIZLfOVmLjObX0DuJb+aqkTl+2+dNweA= github.com/containers/storage v1.52.1-0.20240130205044-62997abeaf2f h1:BJSLHe7f1tgu53d8mGIK/y2KhEev5lggWlIk1rWYT7k=
github.com/containers/storage v1.52.1-0.20240129173630-7a525ce0e2bc/go.mod h1:T/ZMocbhShnMLIF0pdkiLPwpkwlGlyUWJeSXnfC/uew= github.com/containers/storage v1.52.1-0.20240130205044-62997abeaf2f/go.mod h1:T/ZMocbhShnMLIF0pdkiLPwpkwlGlyUWJeSXnfC/uew=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=

View File

@ -33,11 +33,6 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool, sshMode
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
confR, err := config.New(nil) // create a hand made config for the remote engine since we might use remote and native at once
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("could not make config: %w", err)
}
locations := []*entities.ImageScpOptions{} locations := []*entities.ImageScpOptions{}
cliConnections := []string{} cliConnections := []string{}
args := []string{src} args := []string{src}
@ -84,17 +79,15 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool, sshMode
cliConnections = []string{} cliConnections = []string{}
} }
cfg, err := config.ReadCustomConfig() // get ready to set ssh destination if necessary cfg, err := config.Default()
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
var serv map[string]config.Destination err = GetServiceInformation(&sshInfo, cliConnections, cfg)
serv, err = GetServiceInformation(&sshInfo, cliConnections, cfg)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
confR.Engine = config.EngineConfig{Remote: true, CgroupManager: "cgroupfs", ServiceDestinations: serv} // pass the service dest (either remote or something else) to engine
saveCmd, loadCmd := CreateCommands(source, dest, parentFlags, podman) saveCmd, loadCmd := CreateCommands(source, dest, parentFlags, podman)
switch { switch {
@ -401,15 +394,15 @@ func RemoteArgLength(input string, side int) int {
} }
// GetServiceInformation takes the parsed list of hosts to connect to and validates the information // GetServiceInformation takes the parsed list of hosts to connect to and validates the information
func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections []string, cfg *config.Config) (map[string]config.Destination, error) { func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections []string, cfg *config.Config) error {
var serv map[string]config.Destination
var urlS string var urlS string
var iden string var iden string
for i, val := range cliConnections { for i, val := range cliConnections {
connection, _, _ := strings.Cut(val, "::") connection, _, _ := strings.Cut(val, "::")
sshInfo.Connections = append(sshInfo.Connections, connection) sshInfo.Connections = append(sshInfo.Connections, connection)
conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]] conn, err := cfg.GetConnection(sshInfo.Connections[i], false)
if found { if err == nil {
// connection found
urlS = conn.URI urlS = conn.URI
iden = conn.Identity iden = conn.Identity
} else { // no match, warn user and do a manual connection. } else { // no match, warn user and do a manual connection.
@ -419,17 +412,17 @@ func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections
} }
urlFinal, err := url.Parse(urlS) // create an actual url to pass to exec command urlFinal, err := url.Parse(urlS) // create an actual url to pass to exec command
if err != nil { if err != nil {
return nil, err return err
} }
if urlFinal.User.Username() == "" { if urlFinal.User.Username() == "" {
if urlFinal.User, err = GetUserInfo(urlFinal); err != nil { if urlFinal.User, err = GetUserInfo(urlFinal); err != nil {
return nil, err return err
} }
} }
sshInfo.URI = append(sshInfo.URI, urlFinal) sshInfo.URI = append(sshInfo.URI, urlFinal)
sshInfo.Identities = append(sshInfo.Identities, iden) sshInfo.Identities = append(sshInfo.Identities, iden)
} }
return serv, nil return nil
} }
func GetUserInfo(uri *url.URL) (*url.Userinfo, error) { func GetUserInfo(uri *url.URL) (*url.Userinfo, error) {

View File

@ -32,7 +32,7 @@ type Schedule struct {
platformBuilders map[string]string // target->connection platformBuilders map[string]string // target->connection
} }
func newFarmWithBuilders(_ context.Context, name string, destinations *map[string]config.Destination, localEngine entities.ImageEngine, buildLocal bool) (*Farm, error) { func newFarmWithBuilders(_ context.Context, name string, cons []config.Connection, localEngine entities.ImageEngine, buildLocal bool) (*Farm, error) {
farm := &Farm{ farm := &Farm{
builders: make(map[string]entities.ImageEngine), builders: make(map[string]entities.ImageEngine),
localEngine: localEngine, localEngine: localEngine,
@ -43,22 +43,22 @@ func newFarmWithBuilders(_ context.Context, name string, destinations *map[strin
builderGroup multierror.Group builderGroup multierror.Group
) )
// Set up the remote connections to handle the builds // Set up the remote connections to handle the builds
for name, dest := range *destinations { for _, con := range cons {
name, dest := name, dest con := con
builderGroup.Go(func() error { builderGroup.Go(func() error {
fmt.Printf("Connecting to %q\n", name) fmt.Printf("Connecting to %q\n", con.Name)
engine, err := infra.NewImageEngine(&entities.PodmanConfig{ engine, err := infra.NewImageEngine(&entities.PodmanConfig{
EngineMode: entities.TunnelMode, EngineMode: entities.TunnelMode,
URI: dest.URI, URI: con.URI,
Identity: dest.Identity, Identity: con.Identity,
MachineMode: dest.IsMachine, MachineMode: con.IsMachine,
FarmNodeName: name, FarmNodeName: con.Name,
}) })
if err != nil { if err != nil {
return fmt.Errorf("initializing image engine at %q: %w", dest.URI, err) return fmt.Errorf("initializing image engine at %q: %w", con.URI, err)
} }
defer fmt.Printf("Builder %q ready\n", name) defer fmt.Printf("Builder %q ready\n", con.Name)
builderMutex.Lock() builderMutex.Lock()
defer builderMutex.Unlock() defer builderMutex.Unlock()
farm.builders[name] = engine farm.builders[name] = engine
@ -90,12 +90,12 @@ func newFarmWithBuilders(_ context.Context, name string, destinations *map[strin
func NewFarm(ctx context.Context, name string, localEngine entities.ImageEngine, buildLocal bool) (*Farm, error) { func NewFarm(ctx context.Context, name string, localEngine entities.ImageEngine, buildLocal bool) (*Farm, error) {
// Get the destinations of the connections specified in the farm // Get the destinations of the connections specified in the farm
destinations, err := getFarmDestinations(name) name, destinations, err := getFarmDestinations(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newFarmWithBuilders(ctx, name, &destinations, localEngine, buildLocal) return newFarmWithBuilders(ctx, name, destinations, localEngine, buildLocal)
} }
// Done performs any necessary end-of-process cleanup for the farm's members. // Done performs any necessary end-of-process cleanup for the farm's members.
@ -460,22 +460,21 @@ func (f *Farm) Build(ctx context.Context, schedule Schedule, options entities.Bu
return nil return nil
} }
func getFarmDestinations(name string) (map[string]config.Destination, error) { func getFarmDestinations(name string) (string, []config.Connection, error) {
dest := make(map[string]config.Destination) cfg, err := config.Default()
cfg, err := config.ReadCustomConfig()
if err != nil { if err != nil {
return dest, err return "", nil, err
} }
// If no farm name is given, then grab all the service destinations available
if name == "" { if name == "" {
return cfg.Engine.ServiceDestinations, nil if name, cons, err := cfg.GetDefaultFarmConnections(); err == nil {
// Use default farm if is there is one
return name, cons, nil
} }
// If no farm name is given, then grab all the service destinations available
// Go through the connections in the farm and get their destination cons, err := cfg.GetAllConnections()
for _, c := range cfg.Farms.List[name] { return name, cons, err
dest[c] = cfg.Engine.ServiceDestinations[c]
} }
cons, err := cfg.GetFarmConnections(name)
return dest, nil return name, cons, err
} }

View File

@ -17,93 +17,81 @@ func AddConnection(uri fmt.Stringer, name, identity string, isDefault bool) erro
if len(identity) < 1 { if len(identity) < 1 {
return errors.New("identity must be defined") return errors.New("identity must be defined")
} }
cfg, err := config.ReadCustomConfig()
if err != nil { return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
return err if _, ok := cfg.Connection.Connections[name]; ok {
}
if _, ok := cfg.Engine.ServiceDestinations[name]; ok {
return errors.New("cannot overwrite connection") return errors.New("cannot overwrite connection")
} }
if isDefault {
cfg.Engine.ActiveService = name
}
dst := config.Destination{ dst := config.Destination{
URI: uri.String(), URI: uri.String(),
IsMachine: true, IsMachine: true,
Identity: identity,
} }
dst.Identity = identity
if cfg.Engine.ServiceDestinations == nil { if isDefault {
cfg.Engine.ServiceDestinations = map[string]config.Destination{ cfg.Connection.Default = name
}
if cfg.Connection.Connections == nil {
cfg.Connection.Connections = map[string]config.Destination{
name: dst, name: dst,
} }
cfg.Engine.ActiveService = name cfg.Connection.Default = name
} else { } else {
cfg.Engine.ServiceDestinations[name] = dst cfg.Connection.Connections[name] = dst
}
return cfg.Write()
}
func AnyConnectionDefault(name ...string) (bool, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return false, err
}
for _, n := range name {
if n == cfg.Engine.ActiveService {
return true, nil
}
} }
return false, nil return nil
})
} }
func ChangeConnectionURI(name string, uri fmt.Stringer) error { func ChangeConnectionURI(name string, uri fmt.Stringer) error {
cfg, err := config.ReadCustomConfig() return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if err != nil { dst, ok := cfg.Connection.Connections[name]
return err
}
dst, ok := cfg.Engine.ServiceDestinations[name]
if !ok { if !ok {
return errors.New("connection not found") return errors.New("connection not found")
} }
dst.URI = uri.String() dst.URI = uri.String()
cfg.Engine.ServiceDestinations[name] = dst cfg.Connection.Connections[name] = dst
return cfg.Write() return nil
})
} }
func ChangeDefault(name string) error { // UpdateConnectionIfDefault updates the default connection to the rootful/rootless when depending
cfg, err := config.ReadCustomConfig() // on the bool but only if other rootful/less connection was already the default.
if err != nil { // Returns true if it modified the default
return err func UpdateConnectionIfDefault(rootful bool, name, rootfulName string) error {
return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if name == cfg.Connection.Default && rootful {
cfg.Connection.Default = rootfulName
} else if rootfulName == cfg.Connection.Default && !rootful {
cfg.Connection.Default = name
} }
return nil
cfg.Engine.ActiveService = name })
return cfg.Write()
} }
func RemoveConnections(names ...string) error { func RemoveConnections(names ...string) error {
cfg, err := config.ReadCustomConfig() return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if err != nil {
return err
}
for _, name := range names { for _, name := range names {
if _, ok := cfg.Engine.ServiceDestinations[name]; ok { if _, ok := cfg.Connection.Connections[name]; ok {
delete(cfg.Engine.ServiceDestinations, name) delete(cfg.Connection.Connections, name)
} else { } else {
return fmt.Errorf("unable to find connection named %q", name) return fmt.Errorf("unable to find connection named %q", name)
} }
if cfg.Engine.ActiveService == name { if cfg.Connection.Default == name {
cfg.Engine.ActiveService = "" cfg.Connection.Default = ""
for service := range cfg.Engine.ServiceDestinations { }
cfg.Engine.ActiveService = service }
for service := range cfg.Connection.Connections {
cfg.Connection.Default = service
break break
} }
} return nil
} })
return cfg.Write()
} }
// removeFilesAndConnections removes any files and connections with the given names // removeFilesAndConnections removes any files and connections with the given names

View File

@ -158,22 +158,7 @@ following command in your terminal session:
// SetRootful modifies the machine's default connection to be either rootful or // SetRootful modifies the machine's default connection to be either rootful or
// rootless // rootless
func SetRootful(rootful bool, name, rootfulName string) error { func SetRootful(rootful bool, name, rootfulName string) error {
changeCon, err := AnyConnectionDefault(name, rootfulName) return UpdateConnectionIfDefault(rootful, name, rootfulName)
if err != nil {
return err
}
if changeCon {
newDefault := name
if rootful {
newDefault += "-root"
}
err := ChangeDefault(newDefault)
if err != nil {
return err
}
}
return nil
} }
// WriteConfig writes the machine's JSON config file // WriteConfig writes the machine's JSON config file

View File

@ -122,6 +122,7 @@ var (
// invalid containers.conf files to fail the cleanup. // invalid containers.conf files to fail the cleanup.
os.Unsetenv("CONTAINERS_CONF") os.Unsetenv("CONTAINERS_CONF")
os.Unsetenv("CONTAINERS_CONF_OVERRIDE") os.Unsetenv("CONTAINERS_CONF_OVERRIDE")
os.Unsetenv("PODMAN_CONNECTIONS_CONF")
podmanTest.Cleanup() podmanTest.Cleanup()
f := CurrentSpecReport() f := CurrentSpecReport()
processTestResult(f) processTestResult(f)

View File

@ -4,7 +4,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/containers/common/pkg/config"
. "github.com/containers/podman/v4/test/utils" . "github.com/containers/podman/v4/test/utils"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -13,21 +12,20 @@ import (
func setupContainersConfWithSystemConnections() { func setupContainersConfWithSystemConnections() {
// make sure connections are not written to real user config on host // make sure connections are not written to real user config on host
file := filepath.Join(podmanTest.TempDir, "containersconf") file := filepath.Join(podmanTest.TempDir, "containers.conf")
f, err := os.Create(file) f, err := os.Create(file)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
connections := `
[engine]
active_service = "QA"
[engine.service_destinations]
[engine.service_destinations.QA]
uri = "ssh://root@podman.test:2222/run/podman/podman.sock"
[engine.service_destinations.QB]
uri = "ssh://root@podman.test:3333/run/podman/podman.sock"`
_, err = f.WriteString(connections)
Expect(err).ToNot(HaveOccurred())
f.Close() f.Close()
os.Setenv("CONTAINERS_CONF", file) os.Setenv("CONTAINERS_CONF", file)
file = filepath.Join(podmanTest.TempDir, "connections.conf")
f, err = os.Create(file)
Expect(err).ToNot(HaveOccurred())
connections := `{"connection":{"default":"QA","connections":{"QA":{"uri":"ssh://root@podman.test:2222/run/podman/podman.sock"},"QB":{"uri":"ssh://root@podman.test:3333/run/podman/podman.sock"}}}}`
_, err = f.WriteString(connections)
Expect(err).ToNot(HaveOccurred())
f.Close()
os.Setenv("PODMAN_CONNECTIONS_CONF", file)
} }
var _ = Describe("podman farm", func() { var _ = Describe("podman farm", func() {
@ -36,19 +34,12 @@ var _ = Describe("podman farm", func() {
Context("without running API service", func() { Context("without running API service", func() {
It("verify system connections exist", func() { It("verify system connections exist", func() {
cfg, err := config.ReadCustomConfig() session := podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg).Should(HaveActiveService("QA")) Expect(session).Should(ExitCleanly())
Expect(cfg).Should(VerifyService( Expect(string(session.Out.Contents())).To(Equal(`QA ssh://root@podman.test:2222/run/podman/podman.sock true true
"QA", QB ssh://root@podman.test:3333/run/podman/podman.sock false true
"ssh://root@podman.test:2222/run/podman/podman.sock", `))
"",
))
Expect(cfg).Should(VerifyService(
"QB",
"ssh://root@podman.test:3333/run/podman/podman.sock",
"",
))
}) })
It("create farm", func() { It("create farm", func() {
@ -73,12 +64,13 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm3\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm3\" created"))
cfg, err := config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm1")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"})) Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) farm2 [QA] false true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm3", []string{})) farm3 [] false true
`))
// create existing farm should exit with error // create existing farm should exit with error
cmd = []string{"farm", "create", "farm3"} cmd = []string{"farm", "create", "farm3"}
@ -109,12 +101,13 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm3\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm3\" created"))
cfg, err := config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm1")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"})) Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) farm2 [QA] false true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm3", []string{})) farm3 [] false true
`))
// update farm1 to remove the QA connection from it // update farm1 to remove the QA connection from it
cmd = []string{"farm", "update", "--remove", "QA,QB", "farm1"} cmd = []string{"farm", "update", "--remove", "QA,QB", "farm1"}
@ -137,12 +130,13 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" updated")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" updated"))
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm2")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{})) Expect(string(session.Out.Contents())).To(Equal(`farm1 [] false true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) farm2 [QA] true true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm3", []string{"QB"})) farm3 [QB] false true
`))
// update farm2 to not be the default, no farms should be the default // update farm2 to not be the default, no farms should be the default
cmd = []string{"farm", "update", "--default=false", "farm2"} cmd = []string{"farm", "update", "--default=false", "farm2"}
@ -151,9 +145,13 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" updated")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" updated"))
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(BeEmpty()) Expect(session).Should(ExitCleanly())
Expect(string(session.Out.Contents())).To(Equal(`farm1 [] false true
farm2 [QA] false true
farm3 [QB] false true
`))
}) })
It("update farm with non-existing connections", func() { It("update farm with non-existing connections", func() {
@ -171,11 +169,12 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" created"))
cfg, err := config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm1")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"})) Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) farm2 [QA] false true
`))
// update farm1 to add no-node connection to it // update farm1 to add no-node connection to it
cmd = []string{"farm", "update", "--add", "no-node", "farm1"} cmd = []string{"farm", "update", "--add", "no-node", "farm1"}
@ -189,12 +188,13 @@ var _ = Describe("podman farm", func() {
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(ExitWithError()) Expect(session).Should(ExitWithError())
// read config again to ensure that nothing has changed // check again to ensure that nothing has changed
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm1")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"})) Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) farm2 [QA] false true
`))
}) })
It("update non-existent farm", func() { It("update non-existent farm", func() {
@ -205,11 +205,6 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm1\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm1\" created"))
cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Farms.Default).Should(Equal("farm1"))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"}))
// update non-existent farm to add QA connection to it // update non-existent farm to add QA connection to it
cmd = []string{"farm", "update", "--add", "no-node", "non-existent"} cmd = []string{"farm", "update", "--add", "no-node", "non-existent"}
session = podmanTest.Podman(cmd) session = podmanTest.Podman(cmd)
@ -222,11 +217,10 @@ var _ = Describe("podman farm", func() {
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(ExitWithError()) Expect(session).Should(ExitWithError())
// read config again and ensure nothing has changed session = podmanTest.Podman(farmListCmd)
cfg, err = config.ReadCustomConfig() session.WaitWithDefaultTimeout()
Expect(err).ShouldNot(HaveOccurred()) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.Default).Should(Equal("farm1")) Expect(session.OutputToString()).To(Equal(`farm1 [QA QB] true true`))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"}))
}) })
It("remove farms", func() { It("remove farms", func() {
@ -244,11 +238,12 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" created"))
cfg, err := config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm1")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"})) Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) farm2 [QA] false true
`))
// remove farm1 and a non-existent farm // remove farm1 and a non-existent farm
// farm 1 should be removed and a warning printed for nonexistent-farm // farm 1 should be removed and a warning printed for nonexistent-farm
@ -259,11 +254,10 @@ var _ = Describe("podman farm", func() {
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm1\" deleted")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm1\" deleted"))
Expect(session.ErrorToString()).Should(ContainSubstring("doesn't exist; nothing to remove")) Expect(session.ErrorToString()).Should(ContainSubstring("doesn't exist; nothing to remove"))
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(Equal("farm2")) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) Expect(session.OutputToString()).To(Equal(`farm2 [QA] true true`))
Expect(cfg.Farms.List).Should(Not(HaveKey("farm1")))
// remove all non-existent farms and expect an error // remove all non-existent farms and expect an error
cmd = []string{"farm", "rm", "foo", "bar"} cmd = []string{"farm", "rm", "foo", "bar"}
@ -287,12 +281,6 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" created"))
cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Farms.Default).Should(Equal("farm1"))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA", "QB"}))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"}))
// remove --all // remove --all
cmd = []string{"farm", "rm", "--all"} cmd = []string{"farm", "rm", "--all"}
session = podmanTest.Podman(cmd) session = podmanTest.Podman(cmd)
@ -300,10 +288,10 @@ var _ = Describe("podman farm", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("All farms have been deleted")) Expect(session.Out.Contents()).Should(ContainSubstring("All farms have been deleted"))
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(farmListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Farms.Default).Should(BeEmpty()) Expect(session).Should(ExitCleanly())
Expect(cfg.Farms.List).Should(BeEmpty()) Expect(session.OutputToString()).To(Equal(""))
}) })
}) })
}) })

View File

@ -4,7 +4,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/containers/common/pkg/config"
. "github.com/containers/podman/v4/test/utils" . "github.com/containers/podman/v4/test/utils"
"github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/homedir"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
@ -13,7 +12,7 @@ import (
var _ = Describe("podman image scp", func() { var _ = Describe("podman image scp", func() {
BeforeEach(setupEmptyContainersConf) BeforeEach(setupConnectionsConf)
It("podman image scp bogus image", func() { It("podman image scp bogus image", func() {
scp := podmanTest.Podman([]string{"image", "scp", "FOOBAR"}) scp := podmanTest.Podman([]string{"image", "scp", "FOOBAR"})
@ -34,15 +33,6 @@ var _ = Describe("podman image scp", func() {
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Engine).Should(HaveField("ActiveService", "QA"))
Expect(cfg.Engine.ServiceDestinations).To(HaveKeyWithValue("QA",
config.Destination{
URI: "ssh://root@podman.test:2222/run/podman/podman.sock",
},
))
scp := podmanTest.Podman([]string{"image", "scp", ALPINE, "QA::"}) scp := podmanTest.Podman([]string{"image", "scp", ALPINE, "QA::"})
scp.WaitWithDefaultTimeout() scp.WaitWithDefaultTimeout()
// exit with error because we cannot make an actual ssh connection // exit with error because we cannot make an actual ssh connection

View File

@ -8,7 +8,6 @@ import (
"os/user" "os/user"
"path/filepath" "path/filepath"
"github.com/containers/common/pkg/config"
. "github.com/containers/podman/v4/test/utils" . "github.com/containers/podman/v4/test/utils"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -16,18 +15,24 @@ import (
. "github.com/onsi/gomega/gexec" . "github.com/onsi/gomega/gexec"
) )
func setupEmptyContainersConf() { func setupConnectionsConf() {
// make sure connections are not written to real user config on host // make sure connections are not written to real user config on host
file := filepath.Join(podmanTest.TempDir, "containersconf") file := filepath.Join(podmanTest.TempDir, "containers.conf")
f, err := os.Create(file) f, err := os.Create(file)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
f.Close() f.Close()
os.Setenv("CONTAINERS_CONF", file) os.Setenv("CONTAINERS_CONF", file)
file = filepath.Join(podmanTest.TempDir, "connections.conf")
os.Setenv("PODMAN_CONNECTIONS_CONF", file)
} }
var systemConnectionListCmd = []string{"system", "connection", "ls", "--format", "{{.Name}} {{.URI}} {{.Identity}} {{.Default}} {{.ReadWrite}}"}
var farmListCmd = []string{"farm", "ls", "--format", "{{.Name}} {{.Connections}} {{.Default}} {{.ReadWrite}}"}
var _ = Describe("podman system connection", func() { var _ = Describe("podman system connection", func() {
BeforeEach(setupEmptyContainersConf) BeforeEach(setupConnectionsConf)
Context("without running API service", func() { Context("without running API service", func() {
It("add ssh://", func() { It("add ssh://", func() {
@ -42,14 +47,10 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
cfg, err := config.ReadCustomConfig() session = podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg).Should(HaveActiveService("QA")) Expect(session).Should(ExitCleanly())
Expect(cfg).Should(VerifyService( Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true"))
"QA",
"ssh://root@podman.test:2222/run/podman/podman.sock",
"~/.ssh/id_rsa",
))
cmd = []string{"system", "connection", "rename", cmd = []string{"system", "connection", "rename",
"QA", "QA",
@ -59,7 +60,10 @@ var _ = Describe("podman system connection", func() {
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(config.ReadCustomConfig()).Should(HaveActiveService("QE")) session = podmanTest.Podman(systemConnectionListCmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.OutputToString()).To(Equal("QE ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true"))
}) })
It("add UDS", func() { It("add UDS", func() {
@ -74,11 +78,10 @@ var _ = Describe("podman system connection", func() {
// stderr will probably warn (ENOENT or EACCESS) about socket // stderr will probably warn (ENOENT or EACCESS) about socket
// but it's too unreliable to test for. // but it's too unreliable to test for.
Expect(config.ReadCustomConfig()).Should(VerifyService( session = podmanTest.Podman(systemConnectionListCmd)
"QA-UDS", session.WaitWithDefaultTimeout()
"unix:///run/podman/podman.sock", Expect(session).Should(ExitCleanly())
"", Expect(session.OutputToString()).To(Equal("QA-UDS unix:///run/podman/podman.sock true true"))
))
cmd = []string{"system", "connection", "add", cmd = []string{"system", "connection", "add",
"QA-UDS1", "QA-UDS1",
@ -90,12 +93,12 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(Exit(0)) Expect(session).Should(Exit(0))
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA-UDS")) session = podmanTest.Podman(systemConnectionListCmd)
Expect(config.ReadCustomConfig()).Should(VerifyService( session.WaitWithDefaultTimeout()
"QA-UDS1", Expect(session).Should(ExitCleanly())
"unix:///run/user/podman/podman.sock", Expect(string(session.Out.Contents())).To(Equal(`QA-UDS unix:///run/podman/podman.sock true true
"", QA-UDS1 unix:///run/user/podman/podman.sock false true
)) `))
}) })
It("add tcp", func() { It("add tcp", func() {
@ -108,18 +111,13 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
Expect(config.ReadCustomConfig()).Should(VerifyService( session = podmanTest.Podman(systemConnectionListCmd)
"QA-TCP", session.WaitWithDefaultTimeout()
"tcp://localhost:8888", Expect(session).Should(ExitCleanly())
"", Expect(session.OutputToString()).To(Equal("QA-TCP tcp://localhost:8888 true true"))
))
}) })
It("add to new farm", func() { It("add to new farm", func() {
cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Farms.List).Should(BeEmpty())
cmd := []string{"system", "connection", "add", cmd := []string{"system", "connection", "add",
"--default", "--default",
"--identity", "~/.ssh/id_rsa", "--identity", "~/.ssh/id_rsa",
@ -132,15 +130,14 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg).Should(HaveActiveService("QA")) Expect(session).Should(ExitCleanly())
Expect(cfg).Should(VerifyService( Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true"))
"QA", session = podmanTest.Podman(farmListCmd)
"ssh://root@podman.test:2222/run/podman/podman.sock", session.WaitWithDefaultTimeout()
"~/.ssh/id_rsa", Expect(session).Should(ExitCleanly())
)) Expect(session.OutputToString()).To(Equal("farm1 [QA] true true"))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA"}))
}) })
It("add to existing farm", func() { It("add to existing farm", func() {
@ -151,10 +148,6 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"empty-farm\" created")) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"empty-farm\" created"))
cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Farms.List).Should(HaveKeyWithValue("empty-farm", []string{}))
cmd = []string{"system", "connection", "add", cmd = []string{"system", "connection", "add",
"--default", "--default",
"--identity", "~/.ssh/id_rsa", "--identity", "~/.ssh/id_rsa",
@ -167,22 +160,17 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg).Should(HaveActiveService("QA")) Expect(session).Should(ExitCleanly())
Expect(cfg).Should(VerifyService( Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true"))
"QA", session = podmanTest.Podman(farmListCmd)
"ssh://root@podman.test:2222/run/podman/podman.sock", session.WaitWithDefaultTimeout()
"~/.ssh/id_rsa", Expect(session).Should(ExitCleanly())
)) Expect(session.OutputToString()).To(Equal("empty-farm [QA] true true"))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("empty-farm", []string{"QA"}))
}) })
It("removing connection should remove from farm also", func() { It("removing connection should remove from farm also", func() {
cfg, err := config.ReadCustomConfig()
Expect(err).ShouldNot(HaveOccurred())
Expect(cfg.Farms.List).Should(BeEmpty())
cmd := []string{"system", "connection", "add", cmd := []string{"system", "connection", "add",
"--default", "--default",
"--identity", "~/.ssh/id_rsa", "--identity", "~/.ssh/id_rsa",
@ -195,15 +183,14 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg).Should(HaveActiveService("QA")) Expect(session).Should(ExitCleanly())
Expect(cfg).Should(VerifyService( Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true"))
"QA", session = podmanTest.Podman(farmListCmd)
"ssh://root@podman.test:2222/run/podman/podman.sock", session.WaitWithDefaultTimeout()
"~/.ssh/id_rsa", Expect(session).Should(ExitCleanly())
)) Expect(session.OutputToString()).To(Equal("farm1 [QA] true true"))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA"}))
// Remove the QA connection // Remove the QA connection
session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"}) session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"})
@ -211,11 +198,14 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
cfg, err = config.ReadCustomConfig() session = podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Engine.ActiveService).Should(BeEmpty()) Expect(session).Should(ExitCleanly())
Expect(cfg.Engine.ServiceDestinations).Should(BeEmpty()) Expect(session.OutputToString()).To(Equal(""))
Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{})) session = podmanTest.Podman(farmListCmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.OutputToString()).To(Equal("farm1 [] true true"))
}) })
It("remove", func() { It("remove", func() {
@ -235,10 +225,10 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
cfg, err := config.ReadCustomConfig() session = podmanTest.Podman(systemConnectionListCmd)
Expect(err).ShouldNot(HaveOccurred()) session.WaitWithDefaultTimeout()
Expect(cfg.Engine.ActiveService).Should(BeEmpty()) Expect(session).Should(ExitCleanly())
Expect(cfg.Engine.ServiceDestinations).Should(BeEmpty()) Expect(session.OutputToString()).To(Equal(""))
} }
}) })
@ -261,6 +251,7 @@ var _ = Describe("podman system connection", func() {
session = podmanTest.Podman([]string{"system", "connection", "list"}) session = podmanTest.Podman([]string{"system", "connection", "list"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.OutputToStringArray()).To(HaveLen(1))
}) })
It("default", func() { It("default", func() {
@ -282,19 +273,18 @@ var _ = Describe("podman system connection", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out.Contents()).Should(BeEmpty()) Expect(session.Out.Contents()).Should(BeEmpty())
Expect(config.ReadCustomConfig()).Should(HaveActiveService("devl")) session = podmanTest.Podman(systemConnectionListCmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(string(session.Out.Contents())).To(Equal(`devl ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true
qe ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa false true
`))
cmd = []string{"system", "connection", "list"} cmd = []string{"system", "connection", "list"}
session = podmanTest.Podman(cmd) session = podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
Expect(session.Out).Should(Say("Name *URI *Identity *Default")) Expect(session.Out).Should(Say("Name *URI *Identity *Default"))
cmd = []string{"system", "connection", "list", "--format", "{{.Name}}"}
session = podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.OutputToString()).Should(Equal("devl qe"))
}) })
It("failed default", func() { It("failed default", func() {
@ -382,12 +372,12 @@ var _ = Describe("podman system connection", func() {
Path: fmt.Sprintf("/run/user/%s/podman/podman.sock", u.Uid), Path: fmt.Sprintf("/run/user/%s/podman/podman.sock", u.Uid),
} }
Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA")) cmd = exec.Command(podmanTest.RemotePodmanBinary, systemConnectionListCmd...)
Expect(config.ReadCustomConfig()).Should(VerifyService( lsSession, err := Start(cmd, GinkgoWriter, GinkgoWriter)
"QA", Expect(err).ToNot(HaveOccurred())
uri.String(), lsSession.Wait(DefaultWaitTimeout)
filepath.Join(u.HomeDir, ".ssh", "id_ed25519"), Expect(lsSession).Should(Exit(0))
)) Expect(string(lsSession.Out.Contents())).To(Equal("QA " + uri.String() + " " + filepath.Join(u.HomeDir, ".ssh", "id_ed25519") + " true true\n"))
}) })
}) })
}) })

View File

@ -51,7 +51,7 @@ function _run_podman_remote() {
# Very basic test, does not actually connect at any time # Very basic test, does not actually connect at any time
@test "podman system connection - basic add / ls / remove" { @test "podman system connection - basic add / ls / remove" {
run_podman system connection ls run_podman system connection ls
is "$output" "Name URI Identity Default" \ is "$output" "Name URI Identity Default ReadWrite" \
"system connection ls: no connections" "system connection ls: no connections"
c1="c1_$(random_string 15)" c1="c1_$(random_string 15)"
@ -61,8 +61,8 @@ function _run_podman_remote() {
run_podman context create --docker "host=tcp://localhost:54321" $c2 run_podman context create --docker "host=tcp://localhost:54321" $c2
run_podman system connection ls run_podman system connection ls
is "$output" \ is "$output" \
".*$c1[ ]\+tcp://localhost:12345[ ]\+true ".*$c1[ ]\+tcp://localhost:12345[ ]\+true[ ]\+true
$c2[ ]\+tcp://localhost:54321[ ]\+false" \ $c2[ ]\+tcp://localhost:54321[ ]\+false[ ]\+true" \
"system connection ls" "system connection ls"
run_podman system connection ls -q run_podman system connection ls -q
is "$(echo $(sort <<<$output))" \ is "$(echo $(sort <<<$output))" \
@ -75,14 +75,14 @@ $c2[ ]\+tcp://localhost:54321[ ]\+false" \
run_podman context use $c2 run_podman context use $c2
run_podman system connection ls run_podman system connection ls
is "$output" \ is "$output" \
".*$c1[ ]\+tcp://localhost:12345[ ]\+false ".*$c1[ ]\+tcp://localhost:12345[ ]\+false[ ]\+true
$c2[ ]\+tcp://localhost:54321[ ]\+true" \ $c2[ ]\+tcp://localhost:54321[ ]\+true[ ]\+true" \
"system connection ls" "system connection ls"
# Remove default connection; the remaining one should still not be default # Remove default connection; the remaining one should still not be default
run_podman system connection rm $c2 run_podman system connection rm $c2
run_podman context ls run_podman context ls
is "$output" ".*$c1[ ]\+tcp://localhost:12345[ ]\+false" \ is "$output" ".*$c1[ ]\+tcp://localhost:12345[ ]\+false[ ]\+true" \
"system connection ls (after removing default connection)" "system connection ls (after removing default connection)"
run_podman context rm $c1 run_podman context rm $c1

View File

@ -3,116 +3,12 @@ package utils
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"github.com/containers/common/pkg/config"
. "github.com/onsi/gomega" //nolint:revive,stylecheck
"github.com/onsi/gomega/format" "github.com/onsi/gomega/format"
"github.com/onsi/gomega/gexec" "github.com/onsi/gomega/gexec"
"github.com/onsi/gomega/matchers"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
// HaveActiveService verifies the given service is the active service.
func HaveActiveService(name interface{}) OmegaMatcher {
return WithTransform(
func(cfg *config.Config) string {
return cfg.Engine.ActiveService
},
Equal(name))
}
type ServiceMatcher struct {
types.GomegaMatcher
Name interface{}
URI interface{}
Identity interface{}
failureMessage string
negatedFailureMessage string
}
func VerifyService(name, uri, identity interface{}) OmegaMatcher {
return &ServiceMatcher{
Name: name,
URI: uri,
Identity: identity,
}
}
func (matcher *ServiceMatcher) Match(actual interface{}) (success bool, err error) {
cfg, ok := actual.(*config.Config)
if !ok {
return false, fmt.Errorf("ServiceMatcher matcher expects a config.Config")
}
if _, err = url.Parse(matcher.URI.(string)); err != nil {
return false, err
}
success, err = HaveKey(matcher.Name).Match(cfg.Engine.ServiceDestinations)
if !success || err != nil {
matcher.failureMessage = HaveKey(matcher.Name).FailureMessage(cfg.Engine.ServiceDestinations)
matcher.negatedFailureMessage = HaveKey(matcher.Name).NegatedFailureMessage(cfg.Engine.ServiceDestinations)
return
}
sd := cfg.Engine.ServiceDestinations[matcher.Name.(string)]
success, err = Equal(matcher.URI).Match(sd.URI)
if !success || err != nil {
matcher.failureMessage = Equal(matcher.URI).FailureMessage(sd.URI)
matcher.negatedFailureMessage = Equal(matcher.URI).NegatedFailureMessage(sd.URI)
return
}
success, err = Equal(matcher.Identity).Match(sd.Identity)
if !success || err != nil {
matcher.failureMessage = Equal(matcher.Identity).FailureMessage(sd.Identity)
matcher.negatedFailureMessage = Equal(matcher.Identity).NegatedFailureMessage(sd.Identity)
return
}
return true, nil
}
func (matcher *ServiceMatcher) FailureMessage(_ interface{}) string {
return matcher.failureMessage
}
func (matcher *ServiceMatcher) NegatedFailureMessage(_ interface{}) string {
return matcher.negatedFailureMessage
}
type URLMatcher struct {
matchers.EqualMatcher
}
// VerifyURL matches when actual is a valid URL and matches expected.
func VerifyURL(uri interface{}) OmegaMatcher {
return &URLMatcher{matchers.EqualMatcher{Expected: uri}}
}
func (matcher *URLMatcher) Match(actual interface{}) (bool, error) {
e, ok := matcher.Expected.(string)
if !ok {
return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", matcher.Expected)
}
eURI, err := url.Parse(e)
if err != nil {
return false, err
}
a, ok := actual.(string)
if !ok {
return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", actual)
}
aURI, err := url.Parse(a)
if err != nil {
return false, err
}
return (&matchers.EqualMatcher{Expected: eURI}).Match(aURI)
}
type ExitMatcher struct { type ExitMatcher struct {
types.GomegaMatcher types.GomegaMatcher
Expected int Expected int

View File

@ -9,11 +9,9 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/BurntSushi/toml"
"github.com/containers/common/internal/attributedstring" "github.com/containers/common/internal/attributedstring"
"github.com/containers/common/libnetwork/types" "github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/capabilities" "github.com/containers/common/pkg/capabilities"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/unshare" "github.com/containers/storage/pkg/unshare"
units "github.com/docker/go-units" units "github.com/docker/go-units"
selinux "github.com/opencontainers/selinux/go-selinux" selinux "github.com/opencontainers/selinux/go-selinux"
@ -667,9 +665,9 @@ type MachineConfig struct {
// FarmConfig represents the "farm" TOML config tables // FarmConfig represents the "farm" TOML config tables
type FarmConfig struct { type FarmConfig struct {
// Default is the default farm to be used when farming out builds // Default is the default farm to be used when farming out builds
Default string `toml:"default,omitempty"` Default string `json:",omitempty" toml:"default,omitempty"`
// List is a map of farms created where key=farm-name and value=list of connections // List is a map of farms created where key=farm-name and value=list of connections
List map[string][]string `toml:"list,omitempty"` List map[string][]string `json:",omitempty" toml:"list,omitempty"`
} }
// Destination represents destination for remote service // Destination represents destination for remote service
@ -678,10 +676,10 @@ type Destination struct {
URI string `toml:"uri"` URI string `toml:"uri"`
// Identity file with ssh key, optional // Identity file with ssh key, optional
Identity string `toml:"identity,omitempty"` Identity string `json:",omitempty" toml:"identity,omitempty"`
// isMachine describes if the remote destination is a machine. // isMachine describes if the remote destination is a machine.
IsMachine bool `toml:"is_machine,omitempty"` IsMachine bool `json:",omitempty" toml:"is_machine,omitempty"`
} }
// Consumes container image's os and arch and returns if any dedicated runtime was // Consumes container image's os and arch and returns if any dedicated runtime was
@ -1008,82 +1006,6 @@ func IsValidDeviceMode(mode string) bool {
return true return true
} }
func rootlessConfigPath() (string, error) {
if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
return filepath.Join(configHome, _configPath), nil
}
home, err := unshare.HomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, UserOverrideContainersConfig), nil
}
func Path() string {
if path := os.Getenv("CONTAINERS_CONF"); path != "" {
return path
}
if unshare.IsRootless() {
if rpath, err := rootlessConfigPath(); err == nil {
return rpath
}
return "$HOME/" + UserOverrideContainersConfig
}
return OverrideContainersConfig
}
// ReadCustomConfig reads the custom config and only generates a config based on it
// If the custom config file does not exists, function will return an empty config
func ReadCustomConfig() (*Config, error) {
path, err := customConfigFile()
if err != nil {
return nil, err
}
newConfig := &Config{}
if _, err := os.Stat(path); err == nil {
if err := readConfigFromFile(path, newConfig); err != nil {
return nil, err
}
} else {
if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
}
// Let's always initialize the farm list so it is never nil
if newConfig.Farms.List == nil {
newConfig.Farms.List = make(map[string][]string)
}
return newConfig, nil
}
// Write writes the configuration to the default file
func (c *Config) Write() error {
var err error
path, err := customConfigFile()
if err != nil {
return err
}
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()
enc := toml.NewEncoder(configFile)
if err := enc.Encode(c); err != nil {
return err
}
// If no errors commit the changes to the config file
return configFile.Commit()
}
// Reload clean the cached config and reloads the configuration from containers.conf files // Reload clean the cached config and reloads the configuration from containers.conf files
// This function is meant to be used for long-running processes that need to reload potential changes made to // This function is meant to be used for long-running processes that need to reload potential changes made to
// the cached containers.conf files. // the cached containers.conf files.

View File

@ -1,9 +1,5 @@
package config package config
import (
"os"
)
const ( const (
// OverrideContainersConfig holds the default config path overridden by the root user // OverrideContainersConfig holds the default config path overridden by the root user
OverrideContainersConfig = "/etc/" + _configPath OverrideContainersConfig = "/etc/" + _configPath
@ -16,18 +12,6 @@ const (
DefaultSignaturePolicyPath = "/etc/containers/policy.json" DefaultSignaturePolicyPath = "/etc/containers/policy.json"
) )
// podman remote clients on darwin cannot use unshare.isRootless() to determine the configuration file locations.
func customConfigFile() (string, error) {
if path, found := os.LookupEnv("CONTAINERS_CONF"); found {
return path, nil
}
return rootlessConfigPath()
}
func ifRootlessConfigPath() (string, error) {
return rootlessConfigPath()
}
var defaultHelperBinariesDir = []string{ var defaultHelperBinariesDir = []string{
// Relative to the binary directory // Relative to the binary directory
"$BINDIR/../libexec/podman", "$BINDIR/../libexec/podman",

View File

@ -1,9 +1,5 @@
package config package config
import (
"os"
)
const ( const (
// OverrideContainersConfig holds the default config path overridden by the root user // OverrideContainersConfig holds the default config path overridden by the root user
OverrideContainersConfig = "/usr/local/etc/" + _configPath OverrideContainersConfig = "/usr/local/etc/" + _configPath
@ -16,18 +12,6 @@ const (
DefaultSignaturePolicyPath = "/usr/local/etc/containers/policy.json" DefaultSignaturePolicyPath = "/usr/local/etc/containers/policy.json"
) )
// podman remote clients on freebsd cannot use unshare.isRootless() to determine the configuration file locations.
func customConfigFile() (string, error) {
if path, found := os.LookupEnv("CONTAINERS_CONF"); found {
return path, nil
}
return rootlessConfigPath()
}
func ifRootlessConfigPath() (string, error) {
return rootlessConfigPath()
}
var defaultHelperBinariesDir = []string{ var defaultHelperBinariesDir = []string{
"/usr/local/bin", "/usr/local/bin",
"/usr/local/libexec/podman", "/usr/local/libexec/podman",

View File

@ -1,9 +1,6 @@
package config package config
import ( import (
"os"
"github.com/containers/storage/pkg/unshare"
selinux "github.com/opencontainers/selinux/go-selinux" selinux "github.com/opencontainers/selinux/go-selinux"
) )
@ -23,31 +20,6 @@ func selinuxEnabled() bool {
return selinux.GetEnabled() return selinux.GetEnabled()
} }
func customConfigFile() (string, error) {
if path, found := os.LookupEnv("CONTAINERS_CONF"); found {
return path, nil
}
if unshare.GetRootlessUID() > 0 {
path, err := rootlessConfigPath()
if err != nil {
return "", err
}
return path, nil
}
return OverrideContainersConfig, nil
}
func ifRootlessConfigPath() (string, error) {
if unshare.GetRootlessUID() > 0 {
path, err := rootlessConfigPath()
if err != nil {
return "", err
}
return path, nil
}
return "", nil
}
var defaultHelperBinariesDir = []string{ var defaultHelperBinariesDir = []string{
"/usr/local/libexec/podman", "/usr/local/libexec/podman",
"/usr/local/lib/podman", "/usr/local/lib/podman",

View File

@ -0,0 +1,25 @@
//go:build !windows
package config
import (
"os"
"path/filepath"
"github.com/containers/storage/pkg/unshare"
)
// userConfigPath returns the path to the users local config that is
// not shared with other users. It uses $XDG_CONFIG_HOME/containers...
// if set or $HOME/.config/containers... if not.
func userConfigPath() (string, error) {
if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
return filepath.Join(configHome, _configPath), nil
}
home, err := unshare.HomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, UserOverrideContainersConfig), nil
}

View File

@ -17,15 +17,9 @@ const (
_typeBind = "bind" _typeBind = "bind"
) )
// podman remote clients on windows cannot use unshare.isRootless() to determine the configuration file locations. // userConfigPath returns the path to the users local config that is
func customConfigFile() (string, error) { // not shared with other users. It uses $APPDATA/containers...
if path, found := os.LookupEnv("CONTAINERS_CONF"); found { func userConfigPath() (string, error) {
return path, nil
}
return os.Getenv("APPDATA") + "\\containers\\containers.conf", nil
}
func ifRootlessConfigPath() (string, error) {
return os.Getenv("APPDATA") + "\\containers\\containers.conf", nil return os.Getenv("APPDATA") + "\\containers\\containers.conf", nil
} }

View File

@ -0,0 +1,286 @@
package config
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/containers/storage/pkg/ioutils"
)
const connectionsFile = "podman-connections.conf"
// 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() (*ConnectionsFile, string, error) {
path, err := connectionsConfigFile()
if err != nil {
return nil, "", err
}
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, path, 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, path, 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 {
conf, path, err := readConnectionConf()
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) {
conConf, _, err := readConnectionConf()
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) {
conConf, _, err := readConnectionConf()
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) {
conConf, _, err := readConnectionConf()
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) {
conConf, _, err := readConnectionConf()
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
}

View File

@ -10,7 +10,8 @@
# locations in the following order: # locations in the following order:
# 1. /usr/share/containers/containers.conf # 1. /usr/share/containers/containers.conf
# 2. /etc/containers/containers.conf # 2. /etc/containers/containers.conf
# 3. $HOME/.config/containers/containers.conf (Rootless containers ONLY) # 3. $XDG_CONFIG_HOME/containers/containers.conf or
# $HOME/.config/containers/containers.conf if $XDG_CONFIG_HOME is not set
# Items specified in the latter containers.conf, if they exist, override the # Items specified in the latter containers.conf, if they exist, override the
# previous containers.conf settings, or the default settings. # previous containers.conf settings, or the default settings.

View File

@ -21,7 +21,6 @@ var (
) )
const ( const (
// FIXME: update code base and tests to use the two constants below.
containersConfEnv = "CONTAINERS_CONF" containersConfEnv = "CONTAINERS_CONF"
containersConfOverrideEnv = containersConfEnv + "_OVERRIDE" containersConfOverrideEnv = containersConfEnv + "_OVERRIDE"
) )
@ -79,15 +78,34 @@ func newLocked(options *Options) (*Config, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("finding config on system: %w", err) return nil, fmt.Errorf("finding config on system: %w", err)
} }
// connectionsPath, err := connectionsConfigFile()
// if err != nil {
// return nil, err
// }
for _, path := range configs { for _, path := range configs {
// var dests []*Destination
// if path == connectionsPath {
// // Store the dest pointers so we know after the load if there are new pointers
// // the connection changed and thus is read write.
// dests = maps.Values(config.Engine.ServiceDestinations)
// }
// Merge changes in later configs with the previous configs. // Merge changes in later configs with the previous configs.
// Each config file that specified fields, will override the // Each config file that specified fields, will override the
// previous fields. // previous fields.
if err = readConfigFromFile(path, config); err != nil { if err = readConfigFromFile(path, config, true); err != nil {
return nil, fmt.Errorf("reading system config %q: %w", path, err) return nil, fmt.Errorf("reading system config %q: %w", path, err)
} }
logrus.Debugf("Merged system config %q", path) logrus.Debugf("Merged system config %q", path)
logrus.Tracef("%+v", config) logrus.Tracef("%+v", config)
// // if there is a new dest now we know it is read write as it was in the connections.conf file
// for _, dest := range config.Engine.ServiceDestinations {
// if !slices.Contains(dests, dest) {
// dest.ReadWrite = true
// }
// }
} }
modules, err := options.modules() modules, err := options.modules()
@ -115,7 +133,7 @@ func newLocked(options *Options) (*Config, error) {
} }
// readConfigFromFile reads in container config in the specified // readConfigFromFile reads in container config in the specified
// file and then merge changes with the current default. // file and then merge changes with the current default.
if err := readConfigFromFile(add, config); err != nil { if err := readConfigFromFile(add, config, false); err != nil {
return nil, fmt.Errorf("reading additional config %q: %w", add, err) return nil, fmt.Errorf("reading additional config %q: %w", add, err)
} }
logrus.Debugf("Merged additional config %q", add) logrus.Debugf("Merged additional config %q", add)
@ -157,12 +175,8 @@ func systemConfigs() (configs []string, finalErr error) {
} }
return append(configs, path), nil return append(configs, path), nil
} }
if _, err := os.Stat(DefaultContainersConfig); err == nil {
configs = append(configs, DefaultContainersConfig) configs = append(configs, DefaultContainersConfig)
}
if _, err := os.Stat(OverrideContainersConfig); err == nil {
configs = append(configs, OverrideContainersConfig) configs = append(configs, OverrideContainersConfig)
}
var err error var err error
configs, err = addConfigs(OverrideContainersConfig+".d", configs) configs, err = addConfigs(OverrideContainersConfig+".d", configs)
@ -170,19 +184,15 @@ func systemConfigs() (configs []string, finalErr error) {
return nil, err return nil, err
} }
path, err := ifRootlessConfigPath() path, err := userConfigPath()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if path != "" {
if _, err := os.Stat(path); err == nil {
configs = append(configs, path) configs = append(configs, path)
}
configs, err = addConfigs(path+".d", configs) configs, err = addConfigs(path+".d", configs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return configs, nil return configs, nil
} }
@ -225,10 +235,13 @@ func addConfigs(dirPath string, configs []string) ([]string, error) {
// unmarshal its content into a Config. The config param specifies the previous // unmarshal its content into a Config. The config param specifies the previous
// default config. If the path, only specifies a few fields in the Toml file // default config. If the path, only specifies a few fields in the Toml file
// the defaults from the config parameter will be used for all other fields. // the defaults from the config parameter will be used for all other fields.
func readConfigFromFile(path string, config *Config) error { func readConfigFromFile(path string, config *Config, ignoreErrNotExist bool) error {
logrus.Tracef("Reading configuration file %q", path) logrus.Tracef("Reading configuration file %q", path)
meta, err := toml.DecodeFile(path, config) meta, err := toml.DecodeFile(path, config)
if err != nil { if err != nil {
if ignoreErrNotExist && errors.Is(err, fs.ErrNotExist) {
return nil
}
return fmt.Errorf("decode configuration %v: %w", path, err) return fmt.Errorf("decode configuration %v: %w", path, err)
} }
keys := meta.Undecoded() keys := meta.Undecoded()

View File

@ -19,6 +19,7 @@ type List interface {
Remove(instanceDigest digest.Digest) error Remove(instanceDigest digest.Digest) error
SetURLs(instanceDigest digest.Digest, urls []string) error SetURLs(instanceDigest digest.Digest, urls []string) error
URLs(instanceDigest digest.Digest) ([]string, error) URLs(instanceDigest digest.Digest) ([]string, error)
ClearAnnotations(instanceDigest *digest.Digest) error
SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error
Annotations(instanceDigest *digest.Digest) (map[string]string, error) Annotations(instanceDigest *digest.Digest) (map[string]string, error)
SetOS(instanceDigest digest.Digest, os string) error SetOS(instanceDigest digest.Digest, os string) error
@ -100,18 +101,21 @@ func (l *list) AddInstance(manifestDigest digest.Digest, manifestSize int64, man
Platform: schema2platform, Platform: schema2platform,
}) })
ociv1platform := v1.Platform{ ociv1platform := &v1.Platform{
Architecture: architecture, Architecture: architecture,
OS: osName, OS: osName,
OSVersion: osVersion, OSVersion: osVersion,
OSFeatures: osFeatures, OSFeatures: osFeatures,
Variant: variant, Variant: variant,
} }
if ociv1platform.Architecture == "" && ociv1platform.OS == "" && ociv1platform.OSVersion == "" && ociv1platform.Variant == "" && len(ociv1platform.OSFeatures) == 0 {
ociv1platform = nil
}
l.oci.Manifests = append(l.oci.Manifests, v1.Descriptor{ l.oci.Manifests = append(l.oci.Manifests, v1.Descriptor{
MediaType: manifestType, MediaType: manifestType,
Size: manifestSize, Size: manifestSize,
Digest: manifestDigest, Digest: manifestDigest,
Platform: &ociv1platform, Platform: ociv1platform,
}) })
return nil return nil
@ -170,7 +174,13 @@ func (l *list) SetURLs(instanceDigest digest.Digest, urls []string) error {
return err return err
} }
oci.URLs = append([]string{}, urls...) oci.URLs = append([]string{}, urls...)
if len(oci.URLs) == 0 {
oci.URLs = nil
}
docker.URLs = append([]string{}, urls...) docker.URLs = append([]string{}, urls...)
if len(docker.URLs) == 0 {
docker.URLs = nil
}
return nil return nil
} }
@ -183,7 +193,24 @@ func (l *list) URLs(instanceDigest digest.Digest) ([]string, error) {
return append([]string{}, oci.URLs...), nil return append([]string{}, oci.URLs...), nil
} }
// SetAnnotations sets annotations on the image index, or on a specific manifest. // ClearAnnotations removes all annotations from the image index, or from a
// specific manifest.
// The field is specific to the OCI image index format, and is not present in Docker manifest lists.
func (l *list) ClearAnnotations(instanceDigest *digest.Digest) error {
a := &l.oci.Annotations
if instanceDigest != nil {
oci, err := l.findOCIv1(*instanceDigest)
if err != nil {
return err
}
a = &oci.Annotations
}
*a = nil
return nil
}
// SetAnnotations sets annotations on the image index, or on a specific
// manifest.
// The field is specific to the OCI image index format, and is not present in Docker manifest lists. // The field is specific to the OCI image index format, and is not present in Docker manifest lists.
func (l *list) SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error { func (l *list) SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error {
a := &l.oci.Annotations a := &l.oci.Annotations
@ -194,10 +221,15 @@ func (l *list) SetAnnotations(instanceDigest *digest.Digest, annotations map[str
} }
a = &oci.Annotations a = &oci.Annotations
} }
if *a == nil {
(*a) = make(map[string]string) (*a) = make(map[string]string)
}
for k, v := range annotations { for k, v := range annotations {
(*a)[k] = v (*a)[k] = v
} }
if len(*a) == 0 {
*a = nil
}
return nil return nil
} }
@ -230,7 +262,13 @@ func (l *list) SetOS(instanceDigest digest.Digest, os string) error {
return err return err
} }
docker.Platform.OS = os docker.Platform.OS = os
if oci.Platform == nil {
oci.Platform = &v1.Platform{}
}
oci.Platform.OS = os oci.Platform.OS = os
if oci.Platform.Architecture == "" && oci.Platform.OS == "" && oci.Platform.OSVersion == "" && oci.Platform.Variant == "" && len(oci.Platform.OSFeatures) == 0 {
oci.Platform = nil
}
return nil return nil
} }
@ -240,7 +278,11 @@ func (l *list) OS(instanceDigest digest.Digest) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return oci.Platform.OS, nil platform := oci.Platform
if platform == nil {
platform = &v1.Platform{}
}
return platform.OS, nil
} }
// SetArchitecture sets the Architecture field in the platform information associated with the instance with the specified digest. // SetArchitecture sets the Architecture field in the platform information associated with the instance with the specified digest.
@ -254,7 +296,13 @@ func (l *list) SetArchitecture(instanceDigest digest.Digest, arch string) error
return err return err
} }
docker.Platform.Architecture = arch docker.Platform.Architecture = arch
if oci.Platform == nil {
oci.Platform = &v1.Platform{}
}
oci.Platform.Architecture = arch oci.Platform.Architecture = arch
if oci.Platform.Architecture == "" && oci.Platform.OS == "" && oci.Platform.OSVersion == "" && oci.Platform.Variant == "" && len(oci.Platform.OSFeatures) == 0 {
oci.Platform = nil
}
return nil return nil
} }
@ -264,7 +312,11 @@ func (l *list) Architecture(instanceDigest digest.Digest) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return oci.Platform.Architecture, nil platform := oci.Platform
if platform == nil {
platform = &v1.Platform{}
}
return platform.Architecture, nil
} }
// SetOSVersion sets the OSVersion field in the platform information associated with the instance with the specified digest. // SetOSVersion sets the OSVersion field in the platform information associated with the instance with the specified digest.
@ -278,7 +330,13 @@ func (l *list) SetOSVersion(instanceDigest digest.Digest, osVersion string) erro
return err return err
} }
docker.Platform.OSVersion = osVersion docker.Platform.OSVersion = osVersion
if oci.Platform == nil {
oci.Platform = &v1.Platform{}
}
oci.Platform.OSVersion = osVersion oci.Platform.OSVersion = osVersion
if oci.Platform.Architecture == "" && oci.Platform.OS == "" && oci.Platform.OSVersion == "" && oci.Platform.Variant == "" && len(oci.Platform.OSFeatures) == 0 {
oci.Platform = nil
}
return nil return nil
} }
@ -288,7 +346,11 @@ func (l *list) OSVersion(instanceDigest digest.Digest) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return oci.Platform.OSVersion, nil platform := oci.Platform
if platform == nil {
platform = &v1.Platform{}
}
return platform.OSVersion, nil
} }
// SetVariant sets the Variant field in the platform information associated with the instance with the specified digest. // SetVariant sets the Variant field in the platform information associated with the instance with the specified digest.
@ -302,7 +364,13 @@ func (l *list) SetVariant(instanceDigest digest.Digest, variant string) error {
return err return err
} }
docker.Platform.Variant = variant docker.Platform.Variant = variant
if oci.Platform == nil {
oci.Platform = &v1.Platform{}
}
oci.Platform.Variant = variant oci.Platform.Variant = variant
if oci.Platform.Architecture == "" && oci.Platform.OS == "" && oci.Platform.OSVersion == "" && oci.Platform.Variant == "" && len(oci.Platform.OSFeatures) == 0 {
oci.Platform = nil
}
return nil return nil
} }
@ -312,7 +380,11 @@ func (l *list) Variant(instanceDigest digest.Digest) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return oci.Platform.Variant, nil platform := oci.Platform
if platform == nil {
platform = &v1.Platform{}
}
return platform.Variant, nil
} }
// SetFeatures sets the features list in the platform information associated with the instance with the specified digest. // SetFeatures sets the features list in the platform information associated with the instance with the specified digest.
@ -323,6 +395,9 @@ func (l *list) SetFeatures(instanceDigest digest.Digest, features []string) erro
return err return err
} }
docker.Platform.Features = append([]string{}, features...) docker.Platform.Features = append([]string{}, features...)
if len(docker.Platform.Features) == 0 {
docker.Platform.Features = nil
}
// no OCI equivalent // no OCI equivalent
return nil return nil
} }
@ -348,7 +423,16 @@ func (l *list) SetOSFeatures(instanceDigest digest.Digest, osFeatures []string)
return err return err
} }
docker.Platform.OSFeatures = append([]string{}, osFeatures...) docker.Platform.OSFeatures = append([]string{}, osFeatures...)
if oci.Platform == nil {
oci.Platform = &v1.Platform{}
}
oci.Platform.OSFeatures = append([]string{}, osFeatures...) oci.Platform.OSFeatures = append([]string{}, osFeatures...)
if len(oci.Platform.OSFeatures) == 0 {
oci.Platform.OSFeatures = nil
}
if oci.Platform.Architecture == "" && oci.Platform.OS == "" && oci.Platform.OSVersion == "" && oci.Platform.Variant == "" && len(oci.Platform.OSFeatures) == 0 {
oci.Platform = nil
}
return nil return nil
} }
@ -358,7 +442,11 @@ func (l *list) OSFeatures(instanceDigest digest.Digest) ([]string, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return append([]string{}, oci.Platform.OSFeatures...), nil platform := oci.Platform
if platform == nil {
platform = &v1.Platform{}
}
return append([]string{}, platform.OSFeatures...), nil
} }
// SetMediaType sets the MediaType field in the instance with the specified digest. // SetMediaType sets the MediaType field in the instance with the specified digest.

View File

@ -53,32 +53,32 @@ func golangConnectionCreate(options ConnectionCreateOptions) error {
dst.URI += uri.Path dst.URI += uri.Path
} }
cfg, err := config.ReadCustomConfig() // TODO this really should not live here, it must be in podman where we write the other connections as well.
if err != nil { // This duplicates the code for no reason and I have a really hard time to make any sense of why this code
return err // was added in the first place.
} return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if cfg.Engine.ServiceDestinations == nil { if cfg.Connection.Connections == nil {
cfg.Engine.ServiceDestinations = map[string]config.Destination{ cfg.Connection.Connections = map[string]config.Destination{
options.Name: *dst, options.Name: *dst,
} }
cfg.Engine.ActiveService = options.Name cfg.Connection.Default = options.Name
} else { } else {
cfg.Engine.ServiceDestinations[options.Name] = *dst cfg.Connection.Connections[options.Name] = *dst
} }
// Create or update an existing farm with the connection being added // Create or update an existing farm with the connection being added
if options.Farm != "" { if options.Farm != "" {
if len(cfg.Farms.List) == 0 { if len(cfg.Farm.List) == 0 {
cfg.Farms.Default = options.Farm cfg.Farm.Default = options.Farm
} }
if val, ok := cfg.Farms.List[options.Farm]; ok { if val, ok := cfg.Farm.List[options.Farm]; ok {
cfg.Farms.List[options.Farm] = append(val, options.Name) cfg.Farm.List[options.Farm] = append(val, options.Name)
} else { } else {
cfg.Farms.List[options.Farm] = []string{options.Name} cfg.Farm.List[options.Farm] = []string{options.Name}
} }
} }
return nil
return cfg.Write() })
} }
func golangConnectionDial(options ConnectionDialOptions) (*ConnectionDialReport, error) { func golangConnectionDial(options ConnectionDialOptions) (*ConnectionDialReport, error) {

View File

@ -72,24 +72,32 @@ func nativeConnectionCreate(options ConnectionCreateOptions) error {
return fmt.Errorf("remote podman %q failed to report its UDS socket", uri.Host) return fmt.Errorf("remote podman %q failed to report its UDS socket", uri.Host)
} }
cfg, err := config.ReadCustomConfig() // TODO this really should not live here, it must be in podman where we write the other connections as well.
if err != nil { // This duplicates the code for no reason and I have a really hard time to make any sense of why this code
return err // was added in the first place.
} return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
if options.Default { if cfg.Connection.Connections == nil {
cfg.Engine.ActiveService = options.Name cfg.Connection.Connections = map[string]config.Destination{
}
if cfg.Engine.ServiceDestinations == nil {
cfg.Engine.ServiceDestinations = map[string]config.Destination{
options.Name: *dst, options.Name: *dst,
} }
cfg.Engine.ActiveService = options.Name cfg.Connection.Default = options.Name
} else { } else {
cfg.Engine.ServiceDestinations[options.Name] = *dst cfg.Connection.Connections[options.Name] = *dst
} }
return cfg.Write() // Create or update an existing farm with the connection being added
if options.Farm != "" {
if len(cfg.Farm.List) == 0 {
cfg.Farm.Default = options.Farm
}
if val, ok := cfg.Farm.List[options.Farm]; ok {
cfg.Farm.List[options.Farm] = append(val, options.Name)
} else {
cfg.Farm.List[options.Farm] = []string{options.Name}
}
}
return nil
})
} }
func nativeConnectionExec(options ConnectionExecOptions) (*ConnectionExecReport, error) { func nativeConnectionExec(options ConnectionExecOptions) (*ConnectionExecReport, error) {

View File

@ -6,9 +6,9 @@ const (
// VersionMajor is for an API incompatible changes // VersionMajor is for an API incompatible changes
VersionMajor = 5 VersionMajor = 5
// VersionMinor is for functionality in a backwards-compatible manner // VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 29 VersionMinor = 30
// VersionPatch is for backwards-compatible bug fixes // VersionPatch is for backwards-compatible bug fixes
VersionPatch = 2 VersionPatch = 0
// VersionDev indicates development branch. Releases will be empty string. // VersionDev indicates development branch. Releases will be empty string.
VersionDev = "-dev" VersionDev = "-dev"

View File

@ -41,7 +41,7 @@ containers-storage: ## build using gc on the host
$(GO) build -compiler gc $(BUILDFLAGS) ./cmd/containers-storage $(GO) build -compiler gc $(BUILDFLAGS) ./cmd/containers-storage
codespell: codespell:
codespell -S Makefile,build,buildah,buildah.spec,imgtype,copy,AUTHORS,bin,vendor,.git,go.sum,CHANGELOG.md,changelog.txt,seccomp.json,.cirrus.yml,"*.xz,*.gz,*.tar,*.tgz,*ico,*.png,*.1,*.5,*.orig,*.rej" -L worl,flate,uint,iff,od,ERRO -w codespell -S Makefile,build,buildah,buildah.spec,imgtype,copy,AUTHORS,bin,vendor,.git,go.sum,CHANGELOG.md,changelog.txt,seccomp.json,.cirrus.yml,"*.xz,*.gz,*.tar,*.tgz,*ico,*.png,*.1,*.5,*.orig,*.rej" -L plack,worl,flate,uint,iff,od,ERRO -w
binary local-binary: containers-storage binary local-binary: containers-storage

View File

@ -73,11 +73,9 @@ type chunkedDiffer struct {
zstdReader *zstd.Decoder zstdReader *zstd.Decoder
rawReader io.Reader rawReader io.Reader
// contentDigest is the digest of the uncompressed content // tocDigest is the digest of the TOC document when the layer
// (diffID) when the layer is fully retrieved. If the layer // is partially pulled.
// is not fully retrieved, instead of using the digest of the tocDigest digest.Digest
// uncompressed content, it refers to the digest of the TOC.
contentDigest digest.Digest
// convertedToZstdChunked is set to true if the layer needs to // convertedToZstdChunked is set to true if the layer needs to
// be converted to the zstd:chunked format before it can be // be converted to the zstd:chunked format before it can be
@ -292,7 +290,7 @@ func makeZstdChunkedDiffer(ctx context.Context, store storage.Store, blobSize in
return nil, err return nil, err
} }
contentDigest, err := digest.Parse(annotations[internal.ManifestChecksumKey]) tocDigest, err := digest.Parse(annotations[internal.ManifestChecksumKey])
if err != nil { if err != nil {
return nil, fmt.Errorf("parse TOC digest %q: %w", annotations[internal.ManifestChecksumKey], err) return nil, fmt.Errorf("parse TOC digest %q: %w", annotations[internal.ManifestChecksumKey], err)
} }
@ -300,7 +298,7 @@ func makeZstdChunkedDiffer(ctx context.Context, store storage.Store, blobSize in
return &chunkedDiffer{ return &chunkedDiffer{
fsVerityDigests: make(map[string]string), fsVerityDigests: make(map[string]string),
blobSize: blobSize, blobSize: blobSize,
contentDigest: contentDigest, tocDigest: tocDigest,
copyBuffer: makeCopyBuffer(), copyBuffer: makeCopyBuffer(),
fileType: fileTypeZstdChunked, fileType: fileTypeZstdChunked,
layersCache: layersCache, layersCache: layersCache,
@ -322,7 +320,7 @@ func makeEstargzChunkedDiffer(ctx context.Context, store storage.Store, blobSize
return nil, err return nil, err
} }
contentDigest, err := digest.Parse(annotations[estargz.TOCJSONDigestAnnotation]) tocDigest, err := digest.Parse(annotations[estargz.TOCJSONDigestAnnotation])
if err != nil { if err != nil {
return nil, fmt.Errorf("parse TOC digest %q: %w", annotations[estargz.TOCJSONDigestAnnotation], err) return nil, fmt.Errorf("parse TOC digest %q: %w", annotations[estargz.TOCJSONDigestAnnotation], err)
} }
@ -330,7 +328,7 @@ func makeEstargzChunkedDiffer(ctx context.Context, store storage.Store, blobSize
return &chunkedDiffer{ return &chunkedDiffer{
fsVerityDigests: make(map[string]string), fsVerityDigests: make(map[string]string),
blobSize: blobSize, blobSize: blobSize,
contentDigest: contentDigest, tocDigest: tocDigest,
copyBuffer: makeCopyBuffer(), copyBuffer: makeCopyBuffer(),
fileType: fileTypeEstargz, fileType: fileTypeEstargz,
layersCache: layersCache, layersCache: layersCache,
@ -1613,6 +1611,9 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
// stream to use for reading the zstd:chunked or Estargz file. // stream to use for reading the zstd:chunked or Estargz file.
stream := c.stream stream := c.stream
var uncompressedDigest digest.Digest
tocDigest := c.tocDigest
if c.convertToZstdChunked { if c.convertToZstdChunked {
fd, err := unix.Open(dest, unix.O_TMPFILE|unix.O_RDWR|unix.O_CLOEXEC, 0o600) fd, err := unix.Open(dest, unix.O_TMPFILE|unix.O_RDWR|unix.O_CLOEXEC, 0o600)
if err != nil { if err != nil {
@ -1663,13 +1664,13 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
c.fileType = fileTypeZstdChunked c.fileType = fileTypeZstdChunked
c.manifest = manifest c.manifest = manifest
c.tarSplit = tarSplit c.tarSplit = tarSplit
// since we retrieved the whole file and it was validated, use the diffID instead of the TOC digest.
c.contentDigest = diffID
c.tocOffset = tocOffset c.tocOffset = tocOffset
// the file was generated by us and the digest for each file was already computed, no need to validate it again. // the file was generated by us and the digest for each file was already computed, no need to validate it again.
c.skipValidation = true c.skipValidation = true
// since we retrieved the whole file and it was validated, do not use the TOC digest, but set the uncompressed digest.
tocDigest = ""
uncompressedDigest = diffID
} }
lcd := chunkedLayerData{ lcd := chunkedLayerData{
@ -1698,7 +1699,8 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
Artifacts: map[string]interface{}{ Artifacts: map[string]interface{}{
tocKey: toc, tocKey: toc,
}, },
TOCDigest: c.contentDigest, TOCDigest: tocDigest,
UncompressedDigest: uncompressedDigest,
} }
if !parseBooleanPullOption(c.storeOpts, "enable_partial_images", false) { if !parseBooleanPullOption(c.storeOpts, "enable_partial_images", false) {

6
vendor/modules.txt vendored
View File

@ -167,7 +167,7 @@ github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util github.com/containers/buildah/pkg/util
github.com/containers/buildah/pkg/volumes github.com/containers/buildah/pkg/volumes
github.com/containers/buildah/util github.com/containers/buildah/util
# github.com/containers/common v0.57.1-0.20240129201029-3310a75e3608 # github.com/containers/common v0.57.1-0.20240130143645-b26099256b92
## explicit; go 1.20 ## explicit; go 1.20
github.com/containers/common/internal/attributedstring github.com/containers/common/internal/attributedstring
github.com/containers/common/libimage github.com/containers/common/libimage
@ -236,7 +236,7 @@ github.com/containers/conmon/runner/config
# github.com/containers/gvisor-tap-vsock v0.7.2 # github.com/containers/gvisor-tap-vsock v0.7.2
## explicit; go 1.20 ## explicit; go 1.20
github.com/containers/gvisor-tap-vsock/pkg/types github.com/containers/gvisor-tap-vsock/pkg/types
# github.com/containers/image/v5 v5.29.2-0.20240129204525-816800b5daf7 # github.com/containers/image/v5 v5.29.2-0.20240130233108-e66a1ade2efc
## explicit; go 1.19 ## explicit; go 1.19
github.com/containers/image/v5/copy github.com/containers/image/v5/copy
github.com/containers/image/v5/directory github.com/containers/image/v5/directory
@ -346,7 +346,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process github.com/containers/psgo/internal/process
# github.com/containers/storage v1.52.1-0.20240129173630-7a525ce0e2bc # github.com/containers/storage v1.52.1-0.20240130205044-62997abeaf2f
## explicit; go 1.20 ## explicit; go 1.20
github.com/containers/storage github.com/containers/storage
github.com/containers/storage/drivers github.com/containers/storage/drivers