-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into ks20drptcrtype
- Loading branch information
Showing
4 changed files
with
227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package template | ||
|
||
import ( | ||
"bytes" | ||
"embed" | ||
"io" | ||
"io/fs" | ||
"text/template" | ||
|
||
"github.com/pkg/errors" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/yaml" | ||
"k8s.io/client-go/kubernetes/scheme" | ||
) | ||
|
||
// Variables contains all the available variables that are supported by the templates | ||
type Variables struct { | ||
Namespace string | ||
} | ||
|
||
// LoadObjectsFromEmbedFS loads all the kubernetes objects from an embedded filesystem and returns a list of Unstructured objects that can be applied in the cluster. | ||
// The function will return all the objects it finds starting from the root of the embedded filesystem. | ||
func LoadObjectsFromEmbedFS(efs *embed.FS, variables *Variables) ([]*unstructured.Unstructured, error) { | ||
var objects []*unstructured.Unstructured | ||
entries, err := getAllTemplateNames(efs) | ||
if err != nil { | ||
return objects, err | ||
} | ||
for _, templatePath := range entries { | ||
templateContent, err := efs.ReadFile(templatePath) | ||
if err != nil { | ||
return objects, err | ||
} | ||
buf, err := replaceTemplateVariables(templatePath, templateContent, variables) | ||
if err != nil { | ||
return objects, err | ||
} | ||
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(buf.Bytes()), 100) | ||
for { | ||
var rawExt runtime.RawExtension | ||
if err := decoder.Decode(&rawExt); err != nil { | ||
if errors.Is(err, io.EOF) { | ||
break | ||
} | ||
return objects, err | ||
} | ||
rawExt.Raw = bytes.TrimSpace(rawExt.Raw) | ||
if len(rawExt.Raw) == 0 || bytes.Equal(rawExt.Raw, []byte("null")) { | ||
continue | ||
} | ||
unstructuredObj := &unstructured.Unstructured{} | ||
_, _, err = scheme.Codecs.UniversalDeserializer().Decode(rawExt.Raw, nil, unstructuredObj) | ||
if err != nil { | ||
return objects, err | ||
} | ||
objects = append(objects, unstructuredObj) | ||
} | ||
} | ||
return objects, nil | ||
} | ||
|
||
// replaceTemplateVariables replaces all the variables in the given template and returns a buffer with the evaluated content | ||
func replaceTemplateVariables(templateName string, templateContent []byte, variables *Variables) (bytes.Buffer, error) { | ||
var buf bytes.Buffer | ||
tmpl, err := template.New(templateName).Parse(string(templateContent)) | ||
if err != nil { | ||
return buf, err | ||
} | ||
err = tmpl.Execute(&buf, variables) | ||
return buf, err | ||
} | ||
|
||
// getAllTemplateNames reads the embedded filesystem and returns a list with all the filenames | ||
func getAllTemplateNames(efs *embed.FS) (files []string, err error) { | ||
err = fs.WalkDir(efs, ".", func(path string, d fs.DirEntry, err error) error { | ||
if d.IsDir() { | ||
return nil | ||
} | ||
files = append(files, path) | ||
return nil | ||
}) | ||
return files, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package template_test | ||
|
||
import ( | ||
"embed" | ||
"testing" | ||
|
||
"github.com/codeready-toolchain/toolchain-common/pkg/template" | ||
"github.com/codeready-toolchain/toolchain-common/pkg/test" | ||
"github.com/stretchr/testify/require" | ||
v1 "k8s.io/api/core/v1" | ||
rbac "k8s.io/api/rbac/v1" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
//go:embed testdata/* | ||
var EFS embed.FS | ||
|
||
//go:embed testdata/host/* | ||
var hostFS embed.FS | ||
|
||
//go:embed testdata/member/* | ||
var memberFS embed.FS | ||
|
||
func TestLoadObjectsFromEmbedFS(t *testing.T) { | ||
t.Run("loads objects recursively from all subdirectories", func(t *testing.T) { | ||
// when | ||
allObjects, err := template.LoadObjectsFromEmbedFS(&EFS, &template.Variables{Namespace: test.HostOperatorNs}) | ||
require.NoError(t, err) | ||
hostFolderObjects, err := template.LoadObjectsFromEmbedFS(&hostFS, &template.Variables{Namespace: test.HostOperatorNs}) | ||
require.NoError(t, err) | ||
memberFolderObjects, err := template.LoadObjectsFromEmbedFS(&memberFS, nil) | ||
require.NoError(t, err) | ||
// then | ||
require.NotNil(t, allObjects) | ||
require.NotNil(t, hostFolderObjects) | ||
require.NotNil(t, memberFolderObjects) | ||
require.Equal(t, 4, len(allObjects), "invalid number of expected total objects") | ||
require.Equal(t, 3, len(hostFolderObjects), "invalid number of expected objects from host folder") | ||
require.Equal(t, 1, len(memberFolderObjects), "invalid number of expected objects from member folder") | ||
// check match for the expected objects | ||
checkExpectedObjects(t, allObjects) | ||
}) | ||
|
||
t.Run("error - when variables are not provided", func(t *testing.T) { | ||
// when | ||
// we do not pass required variables for the templates that requires variables | ||
objects, err := template.LoadObjectsFromEmbedFS(&hostFS, nil) | ||
// then | ||
// we should get back an error | ||
require.Error(t, err) | ||
require.Nil(t, objects) | ||
}) | ||
} | ||
|
||
func checkExpectedObjects(t *testing.T, objects []*unstructured.Unstructured) { | ||
sa := &v1.ServiceAccount{} | ||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(objects[0].Object, sa) | ||
require.NoError(t, err) | ||
require.Equal(t, "toolchaincluster-host", sa.GetName()) | ||
require.Equal(t, "toolchain-host-operator", sa.GetNamespace()) | ||
role := &rbac.Role{} | ||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(objects[1].Object, role) | ||
require.NoError(t, err) | ||
require.Equal(t, "toolchaincluster-host", role.GetName()) | ||
require.Equal(t, "toolchain-host-operator", role.GetNamespace()) | ||
require.Equal(t, []rbac.PolicyRule{ | ||
{ | ||
APIGroups: []string{"toolchain.dev.openshift.com"}, | ||
Resources: []string{"*"}, | ||
Verbs: []string{"*"}, | ||
}, | ||
}, role.Rules) | ||
roleBinding := &rbac.RoleBinding{} | ||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(objects[2].Object, roleBinding) | ||
require.NoError(t, err) | ||
require.Equal(t, "toolchaincluster-host", roleBinding.GetName()) | ||
require.Equal(t, "toolchain-host-operator", roleBinding.GetNamespace()) | ||
require.Equal(t, rbac.RoleRef{ | ||
APIGroup: "rbac.authorization.k8s.io", | ||
Kind: "Role", | ||
Name: "toolchaincluster-host", | ||
}, roleBinding.RoleRef) | ||
require.Equal(t, 1, len(roleBinding.Subjects)) | ||
require.Equal(t, rbac.Subject{ | ||
Kind: "ServiceAccount", | ||
Name: "toolchaincluster-host", | ||
}, roleBinding.Subjects[0]) | ||
clusterRole := &rbac.ClusterRole{} | ||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(objects[3].Object, clusterRole) | ||
require.NoError(t, err) | ||
require.Equal(t, "member-toolchaincluster-cr", clusterRole.GetName()) | ||
require.Equal(t, []rbac.PolicyRule{ | ||
{ | ||
APIGroups: []string{"authentication.k8s.io"}, | ||
Resources: []string{"tokenreviews"}, | ||
Verbs: []string{"create"}, | ||
}, | ||
}, clusterRole.Rules) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
--- | ||
apiVersion: v1 | ||
kind: ServiceAccount | ||
metadata: | ||
name: toolchaincluster-host | ||
namespace: {{.Namespace}} | ||
--- | ||
kind: Role | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
metadata: | ||
name: toolchaincluster-host | ||
namespace: {{.Namespace}} | ||
rules: | ||
- apiGroups: | ||
- toolchain.dev.openshift.com | ||
resources: | ||
- "*" | ||
verbs: | ||
- "*" | ||
--- | ||
kind: RoleBinding | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
metadata: | ||
name: toolchaincluster-host | ||
namespace: {{.Namespace}} | ||
subjects: | ||
- kind: ServiceAccount | ||
name: toolchaincluster-host | ||
roleRef: | ||
kind: Role | ||
name: toolchaincluster-host | ||
apiGroup: rbac.authorization.k8s.io |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
kind: ClusterRole | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
metadata: | ||
name: member-toolchaincluster-cr | ||
rules: | ||
- apiGroups: | ||
- authentication.k8s.io | ||
resources: | ||
- tokenreviews | ||
verbs: | ||
- create |