Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Setup Go
uses: actions/setup-go@v6
Expand All @@ -30,7 +30,7 @@ jobs:
cache: false

- name: Lint
uses: golangci/golangci-lint-action@v8
uses: golangci/golangci-lint-action@v9
with:
args: --build-tags integration --timeout=5m

Expand Down Expand Up @@ -58,7 +58,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Setup Go
uses: actions/setup-go@v6
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ In addition to the standard API services, there are also admin services that req

You can access help for every service and command by using `--help` or `-h`. If you encounter any issues not covered in the help prompt, or if you have suggestions for improvement, please feel free to [contact us](mailto:support@metal-stack.io) or open an issue in this repository. Your feedback is greatly appreciated!

A list of all available services (excluding admin topics). For their associated commands, arguments and flags visit the correct [documentation](./docs/metal.md).
A list of all available services (excluding admin topics). For their associated commands, arguments and flags visit the [metalctlv2 documentation](./docs/metalctlv2.md).

| Entity | Description | Documentation |
|---------------|------------------------------------------------------------|-------------------------------------------------------|
Expand Down
8 changes: 7 additions & 1 deletion cmd/admin/v1/commands.go → cmd/admin/v2/commands.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package v1
package v2

import (
"github.com/metal-stack/cli/cmd/config"
Expand All @@ -17,7 +17,13 @@ func AddCmds(cmd *cobra.Command, c *config.Config) {
adminCmd.AddCommand(newImageCmd(c))
adminCmd.AddCommand(newIPCmd(c))
adminCmd.AddCommand(newNetworkCmd(c))
adminCmd.AddCommand(newSizeCmd(c))
adminCmd.AddCommand(newTenantCmd(c))
adminCmd.AddCommand(newTokenCmd(c))
adminCmd.AddCommand(newProjectCmd(c))
adminCmd.AddCommand(newVPNCmd(c))
adminCmd.AddCommand(newMachineCmd(c))
adminCmd.AddCommand(newTaskCmd(c))

cmd.AddCommand(adminCmd)
}
2 changes: 1 addition & 1 deletion cmd/admin/v1/image.go → cmd/admin/v2/image.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package v1
package v2

import (
"fmt"
Expand Down
6 changes: 3 additions & 3 deletions cmd/admin/v1/ip.go → cmd/admin/v2/ip.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package v1
package v2

import (
adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2"
Expand All @@ -21,10 +21,10 @@ func newIPCmd(c *config.Config) *cobra.Command {

cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.IP]{
BinaryName: config.BinaryName,
GenericCLI: genericcli.NewGenericCLI[any, any, *apiv2.IP](w).WithFS(c.Fs),
GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs),
Singular: "ip",
Plural: "ips",
Description: "an ip address of metal-stack.io",
Description: "manage ip addresses",
Sorter: sorters.IPSorter(),
DescribePrinter: func() printers.Printer { return c.DescribePrinter },
ListPrinter: func() printers.Printer { return c.ListPrinter },
Expand Down
191 changes: 191 additions & 0 deletions cmd/admin/v2/machine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package v2

import (
"fmt"

adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2"
apiv2 "github.com/metal-stack/api/go/metalstack/api/v2"
"github.com/metal-stack/cli/cmd/config"
"github.com/metal-stack/cli/cmd/sorters"
"github.com/metal-stack/metal-lib/pkg/genericcli"
"github.com/metal-stack/metal-lib/pkg/genericcli/printers"
"github.com/metal-stack/metal-lib/pkg/pointer"
"github.com/metal-stack/metal-lib/pkg/tag"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

type machine struct {
c *config.Config
}

func newMachineCmd(c *config.Config) *cobra.Command {
w := &machine{
c: c,
}

cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.Machine]{
BinaryName: config.BinaryName,
GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs),
Singular: "machine",
Plural: "machines",
Description: "manage machines",
Sorter: sorters.MachineSorter(),
DescribePrinter: func() printers.Printer { return c.DescribePrinter },
ListPrinter: func() printers.Printer { return c.ListPrinter },
ListCmdMutateFn: func(cmd *cobra.Command) {
cmd.Flags().StringP("project", "p", "", "project from where machines should be listed")

genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion))
},
DescribeCmdMutateFn: func(cmd *cobra.Command) {
cmd.Flags().StringP("project", "p", "", "project of the machine")

genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion))
},
ValidArgsFn: c.Completion.MachineListCompletion,
}

