Skip to content

Commit

Permalink
Expose custom funcs to template (#638)
Browse files Browse the repository at this point in the history
Having exposed a subset of hardware data to templates there's a need to provide some helper functions for manipulating the data. The concrete use case is formatting of disk partitions.

This PR introduces `formatPartition` to templates accepting the device path. The function currently supports historic block devices (`/dev/sd<char>`) and NVMe drives. Additionally it adds `strings.Contains`, `strings.HasPrefix` and `strings.HasSuffix` which have proven useful. 

**Example template snippet**
```
name: {{ .Hardware.Name }}
disk: {{ formatPartition ( index .Hardware.Disks 0 ) 1 }}
```

Documentation will need adding to the docs website in the on-going documentation effort.
  • Loading branch information
mergify[bot] authored Aug 26, 2022
2 parents a0e9350 + d889e78 commit ed90557
Show file tree
Hide file tree
Showing 4 changed files with 387 additions and 11 deletions.
15 changes: 8 additions & 7 deletions pkg/controllers/workflow/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,11 @@ func (c *Controller) processNewWorkflow(ctx context.Context, logger logr.Logger,
}

data := make(map[string]interface{})

for key, val := range stored.Spec.HardwareMap {
data[key] = val
}
var hardware v1alpha1.Hardware

var hardware v1alpha1.Hardware
err := c.kubeClient.Get(ctx, client.ObjectKey{Name: stored.Spec.HardwareRef, Namespace: stored.Namespace}, &hardware)
if err != nil && !errors.IsNotFound(err) {
logger.Error(err, "error getting Hardware object in processNewWorkflow function")
Expand All @@ -112,8 +111,7 @@ func (c *Controller) processNewWorkflow(ctx context.Context, logger logr.Logger,
}

if err == nil {
// convert between hardware and hardwareTemplate type
contract := toTemplateHardware(hardware)
contract := toTemplateHardwareData(hardware)
data["Hardware"] = contract
}

Expand All @@ -129,12 +127,15 @@ func (c *Controller) processNewWorkflow(ctx context.Context, logger logr.Logger,
return reconcile.Result{}, nil
}

type hardwareTemplate struct {
// templateHardwareData defines the data exposed for a Hardware instance to a Template.
type templateHardwareData struct {
Disks []string
}

func toTemplateHardware(hardware v1alpha1.Hardware) hardwareTemplate {
var contract hardwareTemplate
// toTemplateHardwareData converts a Hardware instance of templateHardwareData for use in template
// rendering.
func toTemplateHardwareData(hardware v1alpha1.Hardware) templateHardwareData {
var contract templateHardwareData
for _, disk := range hardware.Spec.Disks {
contract.Disks = append(contract.Disks, disk.Device)
}
Expand Down
30 changes: 30 additions & 0 deletions workflow/funcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package workflow

import (
"fmt"
"strings"
)

// templateFuncs defines the custom functions available to workflow templates.
var templateFuncs = map[string]interface{}{
"contains": strings.Contains,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"formatPartition": formatPartition,
}

// formatPartition formats a device path with partition for the device type. If it receives an
// unidentifiable device path it returns the dev.
//
// Examples
// formatPartition("/dev/nvme0n1", 0) -> /dev/nvme0n1p1
// formatPartition("/dev/sda", 1) -> /dev/sda1
func formatPartition(dev string, partition int) string {
switch {
case strings.HasPrefix(dev, "/dev/nvme"):
return fmt.Sprintf("%vp%v", dev, partition)
case strings.HasPrefix(dev, "/dev/sd"):
return fmt.Sprintf("%v%v", dev, partition)
}
return dev
}
8 changes: 4 additions & 4 deletions workflow/template_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,17 @@ func RenderTemplate(templateID, templateData string, devices []byte) (string, er

// RenderTemplateHardware renders the workflow template and returns the Workflow and the interpolated bytes.
func RenderTemplateHardware(templateID, templateData string, hardware map[string]interface{}) (*Workflow, *bytes.Buffer, error) {
t := template.New("workflow-template").Option("missingkey=error")
t := template.New("workflow-template").
Option("missingkey=error").
Funcs(templateFuncs)
_, err := t.Parse(templateData)
if err != nil {
err = errors.Wrapf(err, errTemplateParsing, templateID)
return nil, nil, err
}

// introduces hardware to the template rendering
buf := new(bytes.Buffer)
err = t.Execute(buf, hardware)
if err != nil {
if err = t.Execute(buf, hardware); err != nil {
err = errors.Wrapf(err, errTemplateParsing, templateID)
return nil, nil, err
}
Expand Down
Loading

0 comments on commit ed90557

Please sign in to comment.