diff --git a/controller/machine/template.go b/controller/machine/template.go index c1996ec0..138e9142 100644 --- a/controller/machine/template.go +++ b/controller/machine/template.go @@ -12,10 +12,127 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" +) + +var ( + // ErrMissingName is the error returned when the WorfklowTemplate Name is not specified. + ErrMissingName = fmt.Errorf("name can't be empty") + + // ErrMissingImageURL is the error returned when the WorfklowTemplate ImageURL is not specified. + ErrMissingImageURL = fmt.Errorf("imageURL can't be empty") +) - "github.com/tinkerbell/cluster-api-provider-tinkerbell/internal/templates" +const ( + workflowTemplate = ` +version: "0.1" +name: {{.Name}} +global_timeout: 6000 +tasks: + - name: "{{.Name}}" + worker: "{{.DeviceTemplateName}}" + volumes: + - /dev:/dev + - /dev/console:/dev/console + - /lib/firmware:/lib/firmware:ro + actions: + - name: "stream-image" + image: quay.io/tinkerbell-actions/oci2disk:v1.0.0 + timeout: 600 + environment: + IMG_URL: {{.ImageURL}} + DEST_DISK: {{.DestDisk}} + COMPRESSED: true + - name: "add-tink-cloud-init-config" + image: quay.io/tinkerbell-actions/writefile:v1.0.0 + timeout: 90 + environment: + DEST_DISK: {{.DestPartition}} + FS_TYPE: ext4 + DEST_PATH: /etc/cloud/cloud.cfg.d/10_tinkerbell.cfg + UID: 0 + GID: 0 + MODE: 0600 + DIRMODE: 0700 + CONTENTS: | + datasource: + Ec2: + metadata_urls: ["{{.MetadataURL}}"] + strict_id: false + system_info: + default_user: + name: tink + groups: [wheel, adm] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + manage_etc_hosts: localhost + warnings: + dsid_missing_source: off + - name: "add-tink-cloud-init-ds-config" + image: quay.io/tinkerbell-actions/writefile:v1.0.0 + timeout: 90 + environment: + DEST_DISK: {{.DestPartition}} + FS_TYPE: ext4 + DEST_PATH: /etc/cloud/ds-identify.cfg + UID: 0 + GID: 0 + MODE: 0600 + DIRMODE: 0700 + CONTENTS: | + datasource: Ec2 + - name: "kexec-image" + image: ghcr.io/jacobweinstock/waitdaemon:0.1.2 + timeout: 90 + pid: host + environment: + BLOCK_DEVICE: {{.DestPartition}} + FS_TYPE: ext4 + IMAGE: quay.io/tinkerbell-actions/kexec:v1.0.0 + WAIT_SECONDS: 10 + volumes: + - /var/run/docker.sock:/var/run/docker.sock +` ) +// WorkflowTemplate is a helper struct for rendering CAPT Template data. +type WorkflowTemplate struct { + Name string + MetadataURL string + ImageURL string + DestDisk string + DestPartition string + DeviceTemplateName string +} + +// Render renders workflow template for a given machine including user-data. +func (wt *WorkflowTemplate) Render() (string, error) { + if wt.Name == "" { + return "", ErrMissingName + } + + if wt.ImageURL == "" { + return "", ErrMissingImageURL + } + + if wt.DeviceTemplateName == "" { + wt.DeviceTemplateName = "{{.device_1}}" + } + + tpl, err := template.New("template").Parse(workflowTemplate) + if err != nil { + return "", fmt.Errorf("unable to parse template: %w", err) + } + + buf := &bytes.Buffer{} + + err = tpl.Execute(buf, wt) + if err != nil { + return "", fmt.Errorf("unable to execute template: %w", err) + } + + return buf.String(), nil +} + func (scope *machineReconcileScope) templateExists() (bool, error) { namespacedName := types.NamespacedName{ Name: scope.tinkerbellMachine.Name, @@ -56,7 +173,7 @@ func (scope *machineReconcileScope) createTemplate(hw *tinkv1.Hardware) error { metadataURL := fmt.Sprintf("http://%s:50061", metadataIP) - workflowTemplate := templates.WorkflowTemplate{ + workflowTemplate := WorkflowTemplate{ Name: scope.tinkerbellMachine.Name, MetadataURL: metadataURL, ImageURL: imageURL, diff --git a/internal/templates/templates_test.go b/controller/machine/template_test.go similarity index 74% rename from internal/templates/templates_test.go rename to controller/machine/template_test.go index f65e3d73..6acb42a8 100644 --- a/internal/templates/templates_test.go +++ b/controller/machine/template_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package templates_test +package machine_test import ( "testing" @@ -22,11 +22,11 @@ import ( . "github.com/onsi/gomega" //nolint:revive // one day we will remove gomega "sigs.k8s.io/yaml" - "github.com/tinkerbell/cluster-api-provider-tinkerbell/internal/templates" + "github.com/tinkerbell/cluster-api-provider-tinkerbell/controller/machine" ) -func validWorkflowTemplate() *templates.WorkflowTemplate { - return &templates.WorkflowTemplate{ +func validWorkflowTemplate() *machine.WorkflowTemplate { + return &machine.WorkflowTemplate{ Name: "foo", MetadataURL: "http://10.10.10.10", ImageURL: "http://foo.bar.baz/do/it", @@ -40,33 +40,33 @@ func Test_Cloud_config_template(t *testing.T) { t.Parallel() cases := map[string]struct { - mutateF func(*templates.WorkflowTemplate) + mutateF func(*machine.WorkflowTemplate) expectError bool expectedError error - validateF func(*testing.T, *templates.WorkflowTemplate, string) + validateF func(*testing.T, *machine.WorkflowTemplate, string) }{ "requires_non_empty_ImageURL": { - mutateF: func(wt *templates.WorkflowTemplate) { + mutateF: func(wt *machine.WorkflowTemplate) { wt.ImageURL = "" }, expectError: true, - expectedError: templates.ErrMissingImageURL, + expectedError: machine.ErrMissingImageURL, }, "requires_non_empty_Name": { - mutateF: func(wt *templates.WorkflowTemplate) { + mutateF: func(wt *machine.WorkflowTemplate) { wt.Name = "" }, expectError: true, - expectedError: templates.ErrMissingName, + expectedError: machine.ErrMissingName, }, "renders_with_valid_config": { - mutateF: func(_ *templates.WorkflowTemplate) {}, + mutateF: func(_ *machine.WorkflowTemplate) {}, }, "rendered_output_should_be_valid_YAML": { - validateF: func(t *testing.T, _ *templates.WorkflowTemplate, renderResult string) { //nolint:thelper + validateF: func(t *testing.T, _ *machine.WorkflowTemplate, renderResult string) { //nolint:thelper g := NewWithT(t) x := &map[string]interface{}{} diff --git a/go.mod b/go.mod index 8a18a387..98bf6032 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/google/uuid v1.6.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.33.1 - github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 github.com/spf13/pflag v1.0.5 github.com/tinkerbell/rufio v0.3.3 @@ -62,6 +61,7 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.11 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect diff --git a/internal/templates/templates.go b/internal/templates/templates.go deleted file mode 100644 index 7ed6aebe..00000000 --- a/internal/templates/templates.go +++ /dev/null @@ -1,146 +0,0 @@ -/* -Copyright 2022 The Tinkerbell Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package templates provides methods for rendering templates used for -// creating Tinkerbell machines for ClusterAPI. -package templates - -import ( - "bytes" - "fmt" - "text/template" - - "github.com/pkg/errors" -) - -var ( - // ErrMissingName is the error returned when the WorfklowTemplate Name is not specified. - ErrMissingName = fmt.Errorf("name can't be empty") - - // ErrMissingImageURL is the error returned when the WorfklowTemplate ImageURL is not specified. - ErrMissingImageURL = fmt.Errorf("imageURL can't be empty") -) - -// WorkflowTemplate is a helper struct for rendering CAPT Template data. -type WorkflowTemplate struct { - Name string - MetadataURL string - ImageURL string - DestDisk string - DestPartition string - DeviceTemplateName string -} - -// Render renders workflow template for a given machine including user-data. -func (wt *WorkflowTemplate) Render() (string, error) { - if wt.Name == "" { - return "", ErrMissingName - } - - if wt.ImageURL == "" { - return "", ErrMissingImageURL - } - - if wt.DeviceTemplateName == "" { - wt.DeviceTemplateName = "{{.device_1}}" - } - - tpl, err := template.New("template").Parse(workflowTemplate) - if err != nil { - return "", errors.Wrap(err, "unable to parse template") - } - - buf := &bytes.Buffer{} - - err = tpl.Execute(buf, wt) - if err != nil { - return "", errors.Wrap(err, "unable to execute template") - } - - return buf.String(), nil -} - -const ( - workflowTemplate = ` -version: "0.1" -name: {{.Name}} -global_timeout: 6000 -tasks: - - name: "{{.Name}}" - worker: "{{.DeviceTemplateName}}" - volumes: - - /dev:/dev - - /dev/console:/dev/console - - /lib/firmware:/lib/firmware:ro - actions: - - name: "stream-image" - image: quay.io/tinkerbell-actions/oci2disk:v1.0.0 - timeout: 600 - environment: - IMG_URL: {{.ImageURL}} - DEST_DISK: {{.DestDisk}} - COMPRESSED: true - - name: "add-tink-cloud-init-config" - image: quay.io/tinkerbell-actions/writefile:v1.0.0 - timeout: 90 - environment: - DEST_DISK: {{.DestPartition}} - FS_TYPE: ext4 - DEST_PATH: /etc/cloud/cloud.cfg.d/10_tinkerbell.cfg - UID: 0 - GID: 0 - MODE: 0600 - DIRMODE: 0700 - CONTENTS: | - datasource: - Ec2: - metadata_urls: ["{{.MetadataURL}}"] - strict_id: false - system_info: - default_user: - name: tink - groups: [wheel, adm] - sudo: ["ALL=(ALL) NOPASSWD:ALL"] - shell: /bin/bash - manage_etc_hosts: localhost - warnings: - dsid_missing_source: off - - name: "add-tink-cloud-init-ds-config" - image: quay.io/tinkerbell-actions/writefile:v1.0.0 - timeout: 90 - environment: - DEST_DISK: {{.DestPartition}} - FS_TYPE: ext4 - DEST_PATH: /etc/cloud/ds-identify.cfg - UID: 0 - GID: 0 - MODE: 0600 - DIRMODE: 0700 - CONTENTS: | - datasource: Ec2 - - name: "kexec-image" - image: ghcr.io/jacobweinstock/waitdaemon:0.1.2 - timeout: 90 - pid: host - environment: - BLOCK_DEVICE: {{.DestPartition}} - FS_TYPE: ext4 - IMAGE: quay.io/tinkerbell-actions/kexec:v1.0.0 - WAIT_SECONDS: 10 - volumes: - - /var/run/docker.sock:/var/run/docker.sock -` -)