44package v1alpha1
55
66import (
7+ "crypto/rand"
8+ "encoding/hex"
9+ "fmt"
10+
711 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
812)
913
@@ -16,7 +20,7 @@ type DeviceSpec struct {
1620 // Bootstrap is an optional configuration for the device bootstrap process.
1721 // It can be used to provide initial configuration templates or scripts that are applied during the device provisioning.
1822 // +optional
19- Bootstrap * Bootstrap `json:"bootstrap ,omitempty"`
23+ Provisioning * Provisioning `json:"provisioning ,omitempty"`
2024}
2125
2226// Endpoint contains the connection information for the device.
@@ -48,11 +52,40 @@ type TLS struct {
4852 Certificate * CertificateSource `json:"certificate,omitempty"`
4953}
5054
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.
55+ // Provisioning defines the configuration for device bootstrap.
56+ type Provisioning struct {
57+ // Image defines the image to be used for provisioning the device.
5458 // +required
55- Template TemplateSource `json:"template"`
59+ Image Image `json:"image"`
60+
61+ // BootScript defines the script delivered by a TFTP server to the device during bootstrapping.
62+ // +optional
63+ BootScript TemplateSource `json:"bootScript"`
64+ }
65+
66+ // ChecksumType defines the type of checksum used for image verification.
67+ // +kubebuilder:validation:Enum=SHA256;MD5
68+ type ChecksumType string
69+
70+ const (
71+ ChecksumTypeSHA256 ChecksumType = "SHA256"
72+ ChecksumTypeMD5 ChecksumType = "MD5" //nolint: usestdlibvars
73+ )
74+
75+ type Image struct {
76+ // URL is the location of the image to be used for provisioning.
77+ // +required
78+ URL string `json:"url"`
79+
80+ // Checksum is the checksum of the image for verification.
81+ // +required
82+ // kubebuilder:validation:MinLength=1
83+ Checksum string `json:"checksum"`
84+
85+ // ChecksumType is the type of the checksum (e.g., sha256, md5).
86+ // +required
87+ // +kubebuilder:default=MD5
88+ ChecksumType ChecksumType `json:"checksumType"`
5689}
5790
5891// TemplateSource defines a source for template content.
@@ -105,6 +138,14 @@ type DeviceStatus struct {
105138 // +optional
106139 FirmwareVersion string `json:"firmwareVersion,omitempty"`
107140
141+ // Provisioning is the list of provisioning attempts for the Device.
142+ //+listType=map
143+ //+listMapKey=startTime
144+ //+patchStrategy=merge
145+ //+patchMergeKey=startTime
146+ //+optional
147+ Provisioning []ProvisioningInfo `json:"provisioning,omitempty"`
148+
108149 // Ports is the list of ports on the Device.
109150 // +optional
110151 Ports []DevicePort `json:"ports,omitempty"`
@@ -122,6 +163,47 @@ type DeviceStatus struct {
122163 Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
123164}
124165
166+ type ProvisioningInfo struct {
167+ StartTime metav1.Time `json:"startTime"`
168+ Token string `json:"token"`
169+ //+optional
170+ EndTime metav1.Time `json:"endTime,omitzero"`
171+ //+optional
172+ RebootTime metav1.Time `json:"reboot,omitzero"`
173+ //+optional
174+ Error string `json:"error,omitempty"`
175+ }
176+
177+ func (d * Device ) GetActiveProvisioning () * ProvisioningInfo {
178+ for i := range d .Status .Provisioning {
179+ if d .Status .Provisioning [i ].EndTime .IsZero () {
180+ return & d .Status .Provisioning [i ]
181+ }
182+ }
183+ return nil
184+ }
185+
186+ func (d * Device ) CreateProvisioningEntry () (* ProvisioningInfo , error ) {
187+ if d .Status .Phase != DevicePhaseProvisioning {
188+ return nil , fmt .Errorf ("device is in phase %s, expected %s" , d .Status .Phase , DevicePhaseProvisioning )
189+ }
190+ active := d .GetActiveProvisioning ()
191+ if active != nil {
192+ return nil , fmt .Errorf ("device has an active provisioning with StartTime %s" , active .StartTime .String ())
193+ }
194+ token := make ([]byte , 32 )
195+ _ , err := rand .Read (token )
196+ if err != nil {
197+ return nil , err
198+ }
199+ entry := ProvisioningInfo {
200+ StartTime : metav1 .Now (),
201+ Token : hex .EncodeToString (token ),
202+ }
203+ d .Status .Provisioning = append (d .Status .Provisioning , entry )
204+ return & entry , nil
205+ }
206+
125207type DevicePort struct {
126208 // Name is the name of the port.
127209 // +required
@@ -137,7 +219,7 @@ type DevicePort struct {
137219
138220 // Transceiver is the type of transceiver plugged into the port, if any.
139221 // +optional
140- Trasceiver string `json:"transceiver,omitempty"`
222+ Transceiver string `json:"transceiver,omitempty"`
141223
142224 // InterfaceRef is the reference to the corresponding Interface resource
143225 // configuring this port, if any.
@@ -146,14 +228,16 @@ type DevicePort struct {
146228}
147229
148230// 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
231+ // +kubebuilder:validation:Enum=Pending;Provisioning;Active;Failed;ProvisioningCompleted
150232type DevicePhase string
151233
152234const (
153235 // DevicePhasePending indicates that the device is pending and has not yet been provisioned.
154236 DevicePhasePending DevicePhase = "Pending"
155237 // DevicePhaseProvisioning indicates that the device is being provisioned.
156238 DevicePhaseProvisioning DevicePhase = "Provisioning"
239+ // DevicePhaseProvisioningCompleted indicates that the device provisioning has completed and the operator is performing post-provisioning tasks.
240+ DevicePhaseProvisioningCompleted DevicePhase = "ProvisioningCompleted"
157241 // DevicePhaseActive indicates that the device has been successfully provisioned and is now ready for use.
158242 DevicePhaseActive DevicePhase = "Active"
159243 // DevicePhaseFailed indicates that the device provisioning has failed.
@@ -211,11 +295,6 @@ func (d *Device) GetSecretRefs() []SecretReference {
211295 refs = append (refs , d .Spec .Endpoint .TLS .Certificate .SecretRef )
212296 }
213297 }
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- }
219298 for i := range refs {
220299 if refs [i ].Namespace == "" {
221300 refs [i ].Namespace = d .Namespace
@@ -227,11 +306,6 @@ func (d *Device) GetSecretRefs() []SecretReference {
227306// GetConfigMapRefs returns the list of configmaps referenced in the [Device] resource.
228307func (d * Device ) GetConfigMapRefs () []ConfigMapReference {
229308 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- }
235309 for i := range refs {
236310 if refs [i ].Namespace == "" {
237311 refs [i ].Namespace = d .Namespace
0 commit comments