bmcCommandCmd := &cobra.Command{
Use: "bmc-command",
Short: "send a command to the bmc of a machine",
RunE: func(cmd *cobra.Command, args []string) error {
return w.bmcCommand()
},
}
bmcCommandCmd.Flags().String("id", "", "id of the machine where the command should be sent to")
bmcCommandCmd.Flags().String("command", "", "the actual command to send to the machine")
bmcCommandCmd.RegisterFlagCompletionFunc("id", c.Completion.MachineListCompletion)
bmcCommandCmd.RegisterFlagCompletionFunc("command", c.Completion.BMCCommandListCompletion)
genericcli.Must(bmcCommandCmd.MarkFlagRequired("id"))
genericcli.Must(bmcCommandCmd.MarkFlagRequired("command"))

return genericcli.NewCmds(cmdsConfig, bmcCommandCmd)
}

func (c *machine) bmcCommand() error {
ctx, cancel := c.c.NewRequestContext()
defer cancel()

commandString := viper.GetString("command")

cmd, ok := apiv2.MachineBMCCommand_value[commandString]
if !ok {
return fmt.Errorf("unknown command: %s", commandString)
}
_, err := c.c.Client.Adminv2().Machine().BMCCommand(ctx, &adminv2.MachineServiceBMCCommandRequest{
Uuid: viper.GetString("id"),
Command: apiv2.MachineBMCCommand(cmd),
})
if err != nil {
return err
}
return err
}

func (c *machine) updateFromCLI(args []string) (any, error) {
panic("unimplemented")
}

func (c *machine) Create(rq any) (*apiv2.Machine, error) {
panic("unimplemented")
}

func (c *machine) Delete(id string) (*apiv2.Machine, error) {
panic("unimplemented")
}

func (c *machine) Get(id string) (*apiv2.Machine, error) {
ctx, cancel := c.c.NewRequestContext()
defer cancel()

resp, err := c.c.Client.Adminv2().Machine().Get(ctx, &adminv2.MachineServiceGetRequest{
Uuid: id,
})
if err != nil {
return nil, err
}

return resp.Machine, nil
}

func (c *machine) List() ([]*apiv2.Machine, error) {
ctx, cancel := c.c.NewRequestContext()
defer cancel()

resp, err := c.c.Client.Adminv2().Machine().List(ctx, &adminv2.MachineServiceListRequest{
Query: &apiv2.MachineQuery{
Uuid: pointer.PointerOrNil(viper.GetString("id")),
Name: pointer.PointerOrNil(viper.GetString("name")),
Partition: pointer.PointerOrNil(viper.GetString("partition")),
Size: pointer.PointerOrNil(viper.GetString("size")),
Rack: pointer.PointerOrNil(viper.GetString("rack")),
Labels: &apiv2.Labels{
Labels: tag.NewTagMap(viper.GetStringSlice("labels")),
},
Allocation: &apiv2.MachineAllocationQuery{
Uuid: pointer.PointerOrNil(viper.GetString("allocation-uuid")),
Name: pointer.PointerOrNil(viper.GetString("allocation-name")),
Project: pointer.PointerOrNil(viper.GetString("project")),
Image: pointer.PointerOrNil(viper.GetString("image")),
FilesystemLayout: pointer.PointerOrNil(viper.GetString("file-system-layout-id")),
Hostname: pointer.PointerOrNil(viper.GetString("hostname")),
// AllocationType: &0,
Labels: &apiv2.Labels{
Labels: tag.NewTagMap(viper.GetStringSlice("allocation-labels")),
},
// Vpn: &apiv2.MachineVPN{}, these query fields are no pointers and somehow seem wrong? how to search for vpn key?
},
Network: &apiv2.MachineNetworkQuery{},
Nic: &apiv2.MachineNicQuery{},
Disk: &apiv2.MachineDiskQuery{
Names: viper.GetStringSlice("disk-names"),
// Sizes:
},
Bmc: &apiv2.MachineBMCQuery{
Address: pointer.PointerOrNil(viper.GetString("bmc-address")),
Mac: pointer.PointerOrNil(viper.GetString("bmc-mac")),
User: pointer.PointerOrNil(viper.GetString("bmc-user")),
Interface: pointer.PointerOrNil(viper.GetString("bmc-interface")),
},
Fru: &apiv2.MachineFRUQuery{
ChassisPartNumber: pointer.PointerOrNil(viper.GetString("chassis-part-number")),
ChassisPartSerial: pointer.PointerOrNil(viper.GetString("chassis-part-serial")),
BoardMfg: pointer.PointerOrNil(viper.GetString("board-mfg")),
BoardSerial: pointer.PointerOrNil(viper.GetString("board-serial")),
BoardPartNumber: pointer.PointerOrNil(viper.GetString("board-part-number")),
ProductManufacturer: pointer.PointerOrNil(viper.GetString("product-manufacturer")),
ProductPartNumber: pointer.PointerOrNil(viper.GetString("product-part-number")),
ProductSerial: pointer.PointerOrNil(viper.GetString("product-serial")),
},
Hardware: &apiv2.MachineHardwareQuery{
Memory: pointer.PointerOrNil(viper.GetUint64("memory")),
CpuCores: pointer.PointerOrNil(viper.GetUint32("cpu-cores")),
},
// State: &0,
},
Partition: nil, // again partition?
})
if err != nil {
return nil, err
}

return resp.Machines, nil
}

