Skip to content

Commit

Permalink
feat: add the ability to read from external files
Browse files Browse the repository at this point in the history
Signed-off-by: Jesús Fernández <jesus.fernandez@nexthink.com>
  • Loading branch information
Jesús Fernández committed Feb 13, 2024
1 parent 1fbc444 commit 1857e73
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ about `crossplane beta render`.
| [`getResourceCondition`](example/functions/getResourceCondition) | Helper function to retreive conditions of resources |
| [`setResourceNameAnnotation`](example/inline) | Returns the special resource-name annotation with given name |
| [`include`](example/functions/include) | Outputs template as a string |
| [`readFile`](example/functions/readfile) | Outputs the contents of a given file |
| [`readFiles`](example/functions/readfile) | Outputs the contents of multiple files |

## Developing this function

Expand Down
19 changes: 19 additions & 0 deletions example/read-file/composition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: example-read-file
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1beta1
kind: XR
mode: Pipeline
pipeline:
- step: render-templates
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: FileSystem
filesystem:
dirPath: ./templates
32 changes: 32 additions & 0 deletions example/read-file/deployment_runtime_configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: DeploymentRuntimeConfig
metadata:
name: function-go-templating
spec:
deploymentTemplate:
metadata:
labels:
kubernetes.io/part-of: crossplane
spec:
selector: {}
template:
spec:
containers:
- name: package-runtime
args:
- --debug
volumeMounts:
- mountPath: /templates
name: templates
readOnly: true
- mountPath: /files
name: files
readOnly: true
volumes:
- name: templates
configMap:
name: read-file-example-templates
- name: files
configMap:
name: read-file-example-files

8 changes: 8 additions & 0 deletions example/read-file/functions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-go-templating
spec:
package: xpkg.upbound.io/crossplane-contrib/function-go-templating:v0.4.0
runtimeConfigRef:
name: function-go-templating
14 changes: 14 additions & 0 deletions example/read-file/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- composition.yaml
- functions.yaml
configMapGenerator:
- name: read-file-example-templates
files:
- templates/workspace.yaml
- name: read-file-example-files
files:
- terraform/workspace.tf
generatorOptions:
disableNameSuffixHash: true
20 changes: 20 additions & 0 deletions example/read-file/templates/workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- $spec := .observed.composite.resource.spec }}
---
apiVersion: tf.upbound.io/v1beta1
kind: Workspace
metadata:
annotations: {{ setResourceNameAnnotation "workspace" }}
labels:
testing.upbound.io/example-name: test-read-file
name: observe-only-workspace
spec:
forProvider:
source: Inline
module: {{ readFile "files/workspace.tf" | quote }}
{{/* Alternatively
module: {{ readFiles "files/*.tf" "\n" | quote }}
*/}}
varmap:
region: {{ $spec.region }}
providerConfigRef:
name: default
17 changes: 17 additions & 0 deletions example/read-file/terraform/workspace.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
provider "aws" {
region = var.region
}

data "aws_regions" "available" {
all_regions = true
}

output "available_regions" {
value = join(",", data.aws_regions.available.names)
}

variable "region" {
description = "AWS region"
type = string
}

6 changes: 6 additions & 0 deletions example/read-file/xr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: example.crossplane.io/v1beta1
kind: XR
metadata:
name: example
spec:
region: us-east-1
36 changes: 34 additions & 2 deletions function_maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package main
import (
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
"text/template"
"time"

"gopkg.in/yaml.v3"

sprig "github.com/Masterminds/sprig/v3"
"github.com/crossplane-contrib/function-go-templating/input/v1beta1"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
Expand All @@ -24,6 +25,8 @@ var funcMaps = []template.FuncMap{
"toYaml": toYaml,
"fromYaml": fromYaml,
"getResourceCondition": getResourceCondition,
"readFile": readFile,
"readFiles": readFiles,
"setResourceNameAnnotation": setResourceNameAnnotation,
},
}
Expand Down Expand Up @@ -103,4 +106,33 @@ func initInclude(t *template.Template) func(string, interface{}) (string, error)
return buf.String(), err
}

}
}

func readFiles(pattern string, sep string) (string, error) {
filenames, err := getFiles(pattern)
if err != nil {
return "", err
}
contents := []string{}
for _, f := range filenames {
content, _ := readFile(f)
contents = append(contents, content)
}
return strings.Join(contents, sep), nil
}

func readFile(filename string) (string, error) {
content, err := os.ReadFile(filename)
if err != nil {
return "", err
}
return string(content), nil
}

func getFiles(pattern string) ([]string, error) {
matches, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
return matches, nil
}
72 changes: 72 additions & 0 deletions function_maps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,75 @@ Must capture output: {{$var}}
})
}
}

func Test_getFiles(t *testing.T) {
type args struct {
pattern string
}
type want struct {
rsp []string
}

cases := map[string]struct {
reason string
args args
want want
}{
"GetFilesWithGivenPattern": {
reason: "Should return list of files matching *test.go",
args: args{
pattern: "testdata/*.txt",
},
want: want{
rsp: []string{
"testdata/sample.txt",
"testdata/testfile.txt",
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
rsp, _ := getFiles(tc.args.pattern)

if diff := cmp.Diff(tc.want.rsp, rsp, protocmp.Transform()); diff != "" {
t.Errorf("%s\ngetFiles(...): -want rsp, +got rsp:\n%s", tc.reason, diff)
}
})
}
}

func Test_readFiles(t *testing.T) {
type args struct {
pattern string
}
type want struct {
rsp string
}

cases := map[string]struct {
reason string
args args
want want
}{
"ReadFile": {
reason: "Should return contents of testdata/*.txt",
args: args{
pattern: "testdata/*.txt",
},
want: want{
rsp: "Hello world",
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
rsp, _ := readFiles(tc.args.pattern, " ")

if diff := cmp.Diff(tc.want.rsp, rsp, protocmp.Transform()); diff != "" {
t.Errorf("%s\readFile(...): -want rsp, +got rsp:\n%s", tc.reason, diff)
}
})
}
}

1 change: 1 addition & 0 deletions testdata/sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello
1 change: 1 addition & 0 deletions testdata/testfile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
world

0 comments on commit 1857e73

Please sign in to comment.