Skip to content

Commit

Permalink
Merge pull request containers#19358 from umohnani8/buildfarm-2
Browse files Browse the repository at this point in the history
Add phase 1 of podman farm subcommands
  • Loading branch information
openshift-merge-robot authored Aug 10, 2023
2 parents 970976a + bcebcad commit 14e290a
Show file tree
Hide file tree
Showing 63 changed files with 3,728 additions and 13,261 deletions.
18 changes: 18 additions & 0 deletions cmd/podman/common/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,24 @@ func AutocompleteInspect(cmd *cobra.Command, args []string, toComplete string) (
return suggestions, cobra.ShellCompDirectiveNoFileComp
}

func AutoCompleteFarms(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
suggestions := []string{}
cfg, err := config.ReadCustomConfig()
if err != nil {
cobra.CompErrorln(err.Error())
return nil, cobra.ShellCompDirectiveNoFileComp
}

for k := range cfg.Farms.List {
suggestions = append(suggestions, k)
}

return suggestions, cobra.ShellCompDirectiveNoFileComp
}

// AutocompleteSystemConnections - Autocomplete system connections.
func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
Expand Down
80 changes: 80 additions & 0 deletions cmd/podman/farm/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package farm

import (
"fmt"

"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/pkg/util"
"github.com/spf13/cobra"
)

var (
farmCreateDescription = `Create a new farm with connections added via podman system connection add.
The "podman system connection add --farm" command can be used to add a new connection to a new or existing farm.`

createCommand = &cobra.Command{
Use: "create [options] NAME [CONNECTIONS...]",
Args: cobra.MinimumNArgs(1),
Short: "Create a new farm",
Long: farmCreateDescription,
RunE: create,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman farm create myfarm connection1
podman farm create myfarm`,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: createCommand,
Parent: farmCmd,
})
}

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 util.StringInSlice(c, cfg.Farms.List[farmName]) {
// 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)
}
}

// If this is the first farm being created, set it as the default farm
if len(cfg.Farms.List) == 1 {
cfg.Farms.Default = farmName
}

err = cfg.Write()
if err != nil {
return err
}

fmt.Printf("Farm %q created\n", farmName)
return nil
}
49 changes: 49 additions & 0 deletions cmd/podman/farm/farm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package farm

import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/spf13/cobra"
)

var (
// Command: podman _farm_
farmCmd = &cobra.Command{
Use: "farm",
Short: "Farm out builds to remote machines",
Long: "Farm out builds to remote machines that podman can connect to via podman system connection",
RunE: validate.SubCommandExists,
}
)

var (
// Temporary struct to hold cli values.
farmOpts = struct {
Farm string
Local bool
}{}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: farmCmd,
})
farmCmd.Hidden = true

flags := farmCmd.Flags()
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.StringVarP(&farmOpts.Farm, farmFlagName, "f", defaultFarm, "Farm to use for builds")

localFlagName := "local"
// Default for local is true and hide this flag for the remote use case
if !registry.IsRemote() {
flags.BoolVarP(&farmOpts.Local, localFlagName, "l", true, "Build image on local machine including on farm nodes")
}
}
119 changes: 119 additions & 0 deletions cmd/podman/farm/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package farm

import (
"fmt"
"os"
"sort"

"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"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/spf13/cobra"
)

var (
farmLsDescription = `podman farm ls
List all available farms. The output of the farms can be filtered
and the output format can be changed to JSON or a user specified Go template.`
lsCommand = &cobra.Command{
Use: "list [options]",
Aliases: []string{"ls"},
Args: validate.NoArgs,
Short: "List all existing farms",
Long: farmLsDescription,
RunE: list,
ValidArgsFunction: completion.AutocompleteNone,
}

// Temporary struct to hold cli values.
lsOpts = struct {
Format string
}{}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: lsCommand,
Parent: farmCmd,
})
flags := lsCommand.Flags()

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
}

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)
}

sort.Slice(rows, func(i, j int) bool {
return rows[i].Name < rows[j].Name
})

rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()

if report.IsJSON(format) {
buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ")
if err == nil {
fmt.Println(string(buf))
}
return err
}

if format != "" {
rpt, err = rpt.Parse(report.OriginUser, format)
} else {
rpt, err = rpt.Parse(report.OriginPodman,
"{{range .}}{{.Name}}\t{{.Connections}}\t{{.Default}}\n{{end -}}")
}
if err != nil {
return err
}

if rpt.RenderHeaders {
err = rpt.Execute([]map[string]string{{
"Default": "Default",
"Connections": "Connections",
"Name": "Name",
}})
if err != nil {
return err
}
}

return rpt.Execute(rows)
}
99 changes: 99 additions & 0 deletions cmd/podman/farm/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package farm

import (
"errors"
"fmt"

"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
farmRmDescription = `Remove one or more existing farms.`
rmCommand = &cobra.Command{
Use: "remove [options] [FARM...]",
Aliases: []string{"rm"},
Short: "Remove one or more farms",
Long: farmRmDescription,
RunE: rm,
ValidArgsFunction: common.AutoCompleteFarms,
Example: `podman farm rm myfarm1 myfarm2
podman farm rm --all`,
}

// Temporary struct to hold cli values.
rmOpts = struct {
All bool
}{}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: rmCommand,
Parent: farmCmd,
})
flags := rmCommand.Flags()
flags.BoolVarP(&rmOpts.All, "all", "a", false, "Remove all farms")
}

func rm(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig()
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 exists; 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)
}
return nil
}
Loading

0 comments on commit 14e290a

Please sign in to comment.