Repository: crossplane/crossplane
Pull request #6087 (2024-11-11 13:51:06)

fix(crank): add function as input for extensions in cli for validate
<!--
Thank you for helping to improve Crossplane! Please read the contribution docs
(linked below) if this is your first Crossplane pull request.
-->

### Description of your changes
add the option to add function pkg for `crossplane beta validate function.yaml composition.yaml`
update unit-test


<!--
Briefly describe what this pull request does, and how it is covered by tests.
Be proactive - direct your reviewers' attention to anything that needs special
consideration.

We love pull requests that fix an open issue. If yours does, use the below line
to indicate which issue it fixes, for example "Fixes #500".
-->

Fixes # 

I have: <!--You MUST either [x] check or [ ] ~strike through~ every item.-->

- [x] Read and followed Crossplane's [contribution process].
- [x] Run `earthly +reviewable` to ensure this PR is ready for review.
- [x] Added or updated unit tests.
- [x] Added or updated e2e tests.
- [x] Linked a PR or a [docs tracking issue] to [document this change].
- [x] Added `backport release-x.y` labels to auto-backport this PR.

Need help with this checklist? See the [cheat sheet].

[contribution process]: https://github.com/crossplane/crossplane/tree/main/contributing
[docs tracking issue]: https://github.com/crossplane/docs/issues/new
[document this change]: https://docs.crossplane.io/contribute/contribute
[cheat sheet]: https://github.com/crossplane/crossplane/tree/main/contributing#checklist-cheat-sheet



