diff --git a/controller/machine/hardware.go b/controller/machine/hardware.go index 645e818d..72d12e6b 100644 --- a/controller/machine/hardware.go +++ b/controller/machine/hardware.go @@ -27,6 +27,9 @@ const ( // HardwareProvisionedAnnotation signifies that the Hardware with this annotation has be provisioned by CAPT. HardwareProvisionedAnnotation = "v1alpha1.tinkerbell.org/provisioned" + + // HardwareTemplateOverrideAnnotation can be used to override the default Template used for provisioning. + HardwareTemplateOverrideAnnotation = "hardware.tinkerbell.org/capt-template-override" ) var ( diff --git a/controller/machine/template.go b/controller/machine/template.go index 77b17546..09612cef 100644 --- a/controller/machine/template.go +++ b/controller/machine/template.go @@ -158,33 +158,22 @@ func (scope *machineReconcileScope) createTemplate(hw *tinkv1.Hardware) error { templateData := scope.tinkerbellMachine.Spec.TemplateOverride if templateData == "" { - targetDisk := hw.Spec.Disks[0].Device - targetDevice := firstPartitionFromDevice(targetDisk) - - imageURL, err := scope.imageURL() + scope.log.Info("tinkerbellMachine.Spec.TemplateOverride is empty, trying from hardware annotation") + tmplFromAnnotation, err := scope.templateFromAnnotation(hw) if err != nil { - return fmt.Errorf("failed to generate imageURL: %w", err) - } - - metadataIP := os.Getenv("TINKERBELL_IP") - if metadataIP == "" { - metadataIP = "192.168.1.1" - } - - metadataURL := fmt.Sprintf("http://%s:50061", metadataIP) - - workflowTemplate := WorkflowTemplate{ - Name: scope.tinkerbellMachine.Name, - MetadataURL: metadataURL, - ImageURL: imageURL, - DestDisk: targetDisk, - DestPartition: targetDevice, + return fmt.Errorf("failed to get template from hardware annotation: %w", err) } + templateData = tmplFromAnnotation + } - templateData, err = workflowTemplate.Render() + // If still no template, generate the default one. + if templateData == "" { + scope.log.Info("no template found in hardware annotation, generating default template") + defaultTemplate, err := scope.generateDefaultTemplate(hw) if err != nil { - return fmt.Errorf("rendering template: %w", err) + return err } + templateData = defaultTemplate } templateObject := &tinkv1.Template{ @@ -212,6 +201,59 @@ func (scope *machineReconcileScope) createTemplate(hw *tinkv1.Hardware) error { return nil } +func (scope *machineReconcileScope) generateDefaultTemplate(hw *tinkv1.Hardware) (string, error) { + targetDisk := hw.Spec.Disks[0].Device + targetDevice := firstPartitionFromDevice(targetDisk) + + imageURL, err := scope.imageURL() + if err != nil { + return "", fmt.Errorf("failed to generate imageURL: %w", err) + } + + metadataIP := os.Getenv("TINKERBELL_IP") + if metadataIP == "" { + metadataIP = "192.168.1.1" + } + + metadataURL := fmt.Sprintf("http://%s:50061", metadataIP) + + workflowTemplate := WorkflowTemplate{ + Name: scope.tinkerbellMachine.Name, + MetadataURL: metadataURL, + ImageURL: imageURL, + DestDisk: targetDisk, + DestPartition: targetDevice, + } + + templateData, err := workflowTemplate.Render() + if err != nil { + return "", fmt.Errorf("rendering template: %w", err) + } + return templateData, nil +} + +func (scope *machineReconcileScope) templateFromAnnotation(hw *tinkv1.Hardware) (string, error) { + templateData := "" + // Check if hardware has an annotation 'hardware.tinkerbell.org/capt-template-override', if so, + // use it as the name of a Template resource in the same namespace as the Hardware, load it, + // and use it's spec.data as the template. + scope.log.Info("hardware annotations", "annotations", hw.Annotations) + if templateName, ok := hw.Annotations[HardwareTemplateOverrideAnnotation]; ok { + scope.log.Info("found template override in Hardware annotation", "templateName", templateName, "namespace", hw.Namespace) + overrideTemplate := &tinkv1.Template{} + namespacedName := types.NamespacedName{ + Name: templateName, + Namespace: hw.Namespace, + } + if err := scope.client.Get(scope.ctx, namespacedName, overrideTemplate); err != nil { + return "", fmt.Errorf("failed to get Template %q specified in hardware annotation: %w", templateName, err) + } + scope.log.V(4).Info("found template override in Hardware annotations, using it as template", "templateName", templateName) + templateData = *overrideTemplate.Spec.Data + } + return templateData, nil +} + func firstPartitionFromDevice(device string) string { nvmeDevice := regexp.MustCompile(`^/dev/nvme\d+n\d+$`) emmcDevice := regexp.MustCompile(`^/dev/mmcblk\d+$`)