Skip to content
This repository was archived by the owner on Aug 12, 2025. It is now read-only.

Commit b552b02

Browse files
authored
Merge pull request #287 from detiber/backportMultipleReservations
✨ Add support for comma separated reservationIDs
2 parents b0fc6f4 + eff0344 commit b552b02

File tree

9 files changed

+55
-80
lines changed

9 files changed

+55
-80
lines changed

.github/workflows/pr-post.yml

Lines changed: 0 additions & 54 deletions
This file was deleted.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ cd: confirm branchname
217217
# needed kubebuilder for tests
218218
kubebuilder: $(KUBEBUILDER)
219219
$(KUBEBUILDER):
220-
curl -sL https://go.kubebuilder.io/dl/$(KUBEBUILDER_VERSION)/$(BUILDOS)/$(BUILDARCH) | tar -xz -C /tmp/
220+
curl -sL https://github.com/kubernetes-sigs/kubebuilder/releases/download/v$(KUBEBUILDER_VERSION)/kubebuilder_$(KUBEBUILDER_VERSION)_$(BUILDOS)_$(BUILDARCH).tar.gz | tar -xz -C /tmp/
221221
# move to a long-term location and put it on your path
222222
# (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else)
223223
mv /tmp/kubebuilder_$(KUBEBUILDER_VERSION)_$(BUILDOS)_$(BUILDARCH) $(KUBEBUILDER_DIR)

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.3.9
1+
v0.3.10

