Skip to content

Commit

Permalink
Changed hardware templating system (#635)
Browse files Browse the repository at this point in the history
Signed-off-by: Srikar Ganti <[email protected]>

## Description



## Why is this needed



Fixes: #

## How Has This Been Tested?





## How are existing users impacted? What migration steps/scripts do we need?





## Checklist:

I have:

- [ ] updated the documentation and/or roadmap (if required)
- [ ] added unit or e2e tests
- [ ] provided instructions on how to upgrade
  • Loading branch information
mergify[bot] authored Aug 16, 2022
2 parents cf88a1f + 41a5fbe commit a0e9350
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 6 deletions.
3 changes: 3 additions & 0 deletions config/crd/bases/tinkerbell.org_workflowdata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ spec:
type: string
description: A mapping of template devices to hadware mac addresses
type: object
hardwareRef:
description: Name of the Hardware associated with this workflow.
type: string
templateRef:
description: Name of the Template associated with this workflow.
type: string
Expand Down
3 changes: 3 additions & 0 deletions config/crd/bases/tinkerbell.org_workflows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ spec:
type: string
description: A mapping of template devices to hadware mac addresses
type: object
hardwareRef:
description: Name of the Hardware associated with this workflow.
type: string
templateRef:
description: Name of the Template associated with this workflow.
type: string
Expand Down
1 change: 1 addition & 0 deletions config/crd/examples/workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ metadata:
namespace: default
spec:
templateRef: debian
hardwareRef: sm01
hardwareMap:
device_1: 3c:ec:ef:4c:4f:54
2 changes: 1 addition & 1 deletion config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ spec:
control-plane: controller-manager
spec:
containers:
- image: controller:latest
- image: tink-controller:latest
imagePullPolicy: IfNotPresent
name: manager
resources:
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/core/v1alpha1/workflow_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type WorkflowSpec struct {
// Name of the Template associated with this workflow.
TemplateRef string `json:"templateRef,omitempty"`

// Name of the Hardware associated with this workflow.
HardwareRef string `json:"hardwareRef,omitempty"`

// A mapping of template devices to hadware mac addresses
HardwareMap map[string]string `json:"hardwareMap,omitempty"`
}
Expand Down
48 changes: 45 additions & 3 deletions pkg/controllers/workflow/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"time"

"github.com/go-logr/logr"
"github.com/tinkerbell/tink/pkg/apis/core/v1alpha1"
"github.com/tinkerbell/tink/pkg/controllers"
"github.com/tinkerbell/tink/pkg/convert"
Expand Down Expand Up @@ -57,7 +58,7 @@ func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (reco
)
switch wflow.Status.State {
case "":
resp, err = c.processNewWorkflow(ctx, wflow)
resp, err = c.processNewWorkflow(ctx, logger, wflow)
case v1alpha1.WorkflowStateRunning:
resp = c.processRunningWorkflow(ctx, wflow)
default:
Expand All @@ -73,11 +74,12 @@ func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (reco
return resp, err
}

func (c *Controller) processNewWorkflow(ctx context.Context, stored *v1alpha1.Workflow) (reconcile.Result, error) {
func (c *Controller) processNewWorkflow(ctx context.Context, logger logr.Logger, stored *v1alpha1.Workflow) (reconcile.Result, error) {
tpl := &v1alpha1.Template{}
if err := c.kubeClient.Get(ctx, client.ObjectKey{Name: stored.Spec.TemplateRef, Namespace: stored.Namespace}, tpl); err != nil {
if errors.IsNotFound(err) {
// Throw an error to raise awareness and take advantage of immediate requeue.
logger.Error(err, "error getting Template object in processNewWorkflow function")
return reconcile.Result{}, fmt.Errorf(
"no template found: name=%v; namespace=%v",
stored.Spec.TemplateRef,
Expand All @@ -87,7 +89,35 @@ func (c *Controller) processNewWorkflow(ctx context.Context, stored *v1alpha1.Wo
return controllers.RetryIfError(ctx, err)
}

tinkWf, _, err := tinkworkflow.RenderTemplateHardware(stored.Name, ptr.StringValue(tpl.Spec.Data), stored.Spec.HardwareMap)
data := make(map[string]interface{})

for key, val := range stored.Spec.HardwareMap {
data[key] = val
}
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")
return reconcile.Result{}, err
}

if stored.Spec.HardwareRef != "" && errors.IsNotFound(err) {
logger.Error(err, "hardware not found in processNewWorkflow function")
return reconcile.Result{}, fmt.Errorf(
"hardware not found: name=%v; namespace=%v",
stored.Spec.HardwareRef,
stored.Namespace,
)
}

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

tinkWf, _, err := tinkworkflow.RenderTemplateHardware(stored.Name, ptr.StringValue(tpl.Spec.Data), data)
if err != nil {
return reconcile.Result{}, err
}
Expand All @@ -99,6 +129,18 @@ func (c *Controller) processNewWorkflow(ctx context.Context, stored *v1alpha1.Wo
return reconcile.Result{}, nil
}

type hardwareTemplate struct {
Disks []string
}

func toTemplateHardware(hardware v1alpha1.Hardware) hardwareTemplate {
var contract hardwareTemplate
for _, disk := range hardware.Spec.Disks {
contract.Disks = append(contract.Disks, disk.Device)
}
return contract
}

func (c *Controller) processRunningWorkflow(_ context.Context, stored *v1alpha1.Workflow) reconcile.Result {
// Check for global timeout expiration
if c.nowFunc().After(stored.GetStartTime().Add(time.Duration(stored.Status.GlobalTimeout) * time.Second)) {
Expand Down
235 changes: 235 additions & 0 deletions pkg/controllers/workflow/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ tasks:
IMG_URL: "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz"
COMPRESSED: true`

var templateWithDiskTemplate = `version: "0.1"
name: debian
global_timeout: 1800
tasks:
- name: "os-installation"
worker: "{{.device_1}}"
volumes:
- /dev:/dev
- /dev/console:/dev/console
- /lib/firmware:/lib/firmware:ro
actions:
- name: "stream-debian-image"
image: quay.io/tinkerbell-actions/image2disk:v1.0.0
timeout: 600
environment:
DEST_DISK: {{ index .Hardware.Disks 0 }}
# Hegel IP
IMG_URL: "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz"
COMPRESSED: true`

func TestReconcile(t *testing.T) {
cases := []struct {
name string
Expand Down Expand Up @@ -657,6 +677,221 @@ tasks:
},
wantErr: nil,
},
{
name: "Error getting hardware ref",
seedTemplate: &v1alpha1.Template{
TypeMeta: metav1.TypeMeta{
Kind: "Template",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "debian",
Namespace: "default",
},
Spec: v1alpha1.TemplateSpec{
Data: &minimalTemplate,
},
Status: v1alpha1.TemplateStatus{},
},
seedWorkflow: &v1alpha1.Workflow{
TypeMeta: metav1.TypeMeta{
Kind: "Workflow",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "debian",
Namespace: "default",
},
Spec: v1alpha1.WorkflowSpec{
TemplateRef: "debian",
HardwareRef: "i_dont_exist",
HardwareMap: map[string]string{
"device_1": "3c:ec:ef:4c:4f:54",
},
},
Status: v1alpha1.WorkflowStatus{},
},
req: reconcile.Request{
NamespacedName: types.NamespacedName{
Name: "debian",
Namespace: "default",
},
},
want: reconcile.Result{},
wantWflow: &v1alpha1.Workflow{
TypeMeta: metav1.TypeMeta{
Kind: "Workflow",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: "1000",
Name: "debian",
Namespace: "default",
},
Spec: v1alpha1.WorkflowSpec{
TemplateRef: "debian",
HardwareMap: map[string]string{
"device_1": "3c:ec:ef:4c:4f:54",
},
},
Status: v1alpha1.WorkflowStatus{
State: v1alpha1.WorkflowStatePending,
GlobalTimeout: 1800,
Tasks: []v1alpha1.Task{
{
Name: "os-installation",

WorkerAddr: "3c:ec:ef:4c:4f:54",
Volumes: []string{
"/dev:/dev",
"/dev/console:/dev/console",
"/lib/firmware:/lib/firmware:ro",
},
Actions: []v1alpha1.Action{
{
Name: "stream-debian-image",
Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0",
Timeout: 600,
Environment: map[string]string{
"COMPRESSED": "true",
"DEST_DISK": "/dev/nvme0n1",
"IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz",
},
Status: v1alpha1.WorkflowStatePending,
},
},
},
},
},
},
wantErr: errors.New("hardware not found: name=i_dont_exist; namespace=default"),
},
{
name: "success with hardware ref",
seedHardware: &v1alpha1.Hardware{
TypeMeta: metav1.TypeMeta{
Kind: "Hardware",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "machine1",
Namespace: "default",
},
Spec: v1alpha1.HardwareSpec{
Disks: []v1alpha1.Disk{
{Device: "/dev/nvme0n1"},
},
Interfaces: []v1alpha1.Interface{
{
Netboot: &v1alpha1.Netboot{
AllowPXE: &[]bool{true}[0],
AllowWorkflow: &[]bool{true}[0],
},
DHCP: &v1alpha1.DHCP{
Arch: "x86_64",
Hostname: "sm01",
IP: &v1alpha1.IP{
Address: "172.16.10.100",
Gateway: "172.16.10.1",
Netmask: "255.255.255.0",
},
LeaseTime: 86400,
MAC: "3c:ec:ef:4c:4f:54",
NameServers: []string{},
UEFI: true,
},
},
},
},
},
seedTemplate: &v1alpha1.Template{
TypeMeta: metav1.TypeMeta{
Kind: "Template",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "debian",
Namespace: "default",
},
Spec: v1alpha1.TemplateSpec{
Data: &templateWithDiskTemplate,
},
Status: v1alpha1.TemplateStatus{},
},
seedWorkflow: &v1alpha1.Workflow{
TypeMeta: metav1.TypeMeta{
Kind: "Workflow",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "debian",
Namespace: "default",
},
Spec: v1alpha1.WorkflowSpec{
TemplateRef: "debian",
HardwareRef: "machine1",
HardwareMap: map[string]string{
"device_1": "3c:ec:ef:4c:4f:54",
},
},
Status: v1alpha1.WorkflowStatus{},
},
req: reconcile.Request{
NamespacedName: types.NamespacedName{
Name: "debian",
Namespace: "default",
},
},
want: reconcile.Result{},
wantWflow: &v1alpha1.Workflow{
TypeMeta: metav1.TypeMeta{
Kind: "Workflow",
APIVersion: "tinkerbell.org/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: "1000",
Name: "debian",
Namespace: "default",
},
Spec: v1alpha1.WorkflowSpec{
TemplateRef: "debian",
HardwareRef: "machine1",
HardwareMap: map[string]string{
"device_1": "3c:ec:ef:4c:4f:54",
},
},
Status: v1alpha1.WorkflowStatus{
State: v1alpha1.WorkflowStatePending,
GlobalTimeout: 1800,
Tasks: []v1alpha1.Task{
{
Name: "os-installation",

WorkerAddr: "3c:ec:ef:4c:4f:54",
Volumes: []string{
"/dev:/dev",
"/dev/console:/dev/console",
"/lib/firmware:/lib/firmware:ro",
},
Actions: []v1alpha1.Action{
{
Name: "stream-debian-image",
Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0",
Timeout: 600,
Environment: map[string]string{
"COMPRESSED": "true",
"DEST_DISK": "/dev/nvme0n1",
"IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz",
},
Status: v1alpha1.WorkflowStatePending,
},
},
},
},
},
},
wantErr: nil,
},
}

for _, tc := range cases {
Expand Down
Loading

0 comments on commit a0e9350

Please sign in to comment.