diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 40804f5a9f..ec548e59a9 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -837,15 +837,15 @@ func AutoCompleteFarms(cmd *cobra.Command, args []string, toComplete string) ([] if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - suggestions := []string{} - cfg, err := config.ReadCustomConfig() + farms, err := podmanConfig.ContainersConfDefaultsRO.GetAllFarms() if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveNoFileComp } - for k := range cfg.Farms.List { - suggestions = append(suggestions, k) + suggestions := make([]string, 0, len(farms)) + for _, farm := range farms { + suggestions = append(suggestions, farm.Name) } return suggestions, cobra.ShellCompDirectiveNoFileComp @@ -856,16 +856,17 @@ func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete if !validCurrentCmdLine(cmd, args, toComplete) { return nil, cobra.ShellCompDirectiveNoFileComp } - suggestions := []string{} - cfg, err := config.ReadCustomConfig() + + cons, err := podmanConfig.ContainersConfDefaultsRO.GetAllConnections() if err != nil { cobra.CompErrorln(err.Error()) 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 - suggestions = append(suggestions, k+"\t"+v.URI) + suggestions = append(suggestions, con.Name+"\t"+con.URI) } return suggestions, cobra.ShellCompDirectiveNoFileComp diff --git a/cmd/podman/compose.go b/cmd/podman/compose.go index 80cde2289d..3a01d99abd 100644 --- a/cmd/podman/compose.go +++ b/cmd/podman/compose.go @@ -14,7 +14,6 @@ import ( "strings" "text/template" - "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/machine" @@ -114,15 +113,10 @@ func composeDockerHost() (string, error) { 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 { - return "", err - } - - // NOTE: podman --connection=foo and --url=... are injected - // into the default connection below in `root.go`. - defaultConnection := cfg.Engine.ActiveService - if defaultConnection == "" { + logrus.Info(err) switch runtime.GOOS { // If no default connection is set on Linux or FreeBSD, // 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) if err != nil { return "", fmt.Errorf("preparing connection to remote machine: %w", err) diff --git a/cmd/podman/farm/build.go b/cmd/podman/farm/build.go index 57e46a93ca..a787f4bb17 100644 --- a/cmd/podman/farm/build.go +++ b/cmd/podman/farm/build.go @@ -7,7 +7,6 @@ import ( "strings" "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/registry" "github.com/containers/podman/v4/cmd/podman/utils" @@ -50,14 +49,8 @@ func init() { cleanupFlag := "cleanup" flags.BoolVar(&buildOpts.buildOptions.Cleanup, cleanupFlag, false, "Remove built images from farm nodes on success") - podmanConfig := registry.PodmanConfig() farmFlagName := "farm" - // If remote, don't read the client's containers.conf file - defaultFarm := "" - if !registry.IsRemote() { - defaultFarm = podmanConfig.ContainersConfDefaultsRO.Farms.Default - } - flags.StringVar(&buildOpts.farm, farmFlagName, defaultFarm, "Farm to use for builds") + flags.StringVar(&buildOpts.farm, farmFlagName, "", "Farm to use for builds") _ = buildCommand.RegisterFlagCompletionFunc(farmFlagName, common.AutoCompleteFarms) localFlagName := "local" @@ -122,23 +115,9 @@ func build(cmd *cobra.Command, args []string) error { } 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() 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 { return fmt.Errorf("initializing: %w", err) } diff --git a/cmd/podman/farm/create.go b/cmd/podman/farm/create.go index 0642405bf9..a8cee530ff 100644 --- a/cmd/podman/farm/create.go +++ b/cmd/podman/farm/create.go @@ -41,43 +41,39 @@ func create(cmd *cobra.Command, args []string) error { farmName := args[0] connections := args[1:] - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - - if _, ok := cfg.Farms.List[farmName]; ok { - // if farm exists return an error - return fmt.Errorf("farm with name %q already exists", farmName) - } - - // Can create an empty farm without any connections - if len(connections) == 0 { - cfg.Farms.List[farmName] = []string{} - } - - for _, c := range connections { - if _, ok := cfg.Engine.ServiceDestinations[c]; ok { - if slices.Contains(cfg.Farms.List[farmName], c) { - // Don't add duplicate connections to a farm - continue - } - cfg.Farms.List[farmName] = append(cfg.Farms.List[farmName], c) - } else { - return fmt.Errorf("cannot create farm, %q is not a system connection", c) + err := config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if _, ok := cfg.Farm.List[farmName]; ok { + // if farm exists return an error + return fmt.Errorf("farm with name %q already exists", farmName) } - } - // If this is the first farm being created, set it as the default farm - if len(cfg.Farms.List) == 1 { - cfg.Farms.Default = farmName - } + // Can create an empty farm without any connections + if len(connections) == 0 { + cfg.Farm.List[farmName] = []string{} + } - err = cfg.Write() + for _, c := range connections { + if _, ok := cfg.Connection.Connections[c]; ok { + if slices.Contains(cfg.Farm.List[farmName], c) { + // Don't add duplicate connections to a farm + continue + } + cfg.Farm.List[farmName] = append(cfg.Farm.List[farmName], c) + } else { + 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 len(cfg.Farm.List) == 1 { + cfg.Farm.Default = farmName + } + + return nil + }) if err != nil { return err } - fmt.Printf("Farm %q created\n", farmName) return nil } diff --git a/cmd/podman/farm/list.go b/cmd/podman/farm/list.go index 26742f2767..6a63ff75c5 100644 --- a/cmd/podman/farm/list.go +++ b/cmd/podman/farm/list.go @@ -46,50 +46,29 @@ func init() { formatFlagName := "format" flags.StringVar(&lsOpts.Format, formatFlagName, "", "Format farm output using Go template") - _ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&farmOut{})) -} - -type farmOut struct { - Name string - Connections []string - Default bool + _ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&config.Farm{})) } func list(cmd *cobra.Command, args []string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - format := lsOpts.Format if format == "" && len(args) > 0 { format = "json" } - rows := make([]farmOut, 0) - for k, v := range cfg.Farms.List { - defaultFarm := false - if k == cfg.Farms.Default { - defaultFarm = true - } - - r := farmOut{ - Name: k, - Connections: v, - Default: defaultFarm, - } - rows = append(rows, r) + farms, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetAllFarms() + if err != nil { + return err } - sort.Slice(rows, func(i, j int) bool { - return rows[i].Name < rows[j].Name + sort.Slice(farms, func(i, j int) bool { + return farms[i].Name < farms[j].Name }) rpt := report.New(os.Stdout, cmd.Name()) defer rpt.Flush() if report.IsJSON(format) { - buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ") + buf, err := registry.JSONLibrary().MarshalIndent(farms, "", " ") if err == nil { fmt.Println(string(buf)) } @@ -100,7 +79,7 @@ func list(cmd *cobra.Command, args []string) error { rpt, err = rpt.Parse(report.OriginUser, format) } else { 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 { return err @@ -111,11 +90,12 @@ func list(cmd *cobra.Command, args []string) error { "Default": "Default", "Connections": "Connections", "Name": "Name", + "ReadWrite": "ReadWrite", }}) if err != nil { return err } } - return rpt.Execute(rows) + return rpt.Execute(farms) } diff --git a/cmd/podman/farm/remove.go b/cmd/podman/farm/remove.go index 6ee4708110..a9622336ee 100644 --- a/cmd/podman/farm/remove.go +++ b/cmd/podman/farm/remove.go @@ -43,58 +43,56 @@ func init() { } func rm(cmd *cobra.Command, args []string) error { - cfg, err := config.ReadCustomConfig() + deletedFarms := []string{} + err := config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if rmOpts.All { + cfg.Farm.List = make(map[string][]string) + cfg.Farm.Default = "" + return nil + } + + // If the --all is not set, we require at least one arg + if len(args) == 0 { + return errors.New("requires at lease 1 arg(s), received 0") + } + + if len(cfg.Farm.List) == 0 { + return errors.New("no existing farms; nothing to remove") + } + + for _, k := range args { + if _, ok := cfg.Farm.List[k]; !ok { + logrus.Warnf("farm %q doesn't exist; nothing to remove", k) + continue + } + delete(cfg.Farm.List, k) + deletedFarms = append(deletedFarms, k) + if k == cfg.Farm.Default { + cfg.Farm.Default = "" + } + } + // Return error if none of the given farms were deleted + if len(deletedFarms) == 0 { + return fmt.Errorf("failed to delete farms %q", args) + } + + // Set a new default farm if the current default farm has been removed + if cfg.Farm.Default == "" && cfg.Farm.List != nil { + for k := range cfg.Farm.List { + cfg.Farm.Default = k + break + } + } + return nil + }) if err != nil { return err } - if rmOpts.All { - cfg.Farms.List = make(map[string][]string) - cfg.Farms.Default = "" - if err := cfg.Write(); err != nil { - return err - } fmt.Println("All farms have been deleted") return nil } - // If the --all is not set, we require at least one arg - if len(args) == 0 { - return errors.New("requires at lease 1 arg(s), received 0") - } - - if len(cfg.Farms.List) == 0 { - return errors.New("no existing farms; nothing to remove") - } - - deletedFarms := []string{} - for _, k := range args { - if _, ok := cfg.Farms.List[k]; !ok { - logrus.Warnf("farm %q doesn't exist; nothing to remove", k) - continue - } - delete(cfg.Farms.List, k) - deletedFarms = append(deletedFarms, k) - if k == cfg.Farms.Default { - cfg.Farms.Default = "" - } - } - // Return error if none of the given farms were deleted - if len(deletedFarms) == 0 { - return fmt.Errorf("failed to delete farms %q", args) - } - - // Set a new default farm if the current default farm has been removed - if cfg.Farms.Default == "" && cfg.Farms.List != nil { - for k := range cfg.Farms.List { - cfg.Farms.Default = k - break - } - } - if err := cfg.Write(); err != nil { - return err - } - for _, k := range deletedFarms { fmt.Printf("Farm %q deleted\n", k) } diff --git a/cmd/podman/farm/update.go b/cmd/podman/farm/update.go index e145b0f8fb..08c31114b7 100644 --- a/cmd/podman/farm/update.go +++ b/cmd/podman/farm/update.go @@ -63,62 +63,59 @@ 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) } - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - - if len(cfg.Farms.List) == 0 { - return errors.New("no farms are created at this time, there is nothing to update") - } - - if _, ok := cfg.Farms.List[farmName]; !ok { - return fmt.Errorf("cannot update farm, %q farm doesn't exist", farmName) - } - - if defChanged { - // Change the default to the given farm if --default=true - if updateOpts.Default { - cfg.Farms.Default = farmName - } else { - // if --default=false, user doesn't want any farms to be default so clear the DefaultFarm - cfg.Farms.Default = "" - } - } - - if val, ok := cfg.Farms.List[farmName]; ok { - cMap := make(map[string]int) - for _, c := range val { - cMap[c] = 0 + err := config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if len(cfg.Farm.List) == 0 { + return errors.New("no farms are created at this time, there is nothing to update") } - for _, cRemove := range updateOpts.Remove { - connections := cfg.Farms.List[farmName] - if slices.Contains(connections, cRemove) { - delete(cMap, cRemove) + if _, ok := cfg.Farm.List[farmName]; !ok { + return fmt.Errorf("cannot update farm, %q farm doesn't exist", farmName) + } + + if defChanged { + // Change the default to the given farm if --default=true + if updateOpts.Default { + cfg.Farm.Default = farmName } else { - return fmt.Errorf("cannot remove from farm, %q is not a connection in the farm", cRemove) + // if --default=false, user doesn't want any farms to be default so clear the DefaultFarm + cfg.Farm.Default = "" } } - for _, cAdd := range updateOpts.Add { - if _, ok := cfg.Engine.ServiceDestinations[cAdd]; ok { - if _, ok := cMap[cAdd]; !ok { - cMap[cAdd] = 0 + if val, ok := cfg.Farm.List[farmName]; ok { + cMap := make(map[string]int) + for _, c := range val { + cMap[c] = 0 + } + + for _, cRemove := range updateOpts.Remove { + connections := cfg.Farm.List[farmName] + if slices.Contains(connections, cRemove) { + delete(cMap, cRemove) + } else { + return fmt.Errorf("cannot remove from farm, %q is not a connection in the farm", cRemove) } - } else { - return fmt.Errorf("cannot add to farm, %q is not a system connection", cAdd) } - } - updatedConnections := []string{} - for k := range cMap { - updatedConnections = append(updatedConnections, k) - } - cfg.Farms.List[farmName] = updatedConnections - } + for _, cAdd := range updateOpts.Add { + if _, ok := cfg.Connection.Connections[cAdd]; ok { + if _, ok := cMap[cAdd]; !ok { + cMap[cAdd] = 0 + } + } else { + return fmt.Errorf("cannot add to farm, %q is not a system connection", cAdd) + } + } - if err := cfg.Write(); err != nil { + updatedConnections := []string{} + for k := range cMap { + updatedConnections = append(updatedConnections, k) + } + cfg.Farm.List[farmName] = updatedConnections + } + return nil + }) + if err != nil { return err } fmt.Printf("Farm %q updated\n", farmName) diff --git a/cmd/podman/machine/info.go b/cmd/podman/machine/info.go index fbc5af3c92..df2b7a8950 100644 --- a/cmd/podman/machine/info.go +++ b/cmd/podman/machine/info.go @@ -8,7 +8,6 @@ import ( "runtime" "github.com/containers/common/pkg/completion" - "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/report" "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" @@ -108,16 +107,18 @@ func hostInfo() (*entities.MachineHostInfo, error) { host.NumberOfMachines = len(listResponse) - cfg, err := config.ReadCustomConfig() - if err != nil { - return nil, err + defaultCon := "" + con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true) + 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 host.MachineState = "Stopped" for _, vm := range listResponse { // Set default machine if found - if vm.Name == cfg.Engine.ActiveService { + if vm.Name == defaultCon { host.DefaultMachine = vm.Name } // If machine is running or starting, it is automatically the current machine diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index 7deac5c5aa..f3a5d65e2c 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -7,7 +7,6 @@ import ( "os" "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/libpod/events" "github.com/containers/podman/v4/pkg/machine" @@ -150,15 +149,18 @@ func initMachine(cmd *cobra.Command, args []string) error { 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 { return err } - - // check if a system connection already exists - for _, connection := range []string{initOpts.Name, fmt.Sprintf("%s-root", initOpts.Name)} { - if _, valueFound := cfg.Engine.ServiceDestinations[connection]; valueFound { - return fmt.Errorf("system connection %q already exists. consider a different machine name or remove the connection with `podman system connection rm`", connection) + for _, con := range cons { + if con.ReadWrite { + for _, connection := range []string{initOpts.Name, fmt.Sprintf("%s-root", initOpts.Name)} { + 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) + } + } } } diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index e0c92c4238..f0de62c6ae 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -10,7 +10,6 @@ import ( "time" "github.com/containers/common/pkg/completion" - "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/report" "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" @@ -79,12 +78,15 @@ func list(cmd *cobra.Command, args []string) error { return listResponse[i].Running }) - if report.IsJSON(listFlag.format) { - machineReporter, err := toMachineFormat(listResponse) - if err != nil { - return err - } + defaultCon := "" + con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true) + 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 + } + if report.IsJSON(listFlag.format) { + machineReporter := toMachineFormat(listResponse, defaultCon) b, err := json.MarshalIndent(machineReporter, "", " ") if err != nil { return err @@ -93,11 +95,7 @@ func list(cmd *cobra.Command, args []string) error { return nil } - machineReporter, err := toHumanFormat(listResponse) - if err != nil { - return err - } - + machineReporter := toHumanFormat(listResponse, defaultCon) return outputTemplate(cmd, machineReporter) } @@ -153,16 +151,11 @@ func streamName(imageStream string) string { return imageStream } -func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) { - cfg, err := config.ReadCustomConfig() - if err != nil { - return nil, err - } - +func toMachineFormat(vms []*machine.ListResponse, defaultCon string) []*entities.ListReporter { machineResponses := make([]*entities.ListReporter, 0, len(vms)) for _, vm := range vms { response := new(entities.ListReporter) - response.Default = vm.Name == cfg.Engine.ActiveService + response.Default = vm.Name == defaultCon response.Name = vm.Name response.Running = vm.Running response.LastUp = strTime(vm.LastUp) @@ -180,19 +173,14 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, err machineResponses = append(machineResponses, response) } - return machineResponses, nil + return machineResponses } -func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) { - cfg, err := config.ReadCustomConfig() - if err != nil { - return nil, err - } - +func toHumanFormat(vms []*machine.ListResponse, defaultCon string) []*entities.ListReporter { humanResponses := make([]*entities.ListReporter, 0, len(vms)) for _, vm := range vms { response := new(entities.ListReporter) - if vm.Name == cfg.Engine.ActiveService { + if vm.Name == defaultCon { response.Name = vm.Name + "*" response.Default = true } else { @@ -218,5 +206,5 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error humanResponses = append(humanResponses, response) } - return humanResponses, nil + return humanResponses } diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go index 9e5084630a..416643efd2 100644 --- a/cmd/podman/machine/ssh.go +++ b/cmd/podman/machine/ssh.go @@ -7,7 +7,6 @@ import ( "net/url" "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/utils" "github.com/containers/podman/v4/pkg/machine" @@ -93,12 +92,12 @@ func ssh(cmd *cobra.Command, args []string) error { } func remoteConnectionUsername() (string, error) { - cfg, err := config.ReadCustomConfig() + con, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true) if err != nil { return "", err } - dest := cfg.Engine.ServiceDestinations[cfg.Engine.ActiveService].URI - uri, err := url.Parse(dest) + + uri, err := url.Parse(con.URI) if err != nil { return "", err } diff --git a/cmd/podman/root.go b/cmd/podman/root.go index a10ede54a9..2a1df1a2fd 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -143,7 +143,7 @@ func Execute() { } // 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 contextConn, host := cmd.Root().LocalFlags().Lookup("context"), cmd.Root().LocalFlags().Lookup("host") 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 { case conn != nil && conn.Changed: if contextConn != nil && contextConn.Changed { - err = fmt.Errorf("use of --connection and --context at the same time is not allowed") - return + return fmt.Errorf("use of --connection and --context at the same time is not allowed") } - if dest, ok := conf.Engine.ServiceDestinations[conn.Value.String()]; ok { - podmanConfig.URI = dest.URI - podmanConfig.Identity = dest.Identity - podmanConfig.MachineMode = dest.IsMachine - return + con, err := conf.GetConnection(conn.Value.String(), false) + if err != nil { + return err } - err = fmt.Errorf("connection %q not found", conn.Value.String()) - return + podmanConfig.URI = con.URI + podmanConfig.Identity = con.Identity + podmanConfig.MachineMode = con.IsMachine case url.Changed: podmanConfig.URI = url.Value.String() - return case contextConn != nil && contextConn.Changed: service := contextConn.Value.String() if service != "default" { - if dest, ok := conf.Engine.ServiceDestinations[contextConn.Value.String()]; ok { - podmanConfig.URI = dest.URI - podmanConfig.Identity = dest.Identity - podmanConfig.MachineMode = dest.IsMachine - return + con, err := conf.GetConnection(service, false) + if err != nil { + return err } - return fmt.Errorf("connection %q not found", service) + podmanConfig.URI = con.URI + podmanConfig.Identity = con.Identity + podmanConfig.MachineMode = con.IsMachine } case host.Changed: podmanConfig.URI = host.Value.String() } - return + return nil } // setupRemoteConnection returns information about the active service destination @@ -191,28 +188,30 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) func setupRemoteConnection(podmanConfig *entities.PodmanConfig) error { conf := podmanConfig.ContainersConfDefaultsRO connEnv, hostEnv, sshkeyEnv := os.Getenv("CONTAINER_CONNECTION"), os.Getenv("CONTAINER_HOST"), os.Getenv("CONTAINER_SSHKEY") - dest, destFound := conf.Engine.ServiceDestinations[conf.Engine.ActiveService] switch { case connEnv != "": - if ConnEnvDest, ok := conf.Engine.ServiceDestinations[connEnv]; ok { - podmanConfig.URI = ConnEnvDest.URI - podmanConfig.Identity = ConnEnvDest.Identity - podmanConfig.MachineMode = ConnEnvDest.IsMachine - return nil + con, err := conf.GetConnection(connEnv, false) + if err != nil { + return err } - return fmt.Errorf("connection %q not found", connEnv) + podmanConfig.URI = con.URI + podmanConfig.Identity = con.Identity + podmanConfig.MachineMode = con.IsMachine case hostEnv != "": if sshkeyEnv != "" { podmanConfig.Identity = sshkeyEnv } podmanConfig.URI = hostEnv - case destFound: - podmanConfig.URI = dest.URI - podmanConfig.Identity = dest.Identity - podmanConfig.MachineMode = dest.IsMachine default: - podmanConfig.URI = registry.DefaultAPIAddress() + 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() + } } return nil } diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go index 786bc0b955..dc425f79a7 100644 --- a/cmd/podman/system/connection/add.go +++ b/cmd/podman/system/connection/add.go @@ -176,49 +176,39 @@ func add(cmd *cobra.Command, args []string) error { 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{ - URI: uri.String(), + URI: uri.String(), + Identity: cOpts.Identity, } - if cmd.Flags().Changed("identity") { - dst.Identity = cOpts.Identity - } - - if cfg.Engine.ServiceDestinations == nil { - cfg.Engine.ServiceDestinations = map[string]config.Destination{ - args[0]: dst, + connection := args[0] + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if cOpts.Default { + cfg.Connection.Default = connection } - cfg.Engine.ActiveService = args[0] - } else { - cfg.Engine.ServiceDestinations[args[0]] = dst - } - if cOpts.Farm != "" { - if cfg.Farms.List == nil { - cfg.Farms.List = map[string][]string{ - cOpts.Farm: {args[0]}, + if cfg.Connection.Connections == nil { + cfg.Connection.Connections = map[string]config.Destination{ + connection: dst, } - cfg.Farms.Default = cOpts.Farm + cfg.Connection.Default = connection } else { - if val, ok := cfg.Farms.List[cOpts.Farm]; ok { - cfg.Farms.List[cOpts.Farm] = append(val, args[0]) + cfg.Connection.Connections[connection] = dst + } + + // Create or update an existing farm with the connection being added + if cOpts.Farm != "" { + if len(cfg.Farm.List) == 0 { + cfg.Farm.Default = cOpts.Farm + } + if val, ok := cfg.Farm.List[cOpts.Farm]; ok { + cfg.Farm.List[cOpts.Farm] = append(val, connection) } else { - cfg.Farms.List[cOpts.Farm] = []string{args[0]} + cfg.Farm.List[cOpts.Farm] = []string{connection} } } - } - return cfg.Write() + return nil + }) } func create(cmd *cobra.Command, args []string) error { @@ -237,24 +227,21 @@ func create(cmd *cobra.Command, args []string) error { return err } - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - dst := config.Destination{ URI: uri.String(), } - if cfg.Engine.ServiceDestinations == nil { - cfg.Engine.ServiceDestinations = map[string]config.Destination{ - args[0]: dst, + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if cfg.Connection.Connections == nil { + cfg.Connection.Connections = map[string]config.Destination{ + args[0]: dst, + } + cfg.Connection.Default = args[0] + } else { + cfg.Connection.Connections[args[0]] = dst } - cfg.Engine.ActiveService = args[0] - } else { - cfg.Engine.ServiceDestinations[args[0]] = dst - } - return cfg.Write() + return nil + }) } func translateDest(path string) (string, error) { diff --git a/cmd/podman/system/connection/default.go b/cmd/podman/system/connection/default.go index 8d1709e9f4..58a3f67514 100644 --- a/cmd/podman/system/connection/default.go +++ b/cmd/podman/system/connection/default.go @@ -45,15 +45,13 @@ func init() { } func defaultRunE(cmd *cobra.Command, args []string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } + connection := args[0] + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + 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 { - return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0]) - } - - cfg.Engine.ActiveService = args[0] - return cfg.Write() + cfg.Connection.Default = connection + return nil + }) } diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go index ea6f822a9d..48e9314e0c 100644 --- a/cmd/podman/system/connection/list.go +++ b/cmd/podman/system/connection/list.go @@ -41,7 +41,7 @@ var ( func init() { initFlags := func(cmd *cobra.Command) { 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") } @@ -62,22 +62,11 @@ func init() { initFlags(inspectCmd) } -type namedDestination struct { - Name string - config.Destination - Default bool -} - func list(cmd *cobra.Command, _ []string) error { return inspect(cmd, nil) } func inspect(cmd *cobra.Command, args []string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - format := cmd.Flag("format").Value.String() if format == "" && args != nil { format = "json" @@ -87,31 +76,23 @@ func inspect(cmd *cobra.Command, args []string) error { if err != nil { return err } - rows := make([]namedDestination, 0) - for k, v := range cfg.Engine.ServiceDestinations { - if args != nil && !slices.Contains(args, k) { + + cons, err := registry.PodmanConfig().ContainersConfDefaultsRO.GetAllConnections() + 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 } if quiet { - fmt.Println(k) + fmt.Println(con.Name) continue } - def := false - if k == cfg.Engine.ActiveService { - def = true - } - r := namedDestination{ - Name: k, - Destination: config.Destination{ - Identity: v.Identity, - URI: v.URI, - IsMachine: v.IsMachine, - }, - Default: def, - } - rows = append(rows, r) + rows = append(rows, con) } if quiet { @@ -137,7 +118,7 @@ func inspect(cmd *cobra.Command, args []string) error { rpt, err = rpt.Parse(report.OriginUser, format) } else { 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 { return err @@ -145,10 +126,11 @@ func inspect(cmd *cobra.Command, args []string) error { if rpt.RenderHeaders { err = rpt.Execute([]map[string]string{{ - "Default": "Default", - "Identity": "Identity", - "Name": "Name", - "URI": "URI", + "Default": "Default", + "Identity": "Identity", + "Name": "Name", + "URI": "URI", + "ReadWrite": "ReadWrite", }}) if err != nil { return err diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go index 1e6ac183fa..187dfd8a7d 100644 --- a/cmd/podman/system/connection/remove.go +++ b/cmd/podman/system/connection/remove.go @@ -48,43 +48,35 @@ func init() { } func rm(cmd *cobra.Command, args []string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if rmOpts.All { + cfg.Connection.Connections = nil + cfg.Connection.Default = "" - if rmOpts.All { - for k := range cfg.Engine.ServiceDestinations { - delete(cfg.Engine.ServiceDestinations, k) + // Clear all the connections in any existing farms + for k := range cfg.Farm.List { + cfg.Farm.List[k] = []string{} + } + return nil } - cfg.Engine.ActiveService = "" - // Clear all the connections in any existing farms - for k := range cfg.Farms.List { - cfg.Farms.List[k] = []string{} + if len(args) != 1 { + return errors.New("accepts 1 arg(s), received 0") } - return cfg.Write() - } - if len(args) != 1 { - return errors.New("accepts 1 arg(s), received 0") - } - - if cfg.Engine.ServiceDestinations != nil { - delete(cfg.Engine.ServiceDestinations, args[0]) - } - - 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 - for k, v := range cfg.Farms.List { - index := slices.Index(v, args[0]) - if index > -1 { - cfg.Farms.List[k] = append(v[:index], v[index+1:]...) + delete(cfg.Connection.Connections, args[0]) + if cfg.Connection.Default == args[0] { + cfg.Connection.Default = "" } - } - return cfg.Write() + // If there are existing farm, remove the deleted connection that might be part of a farm + for k, v := range cfg.Farm.List { + index := slices.Index(v, args[0]) + if index > -1 { + cfg.Farm.List[k] = append(v[:index], v[index+1:]...) + } + } + + return nil + }) } diff --git a/cmd/podman/system/connection/rename.go b/cmd/podman/system/connection/rename.go index 8a4552e5e0..9c4f32205b 100644 --- a/cmd/podman/system/connection/rename.go +++ b/cmd/podman/system/connection/rename.go @@ -33,21 +33,18 @@ func init() { } func rename(cmd *cobra.Command, args []string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if _, found := cfg.Connection.Connections[args[0]]; !found { + return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0]) + } - 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]) - } + cfg.Connection.Connections[args[1]] = cfg.Connection.Connections[args[0]] + delete(cfg.Connection.Connections, args[0]) - cfg.Engine.ServiceDestinations[args[1]] = cfg.Engine.ServiceDestinations[args[0]] - delete(cfg.Engine.ServiceDestinations, args[0]) + if cfg.Connection.Default == args[0] { + cfg.Connection.Default = args[1] + } - if cfg.Engine.ActiveService == args[0] { - cfg.Engine.ActiveService = args[1] - } - - return cfg.Write() + return nil + }) } diff --git a/docs/source/markdown/podman-farm-list.1.md b/docs/source/markdown/podman-farm-list.1.md index e643bdc1a4..06a26dbcc4 100644 --- a/docs/source/markdown/podman-farm-list.1.md +++ b/docs/source/markdown/podman-farm-list.1.md @@ -18,18 +18,19 @@ List all the existing farms. Change the default output format. This can be of a supported type like 'json' or a Go template. Valid placeholders for the Go template listed below: -| **Placeholder** | **Description** | -| --------------- | ------------------------------------------ | -| .Connections | List of all system connections in the farm | -| .Default | Indicates whether farm is the default | -| .Name | Farm name | +| **Placeholder** | **Description** | +| --------------- | --------------------------------------------------------------------- | +| .Connections | List of all system connections in the farm | +| .Default | Indicates whether farm is the default | +| .Name | Farm name | +| .ReadWrite | Indicates if this farm can be modified using the podman farm commands | ## EXAMPLE ``` $ podman farm list -Name Connections Default -farm1 [f38 f37] false -farm2 [f37] true +Name Connections Default ReadWrite +farm1 [f38 f37] false true +farm2 [f37] true true ``` ## SEE ALSO **[podman(1)](podman.1.md)**, **[podman-farm(1)](podman-farm.1.md)** diff --git a/docs/source/markdown/podman-system-connection-list.1.md b/docs/source/markdown/podman-system-connection-list.1.md index a344564608..edd2372122 100644 --- a/docs/source/markdown/podman-system-connection-list.1.md +++ b/docs/source/markdown/podman-system-connection-list.1.md @@ -23,6 +23,7 @@ Valid placeholders for the Go template listed below: | .Default | Indicates whether connection is the default | | .Identity | Path to file containing SSH identity | | .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*] | #### **--quiet**, **-q** @@ -32,9 +33,9 @@ Only show connection names ## EXAMPLE ``` $ podman system connection list -Name URI Identity Default -devl ssh://root@example.com:/run/podman/podman.sock ~/.ssh/id_rsa True -devl ssh://user@example.com:/run/user/1000/podman/podman.sock ~/.ssh/id_rsa False +Name URI Identity Default ReadWrite +deva ssh://root@example.com:/run/podman/podman.sock ~/.ssh/id_rsa true true +devb ssh://user@example.com:/run/user/1000/podman/podman.sock ~/.ssh/id_rsa false true ``` ## SEE ALSO **[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)** diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index d505df4c8b..cf908cdcb1 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -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. +#### **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** Set default `--storage-driver` value. diff --git a/go.mod b/go.mod index 95d9519b93..b33729b37b 100644 --- a/go.mod +++ b/go.mod @@ -11,14 +11,14 @@ require ( github.com/checkpoint-restore/go-criu/v7 v7.0.0 github.com/containernetworking/plugins v1.4.0 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/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/ocicrypt v1.1.9 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/stream-metadata-go v0.4.4 github.com/crc-org/vfkit v0.5.0 diff --git a/go.sum b/go.sum index ee9c0f4ea1..64153b12d4 100644 --- a/go.sum +++ b/go.sum @@ -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/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/common v0.57.1-0.20240129201029-3310a75e3608 h1:P+3HLEBeuFr/XcFtyZIS8uk5zdbcvtrHKCSzHlUUbdY= -github.com/containers/common v0.57.1-0.20240129201029-3310a75e3608/go.mod h1:Na7hGh5WnmB0RdGkKyb6JQb6DtKrs5qoIGrPucuR8t0= +github.com/containers/common v0.57.1-0.20240130143645-b26099256b92 h1:Q60+ofGhDjVxY5lvYmmcVN8aeS9gtQ6pAn/pyLh7rRM= +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/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/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.20240129204525-816800b5daf7/go.mod h1:op1w+ftmdbE3UNW/6as8mruL1i5AMq6nH+btNOykMcU= +github.com/containers/image/v5 v5.29.2-0.20240130233108-e66a1ade2efc h1:3I5+mrrG7Fuv4aA13t1hAMQcjN3rTAQInfbxa5P+XH4= +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/go.mod h1:f/q1wCdQqOLiK3IZqqBfOD7exMZYBU5pDYsrMa/pSFg= 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/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc= 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.20240129173630-7a525ce0e2bc/go.mod h1:T/ZMocbhShnMLIF0pdkiLPwpkwlGlyUWJeSXnfC/uew= +github.com/containers/storage v1.52.1-0.20240130205044-62997abeaf2f h1:BJSLHe7f1tgu53d8mGIK/y2KhEev5lggWlIk1rWYT7k= +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/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= diff --git a/pkg/domain/utils/scp.go b/pkg/domain/utils/scp.go index b67c571044..96f1d90502 100644 --- a/pkg/domain/utils/scp.go +++ b/pkg/domain/utils/scp.go @@ -33,11 +33,6 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool, sshMode 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{} cliConnections := []string{} args := []string{src} @@ -84,17 +79,15 @@ func ExecuteTransfer(src, dst string, parentFlags []string, quiet bool, sshMode cliConnections = []string{} } - cfg, err := config.ReadCustomConfig() // get ready to set ssh destination if necessary + cfg, err := config.Default() if err != nil { return nil, nil, nil, nil, err } - var serv map[string]config.Destination - serv, err = GetServiceInformation(&sshInfo, cliConnections, cfg) + err = GetServiceInformation(&sshInfo, cliConnections, cfg) if err != nil { 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) 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 -func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections []string, cfg *config.Config) (map[string]config.Destination, error) { - var serv map[string]config.Destination +func GetServiceInformation(sshInfo *entities.ImageScpConnections, cliConnections []string, cfg *config.Config) error { var urlS string var iden string for i, val := range cliConnections { connection, _, _ := strings.Cut(val, "::") sshInfo.Connections = append(sshInfo.Connections, connection) - conn, found := cfg.Engine.ServiceDestinations[sshInfo.Connections[i]] - if found { + conn, err := cfg.GetConnection(sshInfo.Connections[i], false) + if err == nil { + // connection found urlS = conn.URI iden = conn.Identity } 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 if err != nil { - return nil, err + return err } if urlFinal.User.Username() == "" { if urlFinal.User, err = GetUserInfo(urlFinal); err != nil { - return nil, err + return err } } sshInfo.URI = append(sshInfo.URI, urlFinal) sshInfo.Identities = append(sshInfo.Identities, iden) } - return serv, nil + return nil } func GetUserInfo(uri *url.URL) (*url.Userinfo, error) { diff --git a/pkg/farm/farm.go b/pkg/farm/farm.go index d6224d6f3b..5204ba4005 100644 --- a/pkg/farm/farm.go +++ b/pkg/farm/farm.go @@ -32,7 +32,7 @@ type Schedule struct { 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{ builders: make(map[string]entities.ImageEngine), localEngine: localEngine, @@ -43,22 +43,22 @@ func newFarmWithBuilders(_ context.Context, name string, destinations *map[strin builderGroup multierror.Group ) // Set up the remote connections to handle the builds - for name, dest := range *destinations { - name, dest := name, dest + for _, con := range cons { + con := con 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{ EngineMode: entities.TunnelMode, - URI: dest.URI, - Identity: dest.Identity, - MachineMode: dest.IsMachine, - FarmNodeName: name, + URI: con.URI, + Identity: con.Identity, + MachineMode: con.IsMachine, + FarmNodeName: con.Name, }) 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() defer builderMutex.Unlock() 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) { // Get the destinations of the connections specified in the farm - destinations, err := getFarmDestinations(name) + name, destinations, err := getFarmDestinations(name) if err != nil { 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. @@ -460,22 +460,21 @@ func (f *Farm) Build(ctx context.Context, schedule Schedule, options entities.Bu return nil } -func getFarmDestinations(name string) (map[string]config.Destination, error) { - dest := make(map[string]config.Destination) - cfg, err := config.ReadCustomConfig() +func getFarmDestinations(name string) (string, []config.Connection, error) { + cfg, err := config.Default() if err != nil { - return dest, err + return "", nil, err } - // If no farm name is given, then grab all the service destinations available 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 + cons, err := cfg.GetAllConnections() + return name, cons, err } - - // Go through the connections in the farm and get their destination - for _, c := range cfg.Farms.List[name] { - dest[c] = cfg.Engine.ServiceDestinations[c] - } - - return dest, nil + cons, err := cfg.GetFarmConnections(name) + return name, cons, err } diff --git a/pkg/machine/connection.go b/pkg/machine/connection.go index 27f8d95548..c34ba4816f 100644 --- a/pkg/machine/connection.go +++ b/pkg/machine/connection.go @@ -17,93 +17,81 @@ func AddConnection(uri fmt.Stringer, name, identity string, isDefault bool) erro if len(identity) < 1 { return errors.New("identity must be defined") } - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - if _, ok := cfg.Engine.ServiceDestinations[name]; ok { - return errors.New("cannot overwrite connection") - } - if isDefault { - cfg.Engine.ActiveService = name - } - dst := config.Destination{ - URI: uri.String(), - IsMachine: true, - } - dst.Identity = identity - if cfg.Engine.ServiceDestinations == nil { - cfg.Engine.ServiceDestinations = map[string]config.Destination{ - name: dst, - } - cfg.Engine.ActiveService = name - } else { - cfg.Engine.ServiceDestinations[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 config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if _, ok := cfg.Connection.Connections[name]; ok { + return errors.New("cannot overwrite connection") } - } - return false, nil + dst := config.Destination{ + URI: uri.String(), + IsMachine: true, + Identity: identity, + } + + if isDefault { + cfg.Connection.Default = name + } + + if cfg.Connection.Connections == nil { + cfg.Connection.Connections = map[string]config.Destination{ + name: dst, + } + cfg.Connection.Default = name + } else { + cfg.Connection.Connections[name] = dst + } + + return nil + }) } func ChangeConnectionURI(name string, uri fmt.Stringer) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - dst, ok := cfg.Engine.ServiceDestinations[name] - if !ok { - return errors.New("connection not found") - } - dst.URI = uri.String() - cfg.Engine.ServiceDestinations[name] = dst + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + dst, ok := cfg.Connection.Connections[name] + if !ok { + return errors.New("connection not found") + } + dst.URI = uri.String() + cfg.Connection.Connections[name] = dst - return cfg.Write() + return nil + }) } -func ChangeDefault(name string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - - cfg.Engine.ActiveService = name - - return cfg.Write() +// UpdateConnectionIfDefault updates the default connection to the rootful/rootless when depending +// on the bool but only if other rootful/less connection was already the default. +// Returns true if it modified the default +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 + }) } func RemoveConnections(names ...string) error { - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - for _, name := range names { - if _, ok := cfg.Engine.ServiceDestinations[name]; ok { - delete(cfg.Engine.ServiceDestinations, name) - } else { - return fmt.Errorf("unable to find connection named %q", name) - } + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + for _, name := range names { + if _, ok := cfg.Connection.Connections[name]; ok { + delete(cfg.Connection.Connections, name) + } else { + return fmt.Errorf("unable to find connection named %q", name) + } - if cfg.Engine.ActiveService == name { - cfg.Engine.ActiveService = "" - for service := range cfg.Engine.ServiceDestinations { - cfg.Engine.ActiveService = service - break + if cfg.Connection.Default == name { + cfg.Connection.Default = "" } } - } - return cfg.Write() + for service := range cfg.Connection.Connections { + cfg.Connection.Default = service + break + } + return nil + }) } // removeFilesAndConnections removes any files and connections with the given names diff --git a/pkg/machine/machine_common.go b/pkg/machine/machine_common.go index 85c1aff3be..5d440c6681 100644 --- a/pkg/machine/machine_common.go +++ b/pkg/machine/machine_common.go @@ -158,22 +158,7 @@ following command in your terminal session: // SetRootful modifies the machine's default connection to be either rootful or // rootless func SetRootful(rootful bool, name, rootfulName string) error { - changeCon, err := AnyConnectionDefault(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 + return UpdateConnectionIfDefault(rootful, name, rootfulName) } // WriteConfig writes the machine's JSON config file diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index d04e7e38c8..08d27a96c8 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -122,6 +122,7 @@ var ( // invalid containers.conf files to fail the cleanup. os.Unsetenv("CONTAINERS_CONF") os.Unsetenv("CONTAINERS_CONF_OVERRIDE") + os.Unsetenv("PODMAN_CONNECTIONS_CONF") podmanTest.Cleanup() f := CurrentSpecReport() processTestResult(f) diff --git a/test/e2e/farm_test.go b/test/e2e/farm_test.go index 2765a2a90b..4c6f6172bc 100644 --- a/test/e2e/farm_test.go +++ b/test/e2e/farm_test.go @@ -4,7 +4,6 @@ import ( "os" "path/filepath" - "github.com/containers/common/pkg/config" . "github.com/containers/podman/v4/test/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -13,21 +12,20 @@ import ( func setupContainersConfWithSystemConnections() { // 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) 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() 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() { @@ -36,19 +34,12 @@ var _ = Describe("podman farm", func() { Context("without running API service", func() { It("verify system connections exist", func() { - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg).Should(HaveActiveService("QA")) - Expect(cfg).Should(VerifyService( - "QA", - "ssh://root@podman.test:2222/run/podman/podman.sock", - "", - )) - Expect(cfg).Should(VerifyService( - "QB", - "ssh://root@podman.test:3333/run/podman/podman.sock", - "", - )) + session := podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`QA ssh://root@podman.test:2222/run/podman/podman.sock true true +QB ssh://root@podman.test:3333/run/podman/podman.sock false true +`)) }) It("create farm", func() { @@ -73,12 +64,13 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm3\" 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"})) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm3", []string{})) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true +farm2 [QA] false true +farm3 [] false true +`)) // create existing farm should exit with error cmd = []string{"farm", "create", "farm3"} @@ -109,12 +101,13 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm3\" 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"})) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm3", []string{})) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true +farm2 [QA] false true +farm3 [] false true +`)) // update farm1 to remove the QA connection from it cmd = []string{"farm", "update", "--remove", "QA,QB", "farm1"} @@ -137,12 +130,13 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" updated")) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Farms.Default).Should(Equal("farm2")) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{})) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm3", []string{"QB"})) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`farm1 [] false true +farm2 [QA] true true +farm3 [QB] false true +`)) // update farm2 to not be the default, no farms should be the default cmd = []string{"farm", "update", "--default=false", "farm2"} @@ -151,9 +145,13 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(ContainSubstring("Farm \"farm2\" updated")) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Farms.Default).Should(BeEmpty()) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + 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() { @@ -171,11 +169,12 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) 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"})) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true +farm2 [QA] false true +`)) // update farm1 to add no-node connection to it cmd = []string{"farm", "update", "--add", "no-node", "farm1"} @@ -189,12 +188,13 @@ var _ = Describe("podman farm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(ExitWithError()) - // read config again to ensure that nothing has changed - 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"})) + // check again to ensure that nothing has changed + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true +farm2 [QA] false true +`)) }) It("update non-existent farm", func() { @@ -205,11 +205,6 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) 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 cmd = []string{"farm", "update", "--add", "no-node", "non-existent"} session = podmanTest.Podman(cmd) @@ -222,11 +217,10 @@ var _ = Describe("podman farm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(ExitWithError()) - // read config again and ensure nothing has changed - 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"})) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal(`farm1 [QA QB] true true`)) }) It("remove farms", func() { @@ -244,11 +238,12 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) 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"})) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(string(session.Out.Contents())).To(Equal(`farm1 [QA QB] true true +farm2 [QA] false true +`)) // remove farm1 and a non-existent 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.ErrorToString()).Should(ContainSubstring("doesn't exist; nothing to remove")) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Farms.Default).Should(Equal("farm2")) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm2", []string{"QA"})) - Expect(cfg.Farms.List).Should(Not(HaveKey("farm1"))) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal(`farm2 [QA] true true`)) // remove all non-existent farms and expect an error cmd = []string{"farm", "rm", "foo", "bar"} @@ -287,12 +281,6 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) 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 cmd = []string{"farm", "rm", "--all"} session = podmanTest.Podman(cmd) @@ -300,10 +288,10 @@ var _ = Describe("podman farm", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(ContainSubstring("All farms have been deleted")) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Farms.Default).Should(BeEmpty()) - Expect(cfg.Farms.List).Should(BeEmpty()) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("")) }) }) }) diff --git a/test/e2e/image_scp_test.go b/test/e2e/image_scp_test.go index 28ad8a44e0..e42119e278 100644 --- a/test/e2e/image_scp_test.go +++ b/test/e2e/image_scp_test.go @@ -4,7 +4,6 @@ import ( "os" "path/filepath" - "github.com/containers/common/pkg/config" . "github.com/containers/podman/v4/test/utils" "github.com/containers/storage/pkg/homedir" . "github.com/onsi/ginkgo/v2" @@ -13,7 +12,7 @@ import ( var _ = Describe("podman image scp", func() { - BeforeEach(setupEmptyContainersConf) + BeforeEach(setupConnectionsConf) It("podman image scp bogus image", func() { scp := podmanTest.Podman([]string{"image", "scp", "FOOBAR"}) @@ -34,15 +33,6 @@ var _ = Describe("podman image scp", func() { session.WaitWithDefaultTimeout() 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.WaitWithDefaultTimeout() // exit with error because we cannot make an actual ssh connection diff --git a/test/e2e/system_connection_test.go b/test/e2e/system_connection_test.go index 01dedcd485..76bffb0d22 100644 --- a/test/e2e/system_connection_test.go +++ b/test/e2e/system_connection_test.go @@ -8,7 +8,6 @@ import ( "os/user" "path/filepath" - "github.com/containers/common/pkg/config" . "github.com/containers/podman/v4/test/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -16,18 +15,24 @@ import ( . "github.com/onsi/gomega/gexec" ) -func setupEmptyContainersConf() { +func setupConnectionsConf() { // 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) Expect(err).ToNot(HaveOccurred()) f.Close() 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() { - BeforeEach(setupEmptyContainersConf) + BeforeEach(setupConnectionsConf) Context("without running API service", func() { It("add ssh://", func() { @@ -42,14 +47,10 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg).Should(HaveActiveService("QA")) - Expect(cfg).Should(VerifyService( - "QA", - "ssh://root@podman.test:2222/run/podman/podman.sock", - "~/.ssh/id_rsa", - )) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true")) cmd = []string{"system", "connection", "rename", "QA", @@ -59,7 +60,10 @@ var _ = Describe("podman system connection", func() { session.WaitWithDefaultTimeout() 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() { @@ -74,11 +78,10 @@ var _ = Describe("podman system connection", func() { // stderr will probably warn (ENOENT or EACCESS) about socket // but it's too unreliable to test for. - Expect(config.ReadCustomConfig()).Should(VerifyService( - "QA-UDS", - "unix:///run/podman/podman.sock", - "", - )) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("QA-UDS unix:///run/podman/podman.sock true true")) cmd = []string{"system", "connection", "add", "QA-UDS1", @@ -90,12 +93,12 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(Exit(0)) Expect(session.Out.Contents()).Should(BeEmpty()) - Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA-UDS")) - Expect(config.ReadCustomConfig()).Should(VerifyService( - "QA-UDS1", - "unix:///run/user/podman/podman.sock", - "", - )) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + 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() { @@ -108,18 +111,13 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - Expect(config.ReadCustomConfig()).Should(VerifyService( - "QA-TCP", - "tcp://localhost:8888", - "", - )) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("QA-TCP tcp://localhost:8888 true true")) }) 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", "--default", "--identity", "~/.ssh/id_rsa", @@ -132,15 +130,14 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg).Should(HaveActiveService("QA")) - Expect(cfg).Should(VerifyService( - "QA", - "ssh://root@podman.test:2222/run/podman/podman.sock", - "~/.ssh/id_rsa", - )) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA"})) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true")) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("farm1 [QA] true true")) }) It("add to existing farm", func() { @@ -151,10 +148,6 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) 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", "--default", "--identity", "~/.ssh/id_rsa", @@ -167,22 +160,17 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg).Should(HaveActiveService("QA")) - Expect(cfg).Should(VerifyService( - "QA", - "ssh://root@podman.test:2222/run/podman/podman.sock", - "~/.ssh/id_rsa", - )) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("empty-farm", []string{"QA"})) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true")) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("empty-farm [QA] true true")) }) 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", "--default", "--identity", "~/.ssh/id_rsa", @@ -195,15 +183,14 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg).Should(HaveActiveService("QA")) - Expect(cfg).Should(VerifyService( - "QA", - "ssh://root@podman.test:2222/run/podman/podman.sock", - "~/.ssh/id_rsa", - )) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{"QA"})) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("QA ssh://root@podman.test:2222/run/podman/podman.sock ~/.ssh/id_rsa true true")) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("farm1 [QA] true true")) // Remove the QA connection session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"}) @@ -211,11 +198,14 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).Should(BeEmpty()) - Expect(cfg.Engine.ServiceDestinations).Should(BeEmpty()) - Expect(cfg.Farms.List).Should(HaveKeyWithValue("farm1", []string{})) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("")) + session = podmanTest.Podman(farmListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("farm1 [] true true")) }) It("remove", func() { @@ -235,10 +225,10 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).Should(BeEmpty()) - Expect(cfg.Engine.ServiceDestinations).Should(BeEmpty()) + session = podmanTest.Podman(systemConnectionListCmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(Equal("")) } }) @@ -261,6 +251,7 @@ var _ = Describe("podman system connection", func() { session = podmanTest.Podman([]string{"system", "connection", "list"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) + Expect(session.OutputToStringArray()).To(HaveLen(1)) }) It("default", func() { @@ -282,19 +273,18 @@ var _ = Describe("podman system connection", func() { Expect(session).Should(ExitCleanly()) 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"} session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) 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() { @@ -382,12 +372,12 @@ var _ = Describe("podman system connection", func() { Path: fmt.Sprintf("/run/user/%s/podman/podman.sock", u.Uid), } - Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA")) - Expect(config.ReadCustomConfig()).Should(VerifyService( - "QA", - uri.String(), - filepath.Join(u.HomeDir, ".ssh", "id_ed25519"), - )) + cmd = exec.Command(podmanTest.RemotePodmanBinary, systemConnectionListCmd...) + lsSession, err := Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).ToNot(HaveOccurred()) + lsSession.Wait(DefaultWaitTimeout) + 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")) }) }) }) diff --git a/test/system/272-system-connection.bats b/test/system/272-system-connection.bats index 2824394fd6..f01d027fba 100644 --- a/test/system/272-system-connection.bats +++ b/test/system/272-system-connection.bats @@ -51,7 +51,7 @@ function _run_podman_remote() { # Very basic test, does not actually connect at any time @test "podman system connection - basic add / ls / remove" { run_podman system connection ls - is "$output" "Name URI Identity Default" \ + is "$output" "Name URI Identity Default ReadWrite" \ "system connection ls: no connections" 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 system connection ls is "$output" \ - ".*$c1[ ]\+tcp://localhost:12345[ ]\+true -$c2[ ]\+tcp://localhost:54321[ ]\+false" \ + ".*$c1[ ]\+tcp://localhost:12345[ ]\+true[ ]\+true +$c2[ ]\+tcp://localhost:54321[ ]\+false[ ]\+true" \ "system connection ls" run_podman system connection ls -q is "$(echo $(sort <<<$output))" \ @@ -75,14 +75,14 @@ $c2[ ]\+tcp://localhost:54321[ ]\+false" \ run_podman context use $c2 run_podman system connection ls is "$output" \ - ".*$c1[ ]\+tcp://localhost:12345[ ]\+false -$c2[ ]\+tcp://localhost:54321[ ]\+true" \ + ".*$c1[ ]\+tcp://localhost:12345[ ]\+false[ ]\+true +$c2[ ]\+tcp://localhost:54321[ ]\+true[ ]\+true" \ "system connection ls" # Remove default connection; the remaining one should still not be default run_podman system connection rm $c2 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)" run_podman context rm $c1 diff --git a/test/utils/matchers.go b/test/utils/matchers.go index 4e0a38b0b6..26050d1835 100644 --- a/test/utils/matchers.go +++ b/test/utils/matchers.go @@ -3,116 +3,12 @@ package utils import ( "encoding/json" "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/gexec" - "github.com/onsi/gomega/matchers" "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 { types.GomegaMatcher Expected int diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 84deb00418..d52187a00c 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -9,11 +9,9 @@ import ( "runtime" "strings" - "github.com/BurntSushi/toml" "github.com/containers/common/internal/attributedstring" "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/capabilities" - "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/unshare" units "github.com/docker/go-units" selinux "github.com/opencontainers/selinux/go-selinux" @@ -667,9 +665,9 @@ type MachineConfig struct { // FarmConfig represents the "farm" TOML config tables type FarmConfig struct { // 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 map[string][]string `toml:"list,omitempty"` + List map[string][]string `json:",omitempty" toml:"list,omitempty"` } // Destination represents destination for remote service @@ -678,10 +676,10 @@ type Destination struct { URI string `toml:"uri"` // 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 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 @@ -1008,82 +1006,6 @@ func IsValidDeviceMode(mode string) bool { 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 // This function is meant to be used for long-running processes that need to reload potential changes made to // the cached containers.conf files. diff --git a/vendor/github.com/containers/common/pkg/config/config_darwin.go b/vendor/github.com/containers/common/pkg/config/config_darwin.go index 6ff13c15db..9982d7995b 100644 --- a/vendor/github.com/containers/common/pkg/config/config_darwin.go +++ b/vendor/github.com/containers/common/pkg/config/config_darwin.go @@ -1,9 +1,5 @@ package config -import ( - "os" -) - const ( // OverrideContainersConfig holds the default config path overridden by the root user OverrideContainersConfig = "/etc/" + _configPath @@ -16,18 +12,6 @@ const ( 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{ // Relative to the binary directory "$BINDIR/../libexec/podman", diff --git a/vendor/github.com/containers/common/pkg/config/config_freebsd.go b/vendor/github.com/containers/common/pkg/config/config_freebsd.go index 903f0b47ce..5b7f55a7bc 100644 --- a/vendor/github.com/containers/common/pkg/config/config_freebsd.go +++ b/vendor/github.com/containers/common/pkg/config/config_freebsd.go @@ -1,9 +1,5 @@ package config -import ( - "os" -) - const ( // OverrideContainersConfig holds the default config path overridden by the root user OverrideContainersConfig = "/usr/local/etc/" + _configPath @@ -16,18 +12,6 @@ const ( 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{ "/usr/local/bin", "/usr/local/libexec/podman", diff --git a/vendor/github.com/containers/common/pkg/config/config_linux.go b/vendor/github.com/containers/common/pkg/config/config_linux.go index 4ce5d03209..66d193467a 100644 --- a/vendor/github.com/containers/common/pkg/config/config_linux.go +++ b/vendor/github.com/containers/common/pkg/config/config_linux.go @@ -1,9 +1,6 @@ package config import ( - "os" - - "github.com/containers/storage/pkg/unshare" selinux "github.com/opencontainers/selinux/go-selinux" ) @@ -23,31 +20,6 @@ func selinuxEnabled() bool { 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{ "/usr/local/libexec/podman", "/usr/local/lib/podman", diff --git a/vendor/github.com/containers/common/pkg/config/config_unix.go b/vendor/github.com/containers/common/pkg/config/config_unix.go new file mode 100644 index 0000000000..bd1652787c --- /dev/null +++ b/vendor/github.com/containers/common/pkg/config/config_unix.go @@ -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 +} diff --git a/vendor/github.com/containers/common/pkg/config/config_windows.go b/vendor/github.com/containers/common/pkg/config/config_windows.go index 67f0aab238..9011687e4d 100644 --- a/vendor/github.com/containers/common/pkg/config/config_windows.go +++ b/vendor/github.com/containers/common/pkg/config/config_windows.go @@ -17,15 +17,9 @@ const ( _typeBind = "bind" ) -// podman remote clients on windows 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 os.Getenv("APPDATA") + "\\containers\\containers.conf", nil -} - -func ifRootlessConfigPath() (string, error) { +// userConfigPath returns the path to the users local config that is +// not shared with other users. It uses $APPDATA/containers... +func userConfigPath() (string, error) { return os.Getenv("APPDATA") + "\\containers\\containers.conf", nil } diff --git a/vendor/github.com/containers/common/pkg/config/connections.go b/vendor/github.com/containers/common/pkg/config/connections.go new file mode 100644 index 0000000000..6a48a2052f --- /dev/null +++ b/vendor/github.com/containers/common/pkg/config/connections.go @@ -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 +} diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index c638d35b35..e1e624c1f7 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -10,7 +10,8 @@ # locations in the following order: # 1. /usr/share/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 # previous containers.conf settings, or the default settings. diff --git a/vendor/github.com/containers/common/pkg/config/new.go b/vendor/github.com/containers/common/pkg/config/new.go index 64ddf47166..9de3d86258 100644 --- a/vendor/github.com/containers/common/pkg/config/new.go +++ b/vendor/github.com/containers/common/pkg/config/new.go @@ -21,7 +21,6 @@ var ( ) const ( - // FIXME: update code base and tests to use the two constants below. containersConfEnv = "CONTAINERS_CONF" containersConfOverrideEnv = containersConfEnv + "_OVERRIDE" ) @@ -79,15 +78,34 @@ func newLocked(options *Options) (*Config, error) { if err != nil { return nil, fmt.Errorf("finding config on system: %w", err) } + // connectionsPath, err := connectionsConfigFile() + // if err != nil { + // return nil, err + // } + 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. // Each config file that specified fields, will override the // 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) } logrus.Debugf("Merged system config %q", path) 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() @@ -115,7 +133,7 @@ func newLocked(options *Options) (*Config, error) { } // readConfigFromFile reads in container config in the specified // 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) } logrus.Debugf("Merged additional config %q", add) @@ -157,12 +175,8 @@ func systemConfigs() (configs []string, finalErr error) { } return append(configs, path), nil } - if _, err := os.Stat(DefaultContainersConfig); err == nil { - configs = append(configs, DefaultContainersConfig) - } - if _, err := os.Stat(OverrideContainersConfig); err == nil { - configs = append(configs, OverrideContainersConfig) - } + configs = append(configs, DefaultContainersConfig) + configs = append(configs, OverrideContainersConfig) var err error configs, err = addConfigs(OverrideContainersConfig+".d", configs) @@ -170,18 +184,14 @@ func systemConfigs() (configs []string, finalErr error) { return nil, err } - path, err := ifRootlessConfigPath() + path, err := userConfigPath() if err != nil { return nil, err } - if path != "" { - if _, err := os.Stat(path); err == nil { - configs = append(configs, path) - } - configs, err = addConfigs(path+".d", configs) - if err != nil { - return nil, err - } + configs = append(configs, path) + configs, err = addConfigs(path+".d", configs) + if err != nil { + return nil, err } 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 // 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. -func readConfigFromFile(path string, config *Config) error { +func readConfigFromFile(path string, config *Config, ignoreErrNotExist bool) error { logrus.Tracef("Reading configuration file %q", path) meta, err := toml.DecodeFile(path, config) if err != nil { + if ignoreErrNotExist && errors.Is(err, fs.ErrNotExist) { + return nil + } return fmt.Errorf("decode configuration %v: %w", path, err) } keys := meta.Undecoded() diff --git a/vendor/github.com/containers/common/pkg/manifests/manifests.go b/vendor/github.com/containers/common/pkg/manifests/manifests.go index c72dc5f5a1..23245e07b6 100644 --- a/vendor/github.com/containers/common/pkg/manifests/manifests.go +++ b/vendor/github.com/containers/common/pkg/manifests/manifests.go @@ -19,6 +19,7 @@ type List interface { Remove(instanceDigest digest.Digest) error SetURLs(instanceDigest digest.Digest, urls []string) error URLs(instanceDigest digest.Digest) ([]string, error) + ClearAnnotations(instanceDigest *digest.Digest) error SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error Annotations(instanceDigest *digest.Digest) (map[string]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, }) - ociv1platform := v1.Platform{ + ociv1platform := &v1.Platform{ Architecture: architecture, OS: osName, OSVersion: osVersion, OSFeatures: osFeatures, 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{ MediaType: manifestType, Size: manifestSize, Digest: manifestDigest, - Platform: &ociv1platform, + Platform: ociv1platform, }) return nil @@ -170,7 +174,13 @@ func (l *list) SetURLs(instanceDigest digest.Digest, urls []string) error { return err } oci.URLs = append([]string{}, urls...) + if len(oci.URLs) == 0 { + oci.URLs = nil + } docker.URLs = append([]string{}, urls...) + if len(docker.URLs) == 0 { + docker.URLs = nil + } return nil } @@ -183,7 +193,24 @@ func (l *list) URLs(instanceDigest digest.Digest) ([]string, error) { 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. func (l *list) SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error { a := &l.oci.Annotations @@ -194,10 +221,15 @@ func (l *list) SetAnnotations(instanceDigest *digest.Digest, annotations map[str } a = &oci.Annotations } - (*a) = make(map[string]string) + if *a == nil { + (*a) = make(map[string]string) + } for k, v := range annotations { (*a)[k] = v } + if len(*a) == 0 { + *a = nil + } return nil } @@ -230,7 +262,13 @@ func (l *list) SetOS(instanceDigest digest.Digest, os string) error { return err } docker.Platform.OS = os + if oci.Platform == nil { + oci.Platform = &v1.Platform{} + } 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 } @@ -240,7 +278,11 @@ func (l *list) OS(instanceDigest digest.Digest) (string, error) { if err != nil { 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. @@ -254,7 +296,13 @@ func (l *list) SetArchitecture(instanceDigest digest.Digest, arch string) error return err } docker.Platform.Architecture = arch + if oci.Platform == nil { + oci.Platform = &v1.Platform{} + } 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 } @@ -264,7 +312,11 @@ func (l *list) Architecture(instanceDigest digest.Digest) (string, error) { if err != nil { 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. @@ -278,7 +330,13 @@ func (l *list) SetOSVersion(instanceDigest digest.Digest, osVersion string) erro return err } docker.Platform.OSVersion = osVersion + if oci.Platform == nil { + oci.Platform = &v1.Platform{} + } 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 } @@ -288,7 +346,11 @@ func (l *list) OSVersion(instanceDigest digest.Digest) (string, error) { if err != nil { 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. @@ -302,7 +364,13 @@ func (l *list) SetVariant(instanceDigest digest.Digest, variant string) error { return err } docker.Platform.Variant = variant + if oci.Platform == nil { + oci.Platform = &v1.Platform{} + } 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 } @@ -312,7 +380,11 @@ func (l *list) Variant(instanceDigest digest.Digest) (string, error) { if err != nil { 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. @@ -323,6 +395,9 @@ func (l *list) SetFeatures(instanceDigest digest.Digest, features []string) erro return err } docker.Platform.Features = append([]string{}, features...) + if len(docker.Platform.Features) == 0 { + docker.Platform.Features = nil + } // no OCI equivalent return nil } @@ -348,7 +423,16 @@ func (l *list) SetOSFeatures(instanceDigest digest.Digest, osFeatures []string) return err } docker.Platform.OSFeatures = append([]string{}, osFeatures...) + if oci.Platform == nil { + oci.Platform = &v1.Platform{} + } 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 } @@ -358,7 +442,11 @@ func (l *list) OSFeatures(instanceDigest digest.Digest) ([]string, error) { if err != nil { 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. diff --git a/vendor/github.com/containers/common/pkg/ssh/connection_golang.go b/vendor/github.com/containers/common/pkg/ssh/connection_golang.go index adc22d94cb..d12150dbf3 100644 --- a/vendor/github.com/containers/common/pkg/ssh/connection_golang.go +++ b/vendor/github.com/containers/common/pkg/ssh/connection_golang.go @@ -53,32 +53,32 @@ func golangConnectionCreate(options ConnectionCreateOptions) error { dst.URI += uri.Path } - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - if cfg.Engine.ServiceDestinations == nil { - cfg.Engine.ServiceDestinations = map[string]config.Destination{ - options.Name: *dst, - } - cfg.Engine.ActiveService = options.Name - } else { - cfg.Engine.ServiceDestinations[options.Name] = *dst - } - - // Create or update an existing farm with the connection being added - if options.Farm != "" { - if len(cfg.Farms.List) == 0 { - cfg.Farms.Default = options.Farm - } - if val, ok := cfg.Farms.List[options.Farm]; ok { - cfg.Farms.List[options.Farm] = append(val, options.Name) + // TODO this really should not live here, it must be in podman where we write the other connections as well. + // This duplicates the code for no reason and I have a really hard time to make any sense of why this code + // was added in the first place. + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if cfg.Connection.Connections == nil { + cfg.Connection.Connections = map[string]config.Destination{ + options.Name: *dst, + } + cfg.Connection.Default = options.Name } else { - cfg.Farms.List[options.Farm] = []string{options.Name} + 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 golangConnectionDial(options ConnectionDialOptions) (*ConnectionDialReport, error) { diff --git a/vendor/github.com/containers/common/pkg/ssh/connection_native.go b/vendor/github.com/containers/common/pkg/ssh/connection_native.go index c725cb27da..fd73095bff 100644 --- a/vendor/github.com/containers/common/pkg/ssh/connection_native.go +++ b/vendor/github.com/containers/common/pkg/ssh/connection_native.go @@ -72,24 +72,32 @@ func nativeConnectionCreate(options ConnectionCreateOptions) error { return fmt.Errorf("remote podman %q failed to report its UDS socket", uri.Host) } - cfg, err := config.ReadCustomConfig() - if err != nil { - return err - } - if options.Default { - cfg.Engine.ActiveService = options.Name - } - - if cfg.Engine.ServiceDestinations == nil { - cfg.Engine.ServiceDestinations = map[string]config.Destination{ - options.Name: *dst, + // TODO this really should not live here, it must be in podman where we write the other connections as well. + // This duplicates the code for no reason and I have a really hard time to make any sense of why this code + // was added in the first place. + return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error { + if cfg.Connection.Connections == nil { + cfg.Connection.Connections = map[string]config.Destination{ + options.Name: *dst, + } + cfg.Connection.Default = options.Name + } else { + cfg.Connection.Connections[options.Name] = *dst } - cfg.Engine.ActiveService = options.Name - } else { - cfg.Engine.ServiceDestinations[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) { diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 00d67999dc..3cd4b6e10a 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,9 +6,9 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 29 + VersionMinor = 30 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 2 + VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "-dev" diff --git a/vendor/github.com/containers/storage/Makefile b/vendor/github.com/containers/storage/Makefile index 77189d49ea..8461c09019 100644 --- a/vendor/github.com/containers/storage/Makefile +++ b/vendor/github.com/containers/storage/Makefile @@ -41,7 +41,7 @@ containers-storage: ## build using gc on the host $(GO) build -compiler gc $(BUILDFLAGS) ./cmd/containers-storage 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 diff --git a/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go b/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go index f3414720ca..556aa360c0 100644 --- a/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go +++ b/vendor/github.com/containers/storage/pkg/chunked/storage_linux.go @@ -73,11 +73,9 @@ type chunkedDiffer struct { zstdReader *zstd.Decoder rawReader io.Reader - // contentDigest is the digest of the uncompressed content - // (diffID) when the layer is fully retrieved. If the layer - // is not fully retrieved, instead of using the digest of the - // uncompressed content, it refers to the digest of the TOC. - contentDigest digest.Digest + // tocDigest is the digest of the TOC document when the layer + // is partially pulled. + tocDigest digest.Digest // convertedToZstdChunked is set to true if the layer needs to // 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 } - contentDigest, err := digest.Parse(annotations[internal.ManifestChecksumKey]) + tocDigest, err := digest.Parse(annotations[internal.ManifestChecksumKey]) if err != nil { 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{ fsVerityDigests: make(map[string]string), blobSize: blobSize, - contentDigest: contentDigest, + tocDigest: tocDigest, copyBuffer: makeCopyBuffer(), fileType: fileTypeZstdChunked, layersCache: layersCache, @@ -322,7 +320,7 @@ func makeEstargzChunkedDiffer(ctx context.Context, store storage.Store, blobSize return nil, err } - contentDigest, err := digest.Parse(annotations[estargz.TOCJSONDigestAnnotation]) + tocDigest, err := digest.Parse(annotations[estargz.TOCJSONDigestAnnotation]) if err != nil { 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{ fsVerityDigests: make(map[string]string), blobSize: blobSize, - contentDigest: contentDigest, + tocDigest: tocDigest, copyBuffer: makeCopyBuffer(), fileType: fileTypeEstargz, 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 := c.stream + var uncompressedDigest digest.Digest + tocDigest := c.tocDigest + if c.convertToZstdChunked { fd, err := unix.Open(dest, unix.O_TMPFILE|unix.O_RDWR|unix.O_CLOEXEC, 0o600) if err != nil { @@ -1663,13 +1664,13 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff c.fileType = fileTypeZstdChunked c.manifest = manifest 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 // the file was generated by us and the digest for each file was already computed, no need to validate it again. 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{ @@ -1698,7 +1699,8 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff Artifacts: map[string]interface{}{ tocKey: toc, }, - TOCDigest: c.contentDigest, + TOCDigest: tocDigest, + UncompressedDigest: uncompressedDigest, } if !parseBooleanPullOption(c.storeOpts, "enable_partial_images", false) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 5b46919c3f..d22f7eca45 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -167,7 +167,7 @@ github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/pkg/volumes 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 github.com/containers/common/internal/attributedstring github.com/containers/common/libimage @@ -236,7 +236,7 @@ github.com/containers/conmon/runner/config # github.com/containers/gvisor-tap-vsock v0.7.2 ## explicit; go 1.20 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 github.com/containers/image/v5/copy 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/proc 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 github.com/containers/storage github.com/containers/storage/drivers