func (c *machine) Update(rq any) (*apiv2.Machine, error) {
panic("unimplemented")
}

func (c *machine) Convert(r *apiv2.Machine) (string, any, any, error) {
panic("unimplemented")

}

func (c *machine) MachineResponseToCreate(r *apiv2.Machine) any {
panic("unimplemented")
}

func (c *machine) MachineResponseToUpdate(desired *apiv2.Machine) (any, error) {
panic("unimplemented")
}
5 changes: 2 additions & 3 deletions cmd/admin/v1/network.go → cmd/admin/v2/network.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package v1
package v2

import (
"github.com/metal-stack/api/go/enum"
Expand Down Expand Up @@ -55,7 +55,7 @@ func newNetworkCmd(c *config.Config) *cobra.Command {
CreateRequestFromCLI: w.createRequestFromCLI,
UpdateRequestFromCLI: w.updateRequestFromCLI,
Sorter: sorters.NetworkSorter(),
ValidArgsFn: c.Completion.NetworkListCompletion,
ValidArgsFn: c.Completion.NetworkAdminListCompletion,
DescribePrinter: func() printers.Printer { return c.DescribePrinter },
ListPrinter: func() printers.Printer { return c.ListPrinter },
CreateCmdMutateFn: func(cmd *cobra.Command) {
Expand All @@ -79,7 +79,6 @@ func newNetworkCmd(c *config.Config) *cobra.Command {
cmd.Flags().StringSlice("destination-prefixes", nil, "destination-prefixes for this network. [optional]")
cmd.Flags().StringSlice("additional-announcable-cidrs", nil, "additional-announcable-cidrs for this network. [optional]")
cmd.Flags().Uint32("vrf", 0, "the vrf of the network to create. [optional]")

genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion))
genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.Completion.PartitionListCompletion))
genericcli.Must(cmd.RegisterFlagCompletionFunc("addressfamily", c.Completion.NetworkAddressFamilyCompletion))
Expand Down
77 changes: 77 additions & 0 deletions cmd/admin/v2/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package v2

import (
"fmt"

adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2"
apiv2 "github.com/metal-stack/api/go/metalstack/api/v2"
"github.com/metal-stack/cli/cmd/config"
"github.com/metal-stack/cli/cmd/sorters"
"github.com/metal-stack/metal-lib/pkg/genericcli"
"github.com/metal-stack/metal-lib/pkg/genericcli/printers"
"github.com/metal-stack/metal-lib/pkg/pointer"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

type project struct {
c *config.Config
}

func newProjectCmd(c *config.Config) *cobra.Command {
w := &project{
c: c,
}

cmdsConfig := &genericcli.CmdsConfig[*apiv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, *apiv2.Project]{
BinaryName: config.BinaryName,
GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs),
Singular: "project",
Plural: "projects",
Description: "manage api projects",
Sorter: sorters.ProjectSorter(),
DescribePrinter: func() printers.Printer { return c.DescribePrinter },
ListPrinter: func() printers.Printer { return c.ListPrinter },
ListCmdMutateFn: func(cmd *cobra.Command) {
cmd.Flags().String("tenant", "", "lists only projects with the given tenant")
},
}

return genericcli.NewCmds(cmdsConfig)
}

func (c *project) Get(id string) (*apiv2.Project, error) {
panic("unimplemented")
}

func (c *project) List() ([]*apiv2.Project, error) {
ctx, cancel := c.c.NewRequestContext()
defer cancel()

req := &adminv2.ProjectServiceListRequest{
Tenant: pointer.PointerOrNil(viper.GetString("tenant")),
}

resp, err := c.c.Client.Adminv2().Project().List(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to list projects: %w", err)
}

return resp.GetProjects(), nil
}

func (c *project) Create(rq *apiv2.ProjectServiceCreateRequest) (*apiv2.Project, error) {
panic("unimplemented")
}

func (c *project) Delete(id string) (*apiv2.Project, error) {
panic("unimplemented")
}

func (c *project) Convert(r *apiv2.Project) (string, *apiv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, error) {
panic("unimplemented")
}

func (c *project) Update(rq *apiv2.ProjectServiceUpdateRequest) (*apiv2.Project, error) {
panic("unimplemented")
}
Loading