From 0dc788e1c241df07d83cc9c4e5e9962ce982d33e Mon Sep 17 00:00:00 2001 From: Charles Treatman Date: Mon, 20 Mar 2023 14:41:34 -0500 Subject: [PATCH] [WIP] Adopt metal-go --- cmd/ci-clean/main.go | 59 +++++------- controllers/packetmachine_controller.go | 52 +++++----- go.mod | 2 +- go.sum | 14 +-- pkg/cloud/packet/client.go | 121 ++++++++++++++---------- test/e2e/common_test.go | 4 +- test/e2e/go.mod | 2 +- test/e2e/go.sum | 14 +-- test/e2e/suite_test.go | 4 +- version/version.go | 11 ++- 10 files changed, 152 insertions(+), 131 deletions(-) diff --git a/cmd/ci-clean/main.go b/cmd/ci-clean/main.go index 702184dd5..db5c9efc1 100644 --- a/cmd/ci-clean/main.go +++ b/cmd/ci-clean/main.go @@ -24,7 +24,7 @@ import ( "strings" "time" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/spf13/cobra" kerrors "k8s.io/apimachinery/pkg/util/errors" @@ -66,51 +66,45 @@ func main() { func cleanup(metalAuthToken, metalProjectID string) error { metalClient := packet.NewClient(metalAuthToken) - listOpts := &packngo.ListOptions{} var errs []error - devices, _, err := metalClient.Devices.List(metalProjectID, listOpts) + devices, _, err := metalClient.DevicesApi.FindProjectDevices(nil, metalProjectID).Execute() if err != nil { return fmt.Errorf("failed to list devices: %w", err) } - if err := deleteDevices(metalClient, devices); err != nil { + if err := deleteDevices(metalClient, *devices); err != nil { errs = append(errs, err) } - ips, _, err := metalClient.ProjectIPs.List(metalProjectID, listOpts) + ips, _, err := metalClient.IPAddressesApi.FindIPReservations(nil, metalProjectID).Execute() if err != nil { return fmt.Errorf("failed to list ip addresses: %w", err) } - if err := deleteIPs(metalClient, ips); err != nil { + if err := deleteIPs(metalClient, *ips); err != nil { errs = append(errs, err) } - keys, _, err := metalClient.Projects.ListSSHKeys(metalProjectID, listOpts) + keys, _, err := metalClient.SSHKeysApi.FindProjectSSHKeys(nil, metalProjectID).Execute() if err != nil { return fmt.Errorf("failed to list ssh keys: %w", err) } - if err := deleteKeys(metalClient, keys); err != nil { + if err := deleteKeys(metalClient, *keys); err != nil { errs = append(errs, err) } return kerrors.NewAggregate(errs) } -func deleteDevices(metalClient *packet.Client, devices []packngo.Device) error { +func deleteDevices(metalClient *packet.Client, devices metal.DeviceList) error { var errs []error - for _, d := range devices { - created, err := time.Parse(time.RFC3339, d.Created) - if err != nil { - errs = append(errs, fmt.Errorf("failed to parse creation time for device %q: %w", d.Hostname, err)) - continue - } - if time.Since(created) > 4*time.Hour { + for _, d := range devices.Devices { + if time.Since(*d.CreatedAt) > 4*time.Hour { fmt.Printf("Deleting device: %s\n", d.Hostname) - _, err := metalClient.Devices.Delete(d.ID, false) + _, err := metalClient.DevicesApi.DeleteDevice(nil, *d.Id).ForceDelete(false).Execute() if err != nil { errs = append(errs, fmt.Errorf("failed to delete device %q: %w", d.Hostname, err)) } @@ -120,22 +114,20 @@ func deleteDevices(metalClient *packet.Client, devices []packngo.Device) error { return kerrors.NewAggregate(errs) } -func deleteIPs(metalClient *packet.Client, ips []packngo.IPAddressReservation) error { +func deleteIPs(metalClient *packet.Client, ips metal.IPReservationList) error { var errs []error - for _, ip := range ips { - created, err := time.Parse(time.RFC3339, ip.Created) - if err != nil { - errs = append(errs, fmt.Errorf("failed to parse creation time for ip address %q: %w", ip.Address, err)) - continue - } - - if time.Since(created) > 4*time.Hour { + for _, reservation := range ips.IpAddresses { + // TODO: per the spec, `reservation` could be an `IPReservation` or a `VrfIpReservation` + // maybe metal-go could define and we could move the if block to function that takes + // that interface as an argument + ip := reservation.IPReservation + if ip != nil && time.Since(*ip.CreatedAt) > 4*time.Hour { for _, tag := range ip.Tags { if strings.HasPrefix(tag, "cluster-api-provider-packet:cluster-id:") || strings.HasPrefix(tag, "usage=cloud-provider-equinix-metal-auto") { fmt.Printf("Deleting IP: %s\n", ip.Address) - if _, err := metalClient.ProjectIPs.Remove(ip.ID); err != nil { + if _, err := metalClient.IPAddressesApi.DeleteIPAddress(nil, *ip.Id).Execute(); err != nil { errs = append(errs, fmt.Errorf("failed to delete ip address %q: %w", ip.Address, err)) } @@ -148,18 +140,13 @@ func deleteIPs(metalClient *packet.Client, ips []packngo.IPAddressReservation) e return kerrors.NewAggregate(errs) } -func deleteKeys(metalClient *packet.Client, keys []packngo.SSHKey) error { +func deleteKeys(metalClient *packet.Client, keys metal.SSHKeyList) error { var errs []error - for _, k := range keys { - created, err := time.Parse(time.RFC3339, k.Created) - if err != nil { - errs = append(errs, fmt.Errorf("failed to parse creation time for SSH Key %q: %w", k.Label, err)) - continue - } - if time.Since(created) > 4*time.Hour { + for _, k := range keys.SshKeys { + if time.Since(*k.CreatedAt) > 4*time.Hour { fmt.Printf("Deleting SSH Key: %s\n", k.Label) - _, err := metalClient.SSHKeys.Delete(k.ID) + _, err := metalClient.SSHKeysApi.DeleteSSHKey(nil, *k.Id).Execute() if err != nil { errs = append(errs, fmt.Errorf("failed to delete SSH Key %q: %w", k.Label, err)) } diff --git a/controllers/packetmachine_controller.go b/controllers/packetmachine_controller.go index eabef1a6c..d35944c7c 100644 --- a/controllers/packetmachine_controller.go +++ b/controllers/packetmachine_controller.go @@ -24,7 +24,7 @@ import ( "strings" "time" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -254,26 +254,27 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s providerID := machineScope.GetInstanceID() var ( - dev *packngo.Device + dev *metal.Device addrs []corev1.NodeAddress err error - controlPlaneEndpoint packngo.IPAddressReservation + controlPlaneEndpoint metal.IPReservation + resp *http.Response ) if providerID != "" { // If we already have a providerID, then retrieve the device using the // providerID. This means that the Machine has already been created // and we successfully recorded the providerID. - dev, err = r.PacketClient.GetDevice(providerID) + dev, resp, err = r.PacketClient.GetDevice(providerID) if err != nil { - var perr *packngo.ErrorResponse - if errors.As(err, &perr) && perr.Response != nil { - if perr.Response.StatusCode == http.StatusNotFound { + // TODO metal-go doesn't have an ErrorResponse; need to convert all of this + if resp != nil { + if resp.StatusCode == http.StatusNotFound { machineScope.SetFailureReason(capierrors.UpdateMachineError) machineScope.SetFailureMessage(fmt.Errorf("failed to find device: %w", err)) log.Error(err, "unable to find device") conditions.MarkFalse(machineScope.PacketMachine, infrav1.DeviceReadyCondition, infrav1.InstanceNotFoundReason, clusterv1.ConditionSeverityError, err.Error()) - } else if perr.Response.StatusCode == http.StatusForbidden { + } else if resp.StatusCode == http.StatusForbidden { machineScope.SetFailureReason(capierrors.UpdateMachineError) log.Error(err, "device failed to provision") machineScope.SetFailureMessage(fmt.Errorf("device failed to provision: %w", err)) @@ -327,12 +328,12 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s if len(controlPlaneEndpoint.Assignments) == 0 { a := corev1.NodeAddress{ Type: corev1.NodeExternalIP, - Address: controlPlaneEndpoint.Address, + Address: *controlPlaneEndpoint.Address, } addrs = append(addrs, a) } } - createDeviceReq.ControlPlaneEndpoint = controlPlaneEndpoint.Address + createDeviceReq.ControlPlaneEndpoint = *controlPlaneEndpoint.Address } dev, err = r.PacketClient.NewDevice(ctx, createDeviceReq) @@ -359,11 +360,11 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s } // we do not need to set this as equinixmetal:// because SetProviderID() does the formatting for us - machineScope.SetProviderID(dev.ID) - machineScope.SetInstanceStatus(infrav1.PacketResourceStatus(dev.State)) + machineScope.SetProviderID(*dev.Id) + machineScope.SetInstanceStatus(infrav1.PacketResourceStatus(*dev.State)) if machineScope.PacketCluster.Spec.VIPManager == "KUBE_VIP" { - if err := r.PacketClient.EnsureNodeBGPEnabled(dev.ID); err != nil { + if err := r.PacketClient.EnsureNodeBGPEnabled(*dev.Id); err != nil { // Do not treat an error enabling bgp on machine as fatal return ctrl.Result{RequeueAfter: time.Second * 20}, fmt.Errorf("failed to enable bpg on machine %s: %w", machineScope.Name(), err) } @@ -375,7 +376,7 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s // Proceed to reconcile the PacketMachine state. var result reconcile.Result - switch infrav1.PacketResourceStatus(dev.State) { + switch infrav1.PacketResourceStatus(*dev.State) { case infrav1.PacketResourceStatusNew, infrav1.PacketResourceStatusQueued, infrav1.PacketResourceStatusProvisioning: log.Info("Machine instance is pending", "instance-id", machineScope.GetInstanceID()) machineScope.SetNotReady() @@ -389,9 +390,11 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s machineScope.Cluster.Name, machineScope.PacketCluster.Spec.ProjectID) if len(controlPlaneEndpoint.Assignments) == 0 && machineScope.IsControlPlane() { - if _, _, err := r.PacketClient.DeviceIPs.Assign(dev.ID, &packngo.AddressStruct{ - Address: controlPlaneEndpoint.Address, - }); err != nil { + apiRequest := r.PacketClient.DevicesApi.CreateIPAssignment(nil, *dev.Id) + apiRequest.IPAssignmentInput(metal.IPAssignmentInput{ + Address: *controlPlaneEndpoint.Address, + }) + if _, _, err := apiRequest.Execute(); err != nil { log.Error(err, "err assigining elastic ip to control plane. retrying...") return ctrl.Result{RequeueAfter: time.Second * 20}, nil } @@ -422,7 +425,7 @@ func (r *PacketMachineReconciler) reconcileDelete(ctx context.Context, machineSc packetmachine := machineScope.PacketMachine providerID := machineScope.GetInstanceID() - var device *packngo.Device + var device *metal.Device if providerID == "" { // If no providerID was recorded, check to see if there are any instances @@ -443,12 +446,12 @@ func (r *PacketMachineReconciler) reconcileDelete(ctx context.Context, machineSc device = dev } else { + var resp *http.Response // Otherwise, try to retrieve the device by the providerID - dev, err := r.PacketClient.GetDevice(providerID) + dev, resp, err := r.PacketClient.GetDevice(providerID) if err != nil { - var errResp *packngo.ErrorResponse - if errors.As(err, &errResp) && errResp.Response != nil { - if errResp.Response.StatusCode == http.StatusNotFound { + if resp != nil { + if resp.StatusCode == http.StatusNotFound { // When the server does not exist we do not have anything left to do. // Probably somebody manually deleted the server from the UI or via API. log.Info("Server not found by id, nothing left to do") @@ -456,7 +459,7 @@ func (r *PacketMachineReconciler) reconcileDelete(ctx context.Context, machineSc return ctrl.Result{}, nil } - if errResp.Response.StatusCode == http.StatusForbidden { + if resp.StatusCode == http.StatusForbidden { // When a server fails to provision it will return a 403 log.Info("Server appears to have failed provisioning, nothing left to do") controllerutil.RemoveFinalizer(packetmachine, infrav1.MachineFinalizer) @@ -476,7 +479,8 @@ func (r *PacketMachineReconciler) reconcileDelete(ctx context.Context, machineSc return ctrl.Result{}, fmt.Errorf("%w: %s", ErrMissingDevice, packetmachine.Name) } - if _, err := r.PacketClient.Devices.Delete(device.ID, force); err != nil { + apiRequest := r.PacketClient.DevicesApi.DeleteDevice(nil, *device.Id).ForceDelete(force) + if _, err := apiRequest.Execute(); err != nil { return ctrl.Result{}, fmt.Errorf("failed to delete the machine: %w", err) } diff --git a/go.mod b/go.mod index 6e9f7ba36..13da8e51f 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module sigs.k8s.io/cluster-api-provider-packet go 1.17 require ( + github.com/equinix-labs/metal-go v0.6.0 github.com/onsi/gomega v1.20.2 - github.com/packethost/packngo v0.26.0 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 37743681b..a89b0f612 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -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/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -174,6 +172,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/equinix-labs/metal-go v0.6.0 h1:rX5XnPt95HlDMtITSjwQjqFzu1AKQqCult/9uy/lFNw= +github.com/equinix-labs/metal-go v0.6.0/go.mod h1:uTuxWkVf/wl9VUBYnCtecRtHfdTCeajDwgzluSZBD7U= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -506,8 +506,6 @@ github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8lu github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/packethost/packngo v0.26.0 h1:jbPHPEd+KpoM2OBQ3EgbgEYiAUK7vpZ9XhqOMEbnk78= -github.com/packethost/packngo v0.26.0/go.mod h1:/UHguFdPs6Lf6FOkkSEPnRY5tgS0fsVM+Zv/bvBrmt0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -610,13 +608,18 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -694,7 +697,6 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/pkg/cloud/packet/client.go b/pkg/cloud/packet/client.go index 80af36b35..33f1cc078 100644 --- a/pkg/cloud/packet/client.go +++ b/pkg/cloud/packet/client.go @@ -27,7 +27,7 @@ import ( "strings" "text/template" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" @@ -55,7 +55,7 @@ var ( ) type Client struct { - *packngo.Client + *metal.APIClient } // NewClient creates a new Client for the given Packet credentials @@ -63,9 +63,11 @@ func NewClient(packetAPIKey string) *Client { token := strings.TrimSpace(packetAPIKey) if token != "" { - metalClient := &Client{packngo.NewClientWithAuth(clientName, token, nil)} - metalClient.UserAgent = fmt.Sprintf(clientUAFormat, version.Get(), metalClient.UserAgent) - + configuration := metal.NewConfiguration() + configuration.Debug = true + configuration.AddDefaultHeader("X-Auth-Token", os.Getenv("METAL_AUTH_TOKEN")) + configuration.UserAgent = fmt.Sprintf(clientUAFormat, version.Get(), configuration.UserAgent) + metalClient := &Client{metal.NewAPIClient(configuration)} return metalClient } @@ -80,9 +82,9 @@ func GetClient() (*Client, error) { return NewClient(token), nil } -func (p *Client) GetDevice(deviceID string) (*packngo.Device, error) { - dev, _, err := p.Devices.Get(deviceID, nil) - return dev, err +func (p *Client) GetDevice(deviceID string) (*metal.Device, *http.Response, error) { + dev, resp, err := p.DevicesApi.FindDeviceById(nil, deviceID).Execute() + return dev, resp, err } type CreateDeviceRequest struct { @@ -91,7 +93,7 @@ type CreateDeviceRequest struct { ControlPlaneEndpoint string } -func (p *Client) NewDevice(ctx context.Context, req CreateDeviceRequest) (*packngo.Device, error) { +func (p *Client) NewDevice(ctx context.Context, req CreateDeviceRequest) (*metal.Device, error) { if req.MachineScope.PacketMachine.Spec.IPXEUrl != "" { // Error if pxe url and OS conflict if req.MachineScope.PacketMachine.Spec.OS != ipxeOS { @@ -121,7 +123,7 @@ func (p *Client) NewDevice(ctx context.Context, req CreateDeviceRequest) (*packn if req.MachineScope.IsControlPlane() { // control plane machines should get the API key injected - userDataValues["apiKey"] = p.APIKey + userDataValues["apiKey"] = p.APIClient.GetConfig().DefaultHeader["X-Auth-Token"] if req.ControlPlaneEndpoint != "" { userDataValues["controlPlaneEndpoint"] = req.ControlPlaneEndpoint @@ -144,23 +146,29 @@ func (p *Client) NewDevice(ctx context.Context, req CreateDeviceRequest) (*packn facility = req.MachineScope.PacketMachine.Spec.Facility } - serverCreateOpts := &packngo.DeviceCreateRequest{ - Hostname: req.MachineScope.Name(), - ProjectID: req.MachineScope.PacketCluster.Spec.ProjectID, - Facility: []string{facility}, - BillingCycle: req.MachineScope.PacketMachine.Spec.BillingCycle, - Plan: req.MachineScope.PacketMachine.Spec.MachineType, - OS: req.MachineScope.PacketMachine.Spec.OS, - IPXEScriptURL: req.MachineScope.PacketMachine.Spec.IPXEUrl, - Tags: tags, - UserData: userData, + hostname := req.MachineScope.Name() + serverCreateOpts := metal.CreateDeviceRequest{ + DeviceCreateInFacilityInput: &metal.DeviceCreateInFacilityInput{ + Hostname: &hostname, + Facility: metal.FacilityInputFacility{ + String: &facility, + }, + BillingCycle: &req.MachineScope.PacketMachine.Spec.BillingCycle, + Plan: req.MachineScope.PacketMachine.Spec.MachineType, + OperatingSystem: req.MachineScope.PacketMachine.Spec.OS, + IpxeScriptUrl: &req.MachineScope.PacketMachine.Spec.IPXEUrl, + Tags: tags, + Userdata: &userData, + }, } reservationIDs := strings.Split(req.MachineScope.PacketMachine.Spec.HardwareReservationID, ",") // If there are no reservationIDs to process, go ahead and return early if len(reservationIDs) == 0 { - dev, _, err := p.Devices.Create(serverCreateOpts) + apiRequest := p.DevicesApi.CreateDevice(nil, req.MachineScope.PacketCluster.Spec.ProjectID) + apiRequest.CreateDeviceRequest(serverCreateOpts) + dev, _, err := apiRequest.Execute() return dev, err } @@ -170,8 +178,10 @@ func (p *Client) NewDevice(ctx context.Context, req CreateDeviceRequest) (*packn var lastErr error for _, resID := range reservationIDs { - serverCreateOpts.HardwareReservationID = resID - dev, _, err := p.Devices.Create(serverCreateOpts) + serverCreateOpts.DeviceCreateInFacilityInput.HardwareReservationId = &resID + apiRequest := p.DevicesApi.CreateDevice(nil, req.MachineScope.PacketCluster.Spec.ProjectID) + apiRequest.CreateDeviceRequest(serverCreateOpts) + dev, _, err := apiRequest.Execute() if err != nil { lastErr = err continue @@ -183,29 +193,29 @@ func (p *Client) NewDevice(ctx context.Context, req CreateDeviceRequest) (*packn return nil, lastErr } -func (p *Client) GetDeviceAddresses(device *packngo.Device) []corev1.NodeAddress { +func (p *Client) GetDeviceAddresses(device *metal.Device) []corev1.NodeAddress { addrs := make([]corev1.NodeAddress, 0) - for _, addr := range device.Network { + for _, addr := range device.IpAddresses { addrType := corev1.NodeInternalIP - if addr.IpAddressCommon.Public { + if addr.GetPublic() { addrType = corev1.NodeExternalIP } a := corev1.NodeAddress{ Type: addrType, - Address: addr.Address, + Address: addr.GetAddress(), } addrs = append(addrs, a) } return addrs } -func (p *Client) GetDeviceByTags(project string, tags []string) (*packngo.Device, error) { - devices, _, err := p.Devices.List(project, nil) +func (p *Client) GetDeviceByTags(project string, tags []string) (*metal.Device, error) { + devices, _, err := p.DevicesApi.FindProjectDevices(nil, project).Execute() if err != nil { return nil, fmt.Errorf("error retrieving devices: %w", err) } // returns the first one that matches all of the tags - for _, device := range devices { + for _, device := range devices.Devices { if ItemsInList(device.Tags, tags) { return &device, nil } @@ -216,15 +226,20 @@ func (p *Client) GetDeviceByTags(project string, tags []string) (*packngo.Device // CreateIP reserves an IP via Packet API. The request fails straight if no IP are available for the specified project. // This prevent the cluster to become ready. func (p *Client) CreateIP(namespace, clusterName, projectID, facility string) (net.IP, error) { - req := packngo.IPReservationRequest{ - Type: packngo.PublicIPv4, + failOnApprovalRequired := true + req := metal.IPReservationRequestInput{ + Type: "public_ipv4", Quantity: 1, Facility: &facility, - FailOnApprovalRequired: true, + FailOnApprovalRequired: &failOnApprovalRequired, Tags: []string{generateElasticIPIdentifier(clusterName)}, } - r, resp, err := p.ProjectIPs.Request(projectID, &req) + apiRequest := p.IPAddressesApi.RequestIPReservation(nil, projectID) + apiRequest.RequestIPReservationRequest(metal.RequestIPReservationRequest{ + IPReservationRequestInput: &req, + }) + r, resp, err := apiRequest.Execute() if err != nil { return nil, err } @@ -232,9 +247,9 @@ func (p *Client) CreateIP(namespace, clusterName, projectID, facility string) (n return nil, ErrElasticIPQuotaExceeded } - ip := net.ParseIP(r.Address) + ip := net.ParseIP(*r.IPReservation.Address) if ip == nil { - return nil, fmt.Errorf("failed to parse IP: %s, %w", r.Address, ErrInvalidIP) + return nil, fmt.Errorf("failed to parse IP: %s, %w", r.IPReservation.Address, ErrInvalidIP) } return ip, nil } @@ -242,7 +257,7 @@ func (p *Client) CreateIP(namespace, clusterName, projectID, facility string) (n // enableBGP enable bgp on the project func (p *Client) EnableProjectBGP(projectID string) error { // first check if it is enabled before trying to create it - bgpConfig, _, err := p.BGPConfig.Get(projectID, &packngo.GetOptions{}) + bgpConfig, _, err := p.BGPApi.FindBgpConfigByProject(nil, projectID).Execute() // if we already have a config, just return // we need some extra handling logic because the API always returns 200, even if // not BGP config is in place. @@ -253,7 +268,7 @@ func (p *Client) EnableProjectBGP(projectID string) error { // - bgpConfig struct does not have Status=="disabled" if err != nil { return err - } else if bgpConfig != nil && bgpConfig.ID != "" && strings.ToLower(bgpConfig.Status) != "disabled" { + } else if bgpConfig != nil && *bgpConfig.Id != "" && strings.ToLower(*bgpConfig.Status) != "disabled" { return nil } @@ -278,20 +293,23 @@ func (p *Client) EnableProjectBGP(projectID string) error { } // we did not have a valid one, so create it - req := packngo.CreateBGPConfigRequest{ - Asn: outLocalASN, - Md5: outBGPPass, + useCase := "kubernetes-load-balancer" + apiRequest := p.BGPApi.RequestBgpConfig(nil, projectID) + apiRequest.BgpConfigRequestInput(metal.BgpConfigRequestInput{ + Asn: int32(outLocalASN), + Md5: &outBGPPass, DeploymentType: "local", - UseCase: "kubernetes-load-balancer", - } - _, err = p.BGPConfig.Create(projectID, req) + UseCase: &useCase, + }) + _, err = apiRequest.Execute() return err } // ensureNodeBGPEnabled check if the node has bgp enabled, and set it if it does not func (p *Client) EnsureNodeBGPEnabled(id string) error { + // TODO: The spec doesn't define the create BGP session endpoint // fortunately, this is idempotent, so just create - req := packngo.CreateBGPSessionRequest{ + req := metal.CreateBGPSessionRequest{ AddressFamily: "ipv4", } _, response, err := p.BGPSessions.Create(id, req) @@ -303,17 +321,18 @@ func (p *Client) EnsureNodeBGPEnabled(id string) error { return err } -func (p *Client) GetIPByClusterIdentifier(namespace, name, projectID string) (packngo.IPAddressReservation, error) { +func (p *Client) GetIPByClusterIdentifier(namespace, name, projectID string) (metal.IPAddressReservation, error) { var err error - var reservedIP packngo.IPAddressReservation + var reservedIP metal.IPReservationListIpAddressesInner - listOpts := &packngo.ListOptions{} - reservedIPs, _, err := p.ProjectIPs.List(projectID, listOpts) + reservedIPs, _, err := p.IPAddressesApi.FindIPReservations(nil, projectID).Execute() if err != nil { return reservedIP, err } - for _, reservedIP := range reservedIPs { - for _, v := range reservedIP.Tags { + for _, reservedIP := range reservedIPs.IpAddresses { + // `reservedIP` is `oneOf` `IPReservation` or `VrfIpReservation` + // We probably need to be safer here and check which one it is + for _, v := range reservedIP.IPReservation.Tags { if v == generateElasticIPIdentifier(name) { return reservedIP, nil } diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 3ad1bd988..8c7bd86ee 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -29,9 +29,9 @@ import ( "github.com/blang/semver" "github.com/docker/distribution/reference" + metal "github.com/equinix-labs/metal-go/metal/v1" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/packethost/packngo" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" @@ -216,7 +216,7 @@ func (w *wrappedClusterProxy) Dispose(ctx context.Context) { logf("Will clean up EIPs for the following clusters: %v", clusterNames) for _, clusterName := range clusterNames { - var ip packngo.IPAddressReservation + var ip metal.IPAddressReservation g.Eventually(func(g Gomega) { var err error diff --git a/test/e2e/go.mod b/test/e2e/go.mod index 16ee7a358..2faf09d2a 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -5,9 +5,9 @@ go 1.17 require ( github.com/blang/semver v3.5.1+incompatible github.com/docker/distribution v2.8.1+incompatible + github.com/equinix-labs/metal-go v0.6.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.20.2 - github.com/packethost/packngo v0.26.0 golang.org/x/crypto v0.0.0-20220214200702-86341886e292 k8s.io/api v0.24.4 k8s.io/apimachinery v0.24.4 diff --git a/test/e2e/go.sum b/test/e2e/go.sum index 8bcb72263..a5b844369 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -163,8 +163,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -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/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -191,6 +189,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/equinix-labs/metal-go v0.6.0 h1:rX5XnPt95HlDMtITSjwQjqFzu1AKQqCult/9uy/lFNw= +github.com/equinix-labs/metal-go v0.6.0/go.mod h1:uTuxWkVf/wl9VUBYnCtecRtHfdTCeajDwgzluSZBD7U= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -535,8 +535,6 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/packethost/packngo v0.26.0 h1:jbPHPEd+KpoM2OBQ3EgbgEYiAUK7vpZ9XhqOMEbnk78= -github.com/packethost/packngo v0.26.0/go.mod h1:/UHguFdPs6Lf6FOkkSEPnRY5tgS0fsVM+Zv/bvBrmt0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -644,13 +642,18 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -730,7 +733,6 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index a4bc12554..79de90f63 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -30,11 +30,11 @@ import ( "strings" "testing" + metal "github.com/equinix-labs/metal-go/metal/v1" . "github.com/onsi/ginkgo" "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/reporters" . "github.com/onsi/gomega" - "github.com/packethost/packngo" "golang.org/x/crypto/ssh" "k8s.io/apimachinery/pkg/runtime" capi_e2e "sigs.k8s.io/cluster-api/test/e2e" @@ -289,7 +289,7 @@ func generateSSHKey() (string, string) { metalClient := packet.NewClient(metalAuthToken) res, _, err := metalClient.SSHKeys.Create( - &packngo.SSHKeyCreateRequest{ + &metal.SSHKeyCreateRequest{ Label: fmt.Sprintf("capp-e2e-%s", util.RandomString(6)), Key: string(ssh.MarshalAuthorizedKey(pub)), }, diff --git a/version/version.go b/version/version.go index 952f6d2a5..7471b8b90 100644 --- a/version/version.go +++ b/version/version.go @@ -19,8 +19,9 @@ package version import ( "fmt" "runtime" + "strings" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" ) var ( @@ -58,7 +59,7 @@ func Get() Info { GoVersion: runtime.Version(), Compiler: runtime.Compiler, Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), - MetalSdkVersion: packngo.Version, + MetalSdkVersion: getMetalVersion(), } } @@ -66,3 +67,9 @@ func Get() Info { func (info Info) String() string { return info.GitVersion } + +func getMetalVersion() string { + metalUserAgent := metal.NewConfiguration().UserAgent + metalVersion := metalUserAgent[strings.Index(metalUserAgent, "/")+1:] + return metalVersion +}