api/v1alpha3/packetmachine_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ type PacketMachineSpec struct {
4848
// +optional
4949
IPXEUrl string `json:"ipxeURL,omitempty"`
5050

51-
// HardwareReservationID is the unique device hardware reservation ID or `next-available` to
51+
// HardwareReservationID is the unique device hardware reservation ID, a comma separated list of
52+
// hardware reservation IDs, or `next-available` to
5253
// automatically let the Packet api determine one.
5354
// +optional
5455
HardwareReservationID string `json:"hardwareReservationID,omitempty"`

config/crd/bases/infrastructure.cluster.x-k8s.io_packetmachines.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ spec:
6363
description: Facility represents the Packet facility for this cluster. Override from the PacketCluster spec.
6464
type: string
6565
hardwareReservationID:
66-
description: HardwareReservationID is the unique device hardware reservation ID or `next-available` to automatically let the Packet api determine one.
66+
description: HardwareReservationID is the unique device hardware reservation ID, a comma separated list of hardware reservation IDs, or `next-available` to automatically let the Packet api determine one.
6767
type: string
6868
ipxeURL:
6969
description: IPXEUrl can be used to set the pxe boot url when using custom OSes with this provider. Note that OS should also be set to "custom_ipxe" if using this value.

config/crd/bases/infrastructure.cluster.x-k8s.io_packetmachinetemplates.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ spec:
4848
description: Facility represents the Packet facility for this cluster. Override from the PacketCluster spec.
4949
type: string
5050
hardwareReservationID:
51-
description: HardwareReservationID is the unique device hardware reservation ID or `next-available` to automatically let the Packet api determine one.
51+
description: HardwareReservationID is the unique device hardware reservation ID, a comma separated list of hardware reservation IDs, or `next-available` to automatically let the Packet api determine one.
5252
type: string
5353
ipxeURL:
5454
description: IPXEUrl can be used to set the pxe boot url when using custom OSes with this provider. Note that OS should also be set to "custom_ipxe" if using this value.

config/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ bases:
1313
- default
1414
images:
1515
- name: docker.io/packethost/cluster-api-provider-packet:e2e # images with this name
16-
newTag: v0.3.9 # {"type":"string","x-kustomize":{"setter":{"name":"image-tag","value":"v0.3.9"}}}
16+
newTag: v0.3.10 # {"type":"string","x-kustomize":{"setter":{"name":"image-tag","value":"v0.3.10"}}}
1717
newName: docker.io/packethost/cluster-api-provider-packet # and this name

controllers/packetmachine_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ func (r *PacketMachineReconciler) reconcile(ctx context.Context, machineScope *s
245245
case err != nil && strings.Contains(err.Error(), " no available hardware reservations "):
246246
// Do not treat an error indicating there are no hardware reservations available as fatal
247247
return ctrl.Result{}, fmt.Errorf("failed to create machine %s: %w", machineScope.Name(), err)
248+
case err != nil && strings.Contains(err.Error(), "Server is not provisionable"):
249+
// Do not treat an error indicating that reserved hardware is not provisionable as fatal
250+
// This occurs when reserved hardware is in the process of being deprovisioned
251+
return ctrl.Result{}, fmt.Errorf("failed to create machine %s: %w", machineScope.Name(), err)
248252
case err != nil:
249253
errs := fmt.Errorf("failed to create machine %s: %w", machineScope.Name(), err)
250254
machineScope.SetErrorReason(capierrors.CreateMachineError)

pkg/cloud/packet/client.go

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ const (
3838
ipxeOS = "custom_ipxe"
3939
)
4040

41-
var ErrControlPlanEndpointNotFound = errors.New("control plane not found")
41+
var (
42+
ErrControlPlanEndpointNotFound = errors.New("control plane not found")
43+
ErrInvalidRequest = errors.New("invalid request")
44+
)
4245

4346
type PacketClient struct {
4447
*packngo.Client
@@ -75,6 +78,13 @@ type CreateDeviceRequest struct {
7578
}
7679

7780
func (p *PacketClient) NewDevice(req CreateDeviceRequest) (*packngo.Device, error) {
81+
if req.MachineScope.PacketMachine.Spec.IPXEUrl != "" {
82+
// Error if pxe url and OS conflict
83+
if req.MachineScope.PacketMachine.Spec.OS != ipxeOS {
84+
return nil, fmt.Errorf("os should be set to custom_pxe when using pxe urls: %w", ErrInvalidRequest)
85+
}
86+
}
87+
7888
userDataRaw, err := req.MachineScope.GetRawBootstrapData()
7989
if err != nil {
8090
return nil, errors.Wrap(err, "impossible to retrieve bootstrap data from secret")
@@ -119,28 +129,42 @@ func (p *PacketClient) NewDevice(req CreateDeviceRequest) (*packngo.Device, erro
119129
}
120130

121131
serverCreateOpts := &packngo.DeviceCreateRequest{
122-
Hostname: req.MachineScope.Name(),
123-
ProjectID: req.MachineScope.PacketCluster.Spec.ProjectID,
124-
Facility: []string{facility},
125-
BillingCycle: req.MachineScope.PacketMachine.Spec.BillingCycle,
126-
HardwareReservationID: req.MachineScope.PacketMachine.Spec.HardwareReservationID,
127-
Plan: req.MachineScope.PacketMachine.Spec.MachineType,
128-
OS: req.MachineScope.PacketMachine.Spec.OS,
129-
Tags: tags,
130-
UserData: userData,
131-
}
132-
133-
// Update server options to pass pxe url if specified
134-
if req.MachineScope.PacketMachine.Spec.IPXEUrl != "" {
135-
// Error if pxe url and OS conflict
136-
if req.MachineScope.PacketMachine.Spec.OS != ipxeOS {
137-
return nil, fmt.Errorf("os should be set to custom_pxe when using pxe urls")
132+
Hostname: req.MachineScope.Name(),
133+
ProjectID: req.MachineScope.PacketCluster.Spec.ProjectID,
134+
Facility: []string{facility},
135+
BillingCycle: req.MachineScope.PacketMachine.Spec.BillingCycle,
136+
Plan: req.MachineScope.PacketMachine.Spec.MachineType,
137+
OS: req.MachineScope.PacketMachine.Spec.OS,
138+
IPXEScriptURL: req.MachineScope.PacketMachine.Spec.IPXEUrl,
139+
Tags: tags,
140+
UserData: userData,
141+
}
142+
143+
reservationIDs := strings.Split(req.MachineScope.PacketMachine.Spec.HardwareReservationID, ",")
144+
145+
// If there are no reservationIDs to process, go ahead and return early
146+
if len(reservationIDs) == 0 {
147+
dev, _, err := p.Client.Devices.Create(serverCreateOpts)
148+
return dev, err
149+
}
150+
151+
// Do a naive loop through the list of reservationIDs, continuing if we hit any error
152+
// TODO: if we can determine how to differentiate a failure based on the reservation
153+
// being in use vs other errors, then we can make this a bit smarter in the future.
154+
var lastErr error
155+
156+
for _, resID := range reservationIDs {
157+
serverCreateOpts.HardwareReservationID = resID
158+
dev, _, err := p.Client.Devices.Create(serverCreateOpts)
159+
if err != nil {
160+
lastErr = err
161+
continue
138162
}
139-
serverCreateOpts.IPXEScriptURL = req.MachineScope.PacketMachine.Spec.IPXEUrl
163+
164+
return dev, nil
140165
}
141166

142-
dev, _, err := p.Client.Devices.Create(serverCreateOpts)
143-
return dev, err
167+
return nil, lastErr
144168
}
145169

146170
func (p *PacketClient) GetDeviceAddresses(device *packngo.Device) ([]corev1.NodeAddress, error) {

0 commit comments

Comments
 (0)