Skip to content

Commit 41d7efa

Browse files
committed
Rename and refactor bootstrap, implement basic provisioning functions
This renames `DeviceSpec.Bootstrap` to `Provisioning` in order to have a unified naming. We also implement a ProvisioningProvider interface and state transitionings in the device controller.
1 parent e2e7831 commit 41d7efa

File tree

10 files changed

+485
-235
lines changed

10 files changed

+485
-235
lines changed

api/core/v1alpha1/device_types.go

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
package v1alpha1
55

66
import (
7+
"crypto/rand"
8+
"fmt"
9+
710
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
811
)
912

@@ -16,7 +19,7 @@ type DeviceSpec struct {
1619
// Bootstrap is an optional configuration for the device bootstrap process.
1720
// It can be used to provide initial configuration templates or scripts that are applied during the device provisioning.
1821
// +optional
19-
Bootstrap *Bootstrap `json:"bootstrap,omitempty"`
22+
Provisioning *Provisioning `json:"provisioning,omitempty"`
2023
}
2124

2225
// Endpoint contains the connection information for the device.
@@ -48,11 +51,40 @@ type TLS struct {
4851
Certificate *CertificateSource `json:"certificate,omitempty"`
4952
}
5053

51-
// Bootstrap defines the configuration for device bootstrap.
52-
type Bootstrap struct {
53-
// Template defines the multiline string template that contains the initial configuration for the device.
54+
// Provisioning defines the configuration for device bootstrap.
55+
type Provisioning struct {
56+
// Image defines the image to be used for provisioning the device.
5457
// +required
55-
Template TemplateSource `json:"template"`
58+
Image Image `json:"image"`
59+
60+
// BootScript defines the script delivered by a TFTP server to the device during bootstrapping.
61+
// +optional
62+
BootScript TemplateSource `json:"bootScript"`
63+
}
64+
65+
// ChecksumType defines the type of checksum used for image verification.
66+
// +kubebuilder:validation:Enum=SHA256;MD5
67+
type ChecksumType string
68+
69+
const (
70+
ChecksumTypeSHA256 ChecksumType = "SHA256"
71+
ChecksumTypeMD5 ChecksumType = "MD5"
72+
)
73+
74+
type Image struct {
75+
// URL is the location of the image to be used for provisioning.
76+
// +required
77+
URL string `json:"url"`
78+
79+
// Checksum is the checksum of the image for verification.
80+
// +required
81+
// kubebuilder:validation:MinLength=1
82+
Checksum string `json:"checksum"`
83+
84+
// ChecksumType is the type of the checksum (e.g., sha256, md5).
85+
// +required
86+
// +kubebuilder:default=MD5
87+
ChecksumType ChecksumType `json:"checksumType"`
5688
}
5789

5890
// TemplateSource defines a source for template content.
@@ -105,6 +137,14 @@ type DeviceStatus struct {
105137
// +optional
106138
FirmwareVersion string `json:"firmwareVersion,omitempty"`
107139

140+
// Provisioning is the list of provisioning attempts for the Device.
141+
//+listType=map
142+
//+listMapKey=startTime
143+
//+patchStrategy=merge
144+
//+patchMergeKey=startTime
145+
//+optional
146+
Provisioning []ProvisioningInfo `json:"provisioning,omitempty"`
147+
108148
// Ports is the list of ports on the Device.
109149
// +optional
110150
Ports []DevicePort `json:"ports,omitempty"`
@@ -122,6 +162,41 @@ type DeviceStatus struct {
122162
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
123163
}
124164

165+
type ProvisioningInfo struct {
166+
StartTime metav1.Time `json:"startTime"`
167+
EndTime metav1.Time `json:"endTime,omitempty"`
168+
Token string `json:"token"`
169+
Error string `json:"error,omitempty"`
170+
}
171+
172+
func (d *Device) GetActiveProvisioning() *ProvisioningInfo {
173+
for i := range d.Status.Provisioning {
174+
if d.Status.Provisioning[i].EndTime.IsZero() {
175+
return &d.Status.Provisioning[i]
176+
}
177+
}
178+
return nil
179+
}
180+
181+
func (d *Device) CreateProvisioningEntry() (*ProvisioningInfo, error) {
182+
183+
if d.Status.Phase != DevicePhaseProvisioning {
184+
return nil, fmt.Errorf("device is in phase %s, expected %s", d.Status.Phase, DevicePhaseProvisioning)
185+
}
186+
active := d.GetActiveProvisioning()
187+
if active != nil {
188+
return nil, fmt.Errorf("device has an active provisioning with StartTime %s", active.StartTime.String())
189+
}
190+
token := make([]byte, 32)
191+
rand.Read(token)
192+
entry := ProvisioningInfo{
193+
StartTime: metav1.Now(),
194+
Token: fmt.Sprintf("%x", token),
195+
}
196+
d.Status.Provisioning = append(d.Status.Provisioning, entry)
197+
return &d.Status.Provisioning[len(d.Status.Provisioning)-1], nil
198+
}
199+
125200
type DevicePort struct {
126201
// Name is the name of the port.
127202
// +required
@@ -137,7 +212,7 @@ type DevicePort struct {
137212

138213
// Transceiver is the type of transceiver plugged into the port, if any.
139214
// +optional
140-
Trasceiver string `json:"transceiver,omitempty"`
215+
Transceiver string `json:"transceiver,omitempty"`
141216

142217
// InterfaceRef is the reference to the corresponding Interface resource
143218
// configuring this port, if any.
@@ -146,14 +221,16 @@ type DevicePort struct {
146221
}
147222

148223
// DevicePhase represents the current phase of the Device as it's being provisioned and managed by the operator.
149-
// +kubebuilder:validation:Enum=Pending;Provisioning;Active;Failed
224+
// +kubebuilder:validation:Enum=Pending;Provisioning;Active;Failed;ProvisioningCompleted
150225
type DevicePhase string
151226

152227
const (
153228
// DevicePhasePending indicates that the device is pending and has not yet been provisioned.
154229
DevicePhasePending DevicePhase = "Pending"
155230
// DevicePhaseProvisioning indicates that the device is being provisioned.
156231
DevicePhaseProvisioning DevicePhase = "Provisioning"
232+
// DevicePhaseProvisioningCompleted indicates that the device provisioning has completed and the operator is performing post-provisioning tasks.
233+
DevicePhaseProvisioningCompleted DevicePhase = "ProvisioningCompleted"
157234
// DevicePhaseActive indicates that the device has been successfully provisioned and is now ready for use.
158235
DevicePhaseActive DevicePhase = "Active"
159236
// DevicePhaseFailed indicates that the device provisioning has failed.
@@ -211,11 +288,6 @@ func (d *Device) GetSecretRefs() []SecretReference {
211288
refs = append(refs, d.Spec.Endpoint.TLS.Certificate.SecretRef)
212289
}
213290
}
214-
if d.Spec.Bootstrap != nil {
215-
if d.Spec.Bootstrap.Template.SecretRef != nil {
216-
refs = append(refs, d.Spec.Bootstrap.Template.SecretRef.SecretReference)
217-
}
218-
}
219291
for i := range refs {
220292
if refs[i].Namespace == "" {
221293
refs[i].Namespace = d.Namespace
@@ -227,11 +299,6 @@ func (d *Device) GetSecretRefs() []SecretReference {
227299
// GetConfigMapRefs returns the list of configmaps referenced in the [Device] resource.
228300
func (d *Device) GetConfigMapRefs() []ConfigMapReference {
229301
refs := []ConfigMapReference{}
230-
if d.Spec.Bootstrap != nil {
231-
if d.Spec.Bootstrap.Template.ConfigMapRef != nil {
232-
refs = append(refs, d.Spec.Bootstrap.Template.ConfigMapRef.ConfigMapReference)
233-
}
234-
}
235302
for i := range refs {
236303
if refs[i].Namespace == "" {
237304
refs[i].Namespace = d.Namespace

api/core/v1alpha1/groupversion_info.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const FinalizerName = "networking.metal.ironcore.dev/finalizer"
3939
// based on the device they are intended for.
4040
const DeviceLabel = "networking.metal.ironcore.dev/device-name"
4141

42+
const DeviceSerialLabel = "networking.metal.ironcore.dev/device-serial"
43+
4244
// DeviceKind represents the Kind of Device.
4345
const DeviceKind = "Device"
4446

@@ -65,6 +67,21 @@ const (
6567
OperationalCondition = "Operational"
6668
)
6769

70+
// ProvisioningReasonType represents the reason for the current provisioning status.
71+
type ProvisioningReasonType string
72+
73+
const (
74+
ProvisioningScriptExecutionStarted ProvisioningReasonType = "ScriptExecutionStarted"
75+
ProvisioningScriptExecutionFailed ProvisioningReasonType = "ScriptExecutionFailed"
76+
ProvisioningInstallingCertificates ProvisioningReasonType = "InstallingCertificates"
77+
ProvisioningDownloadingImage ProvisioningReasonType = "DownloadingImage"
78+
ProvisioningImageDownloadFailed ProvisioningReasonType = "ImageDownloadFailed"
79+
ProvisioningUpgradeStarting ProvisioningReasonType = "UpgradeStarting"
80+
ProvisioningUpgradeFailed ProvisioningReasonType = "UpgradeFailed"
81+
ProvisioningRebootingDevice ProvisioningReasonType = "RebootingDevice"
82+
ProvisioningExecutionFinishedWithoutReboot ProvisioningReasonType = "ExecutionFinishedWithoutReboot"
83+
)
84+
6885
// Reasons that are used across different objects.
6986
const (
7087
// ReadyReason indicates that the resource is ready for use.

api/core/v1alpha1/zz_generated.deepcopy.go

Lines changed: 59 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)