cloud/equinix/cli: add list command
This also replaces the packngo library with our fork
Change-Id: I7ef23b840ce0de01109ab5764ed2c23feff72e49
Reviewed-on: https://review.monogon.dev/c/monogon/+/3060
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/equinix/cli/BUILD.bazel b/cloud/equinix/cli/BUILD.bazel
index d10fdeb..020e93c 100644
--- a/cloud/equinix/cli/BUILD.bazel
+++ b/cloud/equinix/cli/BUILD.bazel
@@ -4,6 +4,7 @@
name = "cli_lib",
srcs = [
"cmd_delete.go",
+ "cmd_list.go",
"cmd_move.go",
"cmd_reboot.go",
"cmd_yoink.go",
diff --git a/cloud/equinix/cli/cmd_list.go b/cloud/equinix/cli/cmd_list.go
new file mode 100644
index 0000000..bfbb21d
--- /dev/null
+++ b/cloud/equinix/cli/cmd_list.go
@@ -0,0 +1,105 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/signal"
+ "slices"
+ "strings"
+
+ "github.com/packethost/packngo"
+ "github.com/spf13/cobra"
+ "k8s.io/klog/v2"
+
+ "source.monogon.dev/cloud/equinix/wrapngo"
+)
+
+var listCmd = &cobra.Command{
+ Use: "list",
+ Long: `This lists all hardware reservations inside a specified organization or project.`,
+ Args: cobra.NoArgs,
+ Run: doList,
+}
+
+func init() {
+ listCmd.Flags().String("equinix_organization", "", "from which organization to list from")
+ listCmd.Flags().String("equinix_project", "", "from which project to list from")
+ rootCmd.AddCommand(listCmd)
+}
+
+func doList(cmd *cobra.Command, args []string) {
+ organization, err := cmd.Flags().GetString("equinix_organization")
+ if err != nil {
+ klog.Exitf("flag: %v", err)
+ }
+
+ project, err := cmd.Flags().GetString("equinix_project")
+ if err != nil {
+ klog.Exitf("flag: %v", err)
+ }
+
+ if organization == "" && project == "" {
+ klog.Exitf("missing organization or project flag")
+ }
+
+ ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
+ api := wrapngo.New(&c)
+
+ var (
+ reservations []packngo.HardwareReservation
+ )
+ switch {
+ case project != "" && organization == "":
+ klog.Infof("Listing reservations for project: %s", project)
+ reservations, err = api.ListReservations(ctx, project)
+ case organization != "" && project == "":
+ klog.Infof("Listing reservations for organization: %s", organization)
+ reservations, err = api.ListOrganizationReservations(ctx, organization)
+ default:
+ klog.Exitf("exactly one of organization or project flags has to be set")
+ }
+
+ if err != nil {
+ klog.Fatalf("Failed to list reservations: %v", err)
+ }
+
+ type configDC struct {
+ config string
+ dc string
+ }
+ type configDCP struct {
+ configDC
+ project string
+ }
+ mtypes := make(map[configDC]int)
+ mptypes := make(map[configDCP]int)
+
+ klog.Infof("Got %d reservations", len(reservations))
+ for _, r := range reservations {
+ curType := configDC{config: strings.ToLower(r.Plan.Name), dc: strings.ToLower(r.Facility.Metro.Code)}
+ curPType := configDCP{curType, r.Project.Name}
+ mtypes[curType]++
+ mptypes[curPType]++
+ }
+
+ klog.Infof("Found the following configurations:")
+ var mStrings []string
+ for dc, c := range mtypes {
+ mStrings = append(mStrings, fmt.Sprintf("%s | %s | %d", dc.dc, dc.config, c))
+ }
+ slices.Sort(mStrings)
+ for _, s := range mStrings {
+ klog.Info(s)
+ }
+
+ klog.Infof("Found the following configurations (per project):")
+ var mpStrings []string
+ for dc, c := range mptypes {
+ mpStrings = append(mpStrings, fmt.Sprintf("%s | %s | %s | %d", dc.project, dc.dc, dc.config, c))
+ }
+ slices.Sort(mpStrings)
+ for _, s := range mpStrings {
+ klog.Info(s)
+ }
+}
diff --git a/cloud/equinix/wrapngo/wrapn.go b/cloud/equinix/wrapngo/wrapn.go
index 4ef7b9b..8ee654c 100644
--- a/cloud/equinix/wrapngo/wrapn.go
+++ b/cloud/equinix/wrapngo/wrapn.go
@@ -110,6 +110,9 @@
// with project pid. This is an expensive method that takes a while to execute,
// handle with care.
ListReservations(ctx context.Context, pid string) ([]packngo.HardwareReservation, error)
+
+ ListOrganizationReservations(ctx context.Context, oid string) ([]packngo.HardwareReservation, error)
+
// MoveReservation moves a reserved device to the given project.
MoveReservation(ctx context.Context, hardwareReservationDID, projectID string) (*packngo.HardwareReservation, error)
@@ -346,6 +349,13 @@
})
}
+func (c *client) ListOrganizationReservations(ctx context.Context, pid string) ([]packngo.HardwareReservation, error) {
+ return wrap(ctx, c, func(cl *packngo.Client) ([]packngo.HardwareReservation, error) {
+ res, _, err := cl.Organizations.ListHardwareReservations(pid, &packngo.ListOptions{Includes: []string{"facility", "device", "project"}})
+ return res, err
+ })
+}
+
func (c *client) MoveReservation(ctx context.Context, hardwareReservationDID, projectID string) (*packngo.HardwareReservation, error) {
return wrap(ctx, c, func(cl *packngo.Client) (*packngo.HardwareReservation, error) {
hr, _, err := cl.HardwareReservations.Move(hardwareReservationDID, projectID)
diff --git a/cloud/shepherd/provider/equinix/fakequinix_test.go b/cloud/shepherd/provider/equinix/fakequinix_test.go
index bd0df4a..62892cc 100644
--- a/cloud/shepherd/provider/equinix/fakequinix_test.go
+++ b/cloud/shepherd/provider/equinix/fakequinix_test.go
@@ -23,6 +23,10 @@
reboots map[string]int
}
+func (f *fakequinix) ListOrganizationReservations(ctx context.Context, oid string) ([]packngo.HardwareReservation, error) {
+ return nil, fmt.Errorf("not implemented")
+}
+
// newFakequinix makes a fakequinix with a given fake project ID and number of
// hardware reservations to create.
func newFakequinix(pid string, numReservations int) *fakequinix {
diff --git a/go.mod b/go.mod
index b6239da..b568bea 100644
--- a/go.mod
+++ b/go.mod
@@ -66,6 +66,10 @@
// to appear in our dependency graph: https://github.com/golang/go/issues/37175
replace golang.org/x/exp => golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
+// Replace with our patched library to support hardware listings for a whole
+// organization at once.
+replace github.com/packethost/packngo => github.com/monogon-dev/packngo v0.0.0-20240122175436-ecbd9eb00ddb
+
require (
4d63.com/gocheckcompilerdirectives v1.2.1
cloud.google.com/go/storage v1.36.0
diff --git a/go.sum b/go.sum
index 7f04307..1ea565c 100644
--- a/go.sum
+++ b/go.sum
@@ -1337,8 +1337,9 @@
github.com/diskfs/go-diskfs v1.2.0/go.mod h1:ZTeTbzixuyfnZW5y5qKMtjV2o+GLLHo1KfMhotJI4Rk=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
+github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234=
github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
@@ -2283,6 +2284,7 @@
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
@@ -2292,6 +2294,8 @@
github.com/monogon-dev/ethtool v0.0.0-20231122193313-e9c21a3a83cb/go.mod h1:MQ3uW0nQeqrFtI+NIBalCybBz4C0cNAJs94InnMJqyk=
github.com/monogon-dev/netlink v0.0.0-20230125113930-88977c3ff4b3 h1:y05BDqZ6q3if6pYBHJcnQRUd92ihzBEJde/S4fpKEAM=
github.com/monogon-dev/netlink v0.0.0-20230125113930-88977c3ff4b3/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik=
+github.com/monogon-dev/packngo v0.0.0-20240122175436-ecbd9eb00ddb h1:sxSnvzB4iDBNhUBqXME/ETqjF4vX0mURE85T/I/Mr0o=
+github.com/monogon-dev/packngo v0.0.0-20240122175436-ecbd9eb00ddb/go.mod h1:Io6VJqzkiqmIEQbpOjeIw9v8q9PfcTEq8TEY/tMQsfw=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
@@ -2457,8 +2461,6 @@
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/outcaste-io/ristretto v0.2.1 h1:KCItuNIGJZcursqHr3ghO7fc5ddZLEHspL9UR0cQM64=
github.com/outcaste-io/ristretto v0.2.1/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
-github.com/packethost/packngo v0.29.0 h1:gRIhciVZQ/zLNrIdIdbOUyB/Tw5IgoaXyhP4bvE+D2s=
-github.com/packethost/packngo v0.29.0/go.mod h1:/UHguFdPs6Lf6FOkkSEPnRY5tgS0fsVM+Zv/bvBrmt0=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
diff --git a/third_party/go/repositories.bzl b/third_party/go/repositories.bzl
index 5c099f4..7dcb1cc 100644
--- a/third_party/go/repositories.bzl
+++ b/third_party/go/repositories.bzl
@@ -1421,8 +1421,8 @@
go_repository(
name = "com_github_dnaeon_go_vcr",
importpath = "github.com/dnaeon/go-vcr",
- sum = "h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=",
- version = "v1.0.1",
+ sum = "h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=",
+ version = "v1.2.0",
)
go_repository(
name = "com_github_dnstap_golang_dnstap",
@@ -3816,6 +3816,12 @@
version = "v1.0.2",
)
go_repository(
+ name = "com_github_modocache_gover",
+ importpath = "github.com/modocache/gover",
+ sum = "h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA=",
+ version = "v0.0.0-20171022184752-b58185e213c5",
+ )
+ go_repository(
name = "com_github_mohae_deepcopy",
importpath = "github.com/mohae/deepcopy",
sum = "h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=",
@@ -4136,8 +4142,9 @@
go_repository(
name = "com_github_packethost_packngo",
importpath = "github.com/packethost/packngo",
- sum = "h1:gRIhciVZQ/zLNrIdIdbOUyB/Tw5IgoaXyhP4bvE+D2s=",
- version = "v0.29.0",
+ replace = "github.com/monogon-dev/packngo",
+ sum = "h1:sxSnvzB4iDBNhUBqXME/ETqjF4vX0mURE85T/I/Mr0o=",
+ version = "v0.0.0-20240122175436-ecbd9eb00ddb",
)
go_repository(
name = "com_github_pact_foundation_pact_go",