Patch:
diff --git a/cmd/crank/beta/validate/cmd.go b/cmd/crank/beta/validate/cmd.go
index 783268dce..8db98247c 100644
--- a/cmd/crank/beta/validate/cmd.go
+++ b/cmd/crank/beta/validate/cmd.go
@@ -52,7 +52,7 @@ type Cmd struct {
 func (c *Cmd) Help() string {
 	return `
 This command validates the provided Crossplane resources against the schemas of the provided extensions like XRDs,
-CRDs, providers, and configurations. The output of the "crossplane render" command can be
+CRDs, providers, functions and configurations. The output of the "crossplane render" command can be
 piped to this validate command in order to rapidly validate on the outputs of the composition development experience.
 
 If providers or configurations are provided as extensions, they will be downloaded and loaded as CRDs before performing
diff --git a/cmd/crank/beta/validate/manager.go b/cmd/crank/beta/validate/manager.go
index 2654d5b56..737bff11f 100644
--- a/cmd/crank/beta/validate/manager.go
+++ b/cmd/crank/beta/validate/manager.go
@@ -135,7 +135,16 @@ func (m *Manager) PrepExtensions(extensions []*unstructured.Unstructured) error
 			paved := fieldpath.Pave(e.Object)
 			image, err := paved.GetString("spec.package")
 			if err != nil {
-				return errors.Wrapf(err, "cannot get package image")
+				return errors.Wrapf(err, "cannot get provider package image")
+			}
+
+			m.deps[image] = true
+
+		case schema.GroupKind{Group: "pkg.crossplane.io", Kind: "Function"}:
+			paved := fieldpath.Pave(e.Object)
+			image, err := paved.GetString("spec.package")
+			if err != nil {
+				return errors.Wrapf(err, "cannot get function package image")
 			}
 
 			m.deps[image] = true


Test patch:
diff --git a/cmd/crank/beta/validate/manager_test.go b/cmd/crank/beta/validate/manager_test.go
index fd35c2161..be2ff8ecf 100644
--- a/cmd/crank/beta/validate/manager_test.go
+++ b/cmd/crank/beta/validate/manager_test.go
@@ -52,6 +52,15 @@ metadata:
   name: provider-dep-1
 ---
 
+`)
+
+	// provider-test:v1.3.0.
+	provider2Yaml = []byte(`apiVersion: meta.pkg.crossplane.io/v1
+kind: Provider
+metadata:
+  name: provider-test
+---
+
 `)
 
 	// function-dep-1:v1.3.0.
@@ -61,6 +70,15 @@ metadata:
   name: function-dep-1
 ---
 
+`)
+
+	// function-test:v1.3.0.
+	func2Yaml = []byte(`apiVersion: meta.pkg.crossplane.io/v1beta1
+kind: Function
+metadata:
+  name: function-test
+---
+
 `)
 
 	// config-dep-1:v1.3.0.
@@ -95,7 +113,9 @@ spec:
 func TestConfigurationTypeSupport(t *testing.T) {
 	confpkg := static.NewLayer(configPkg, types.OCILayer)
 	pd := static.NewLayer(providerYaml, types.OCILayer)
+	p2 := static.NewLayer(provider2Yaml, types.OCILayer)
 	fd := static.NewLayer(funcYaml, types.OCILayer)
+	f2 := static.NewLayer(func2Yaml, types.OCILayer)
 
 	fetchMockFunc := func(image string) (*conregv1.Layer, error) {
 		switch image {
@@ -103,8 +123,13 @@ func TestConfigurationTypeSupport(t *testing.T) {
 			return &confpkg, nil
 		case "provider-dep-1:v1.3.0":
 			return &pd, nil
+		case "provider-test:v1.3.0":
+			return &p2, nil
 		case "function-dep-1:v1.3.0":
 			return &fd, nil
+		case "function-test:v1.3.0":
+			return &f2, nil
+
 		default:
 			return nil, fmt.Errorf("unknown image: %s", image)
 		}
@@ -204,6 +229,14 @@ func TestConfigurationTypeSupport(t *testing.T) {
 										"function": "function-dep-1",
 										"version":  "v1.3.0",
 									},
+									{
+										"function": "function-test",
+										"version":  "v1.3.0",
+									},
+									{
+										"function": "provider-test",
+										"version":  "v1.3.0",
+									},
 								},
 							},
 						},
@@ -226,7 +259,71 @@ func TestConfigurationTypeSupport(t *testing.T) {
 			want: want{
 				err:   nil,
 				confs: 2, // Configuration.meta and Configuration.pkg
-				deps:  3, // 1 Configuration.pkg, 1 provider, 1 function
+				deps:  5, // 1 Configuration.pkg, 2 provider, 2 function
+			},
+		},
+		"FunctionPkg": {
+			// function-test
+			reason: "Function pkg added",
+			args: args{
+				extensions: []*unstructured.Unstructured{
+					{
+						Object: map[string]interface{}{
+							"apiVersion": "pkg.crossplane.io/v1",
+							"kind":       "Function",
+							"metadata": map[string]interface{}{
+								"name": "function-test",
+							},
+							"spec": map[string]interface{}{
+								"package": "function-test:v1.3.0",
+							},
+						},
+					},
+				},
+				fetchMock: fetchMockFunc,
+			},
+			want: want{
+				err:   nil,
+				confs: 0,
+				deps:  1, // Function.pkg from remote
+			},
+		},
+		"MultipleFunctionPkg": {
+			// function-test
+			reason: "Function pkg added",
+			args: args{
+				extensions: []*unstructured.Unstructured{
+					{
+						Object: map[string]interface{}{
+							"apiVersion": "pkg.crossplane.io/v1",
+							"kind":       "Function",
+							"metadata": map[string]interface{}{
+								"name": "function-test",
+							},
+							"spec": map[string]interface{}{
+								"package": "function-test:v1.3.0",
+							},
+						},
+					},
+					{
+						Object: map[string]interface{}{
+							"apiVersion": "pkg.crossplane.io/v1",
+							"kind":       "Function",
+							"metadata": map[string]interface{}{
+								"name": "function-dep-1",
+							},
+							"spec": map[string]interface{}{
+								"package": "function-dep-1:v1.3.0",
+							},
+						},
+					},
+				},
+				fetchMock: fetchMockFunc,
+			},
+			want: want{
+				err:   nil,
+				confs: 0,
+				deps:  2, // 2 Function.pkg from remote
 			},
 		},
 	}