Skip to content

Commit

Permalink
refactor: move the xr and state resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Peefy committed Dec 20, 2023
1 parent f5bbcf7 commit cca0f99
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 223 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Crossplane Composition Functions using KCL

[![CI](https://github.com/crossplane/function-template-go/actions/workflows/ci.yml/badge.svg)](https://github.com/crossplane/function-template-go/actions/workflows/ci.yml)
[![CI](https://kcl-lang.io/crossplane-kcl/actions/workflows/ci.yml/badge.svg)](https://kcl-lang.io/crossplane-kcl/actions/workflows/ci.yml)

## Developing

Expand Down
112 changes: 28 additions & 84 deletions fn.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import (

fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1"
"github.com/crossplane/function-sdk-go/request"
"github.com/crossplane/function-sdk-go/resource"
"github.com/crossplane/function-sdk-go/resource/composed"
"github.com/crossplane/function-sdk-go/response"

"github.com/crossplane/function-template-go/input/v1beta1"
"kcl-lang.io/crossplane-kcl/input/v1beta1"
pkgresource "kcl-lang.io/crossplane-kcl/pkg/resource"

"sigs.k8s.io/yaml"
)
Expand Down Expand Up @@ -72,10 +71,11 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ
return rsp, nil
}
log.Debug(fmt.Sprintf("DesiredComposed resources: %d", len(desired)))

// The composed resources desired by any previous Functions in the pipeline.
observed, err := request.GetObservedComposedResources(req)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot get desired composed resources from %T", req))
response.Fatal(rsp, errors.Wrapf(err, "cannot get observed composed resources from %T", req))
return rsp, nil
}
log.Debug(fmt.Sprintf("ObservedComposed resources: %d", len(observed)))
Expand All @@ -84,92 +84,43 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ
inputBytes, outputBytes := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
kclRunBytes, err := yaml.Marshal(in)
if err != nil {
response.Fatal(rsp, errors.Wrap(err, "cannot get observed composite resource"))
response.Fatal(rsp, errors.Wrap(err, "cannot marshal input to yaml"))
return rsp, nil
}
inputBytes.Write(kclRunBytes)
// Run pipeline to get the result mutated or validated by the KCL source.
pipeline := kio.NewPipeline(inputBytes, outputBytes, false)
if err := pipeline.Execute(); err != nil {
response.Fatal(rsp, errors.Wrap(err, "cannot get observed composite resource"))
response.Fatal(rsp, errors.Wrap(err, "failed to run kcl function pipelines"))
return rsp, nil
}

output := successOutput{
target: in.Spec.Target,
}
conf := addResourcesConf{
overwrite: true,
}

data, err := buildData(outputBytes.Bytes())

data, err := pkgresource.DataResourcesFromYaml(outputBytes.Bytes())
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot match resources to desired"))
response.Fatal(rsp, errors.Wrapf(err, "cannot parse data resources from the pipeline output in %T", rsp))
return rsp, nil
}

switch in.Spec.Target {
case v1beta1.XR:
conf.data = data
if err := addResourcesTo(dxr, conf); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot add resources to XR"))
return rsp, nil
}
output.object = dxr
output.msgCount = 1
case v1beta1.PatchDesired:
log.Debug("Matching PatchDesired Resources")
desiredMatches, err := matchResources(desired, data)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot match resources to desired"))
return rsp, nil
}
log.Debug(fmt.Sprintf("Matched %+v", desiredMatches))

if err := addResourcesTo(desiredMatches, conf); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot update existing DesiredComposed"))
return rsp, nil
}
output.object = data
output.msgCount = len(data)
case v1beta1.PatchResources:
// Render the List of DesiredComposed resources from the input
// Update the existing desired map to be created as a base
for _, r := range in.Spec.Resources {
tmp := &resource.DesiredComposed{Resource: composed.New()}

if err := renderFromJSON(tmp.Resource, r.Base.Raw); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot parse base template of composed resource %q", r.Name))
return rsp, nil
}

desired[resource.Name(r.Name)] = tmp
}
// Match the data to the desired resources
desiredMatches, err := matchResources(desired, data)
var resources pkgresource.ResourceList
for _, r := range in.Spec.Resources {
base, err := pkgresource.JsonByteToUnstructured(r.Base.Raw)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot match resources to input resources"))
return rsp, nil
}

if err := addResourcesTo(desiredMatches, conf); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot add resources to DesiredComposed"))
return rsp, nil
}
output.object = data
output.msgCount = len(data)
case v1beta1.Resources:
conf.basename = in.Name
conf.data = data
if err := addResourcesTo(desired, conf); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot add resources to DesiredComposed"))
response.Fatal(rsp, errors.Wrapf(err, "cannot parse data resources from the pipeline output in %T", rsp))
return rsp, nil
}
// Pass data here instead of desired
// This is because there already may be desired objects
output.object = data
output.msgCount = len(data)
resources = append(resources, pkgresource.Resource{
Name: r.Name,
Base: *base,
})
}
result, err := pkgresource.ProcessResources(dxr, oxr, desired, observed, in.Spec.Target, resources, &pkgresource.AddResourcesOptions{
Basename: in.Name,
Data: data,
Overwrite: true,
})
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot process xr and state with the pipeline output in %T", rsp))
return rsp, nil
}

// Set dxr and desired state
Expand All @@ -178,27 +129,20 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ
response.Fatal(rsp, errors.Wrapf(err, "cannot set desired composite resource in %T", rsp))
return rsp, nil
}

for _, d := range desired {
log.Debug(fmt.Sprintf("Setting DesiredComposed state to %+v", d.Resource))
}
if err := response.SetDesiredComposedResources(rsp, desired); err != nil {
response.Fatal(rsp, errors.Wrapf(err, "cannot set desired composed resources in %T", rsp))
return rsp, nil
}
log.Debug(fmt.Sprintf("Set %d resource(s) to the desired state", output.msgCount))

// Output success
output.setSuccessMsgs()
for _, msg := range output.msgs {
log.Debug(fmt.Sprintf("Set %d resource(s) to the desired state", result.MsgCount))
for _, msg := range result.Msgs {
rsp.Results = append(rsp.Results, &fnv1beta1.Result{
Severity: fnv1beta1.Severity_SEVERITY_NORMAL,
Message: msg,
})
}

log.Info("Successfully processed crossplane KCL function resources",
"input", in.Name)
log.Info("Successfully processed crossplane KCL function resources", "input", in.Name)

return rsp, nil
}
2 changes: 1 addition & 1 deletion fn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func TestRunFunction(t *testing.T) {
Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"XR"}`),
},
Resources: map[string]*fnv1beta1.Resource{
"0": {
"": {
Resource: resource.MustStructJSON(`{"apiVersion":"example.org/v1","kind":"Generated"}`),
},
},
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/crossplane/function-template-go
module kcl-lang.io/crossplane-kcl

go 1.21

Expand All @@ -7,11 +7,12 @@ require (
github.com/crossplane/crossplane-runtime v1.14.3
github.com/crossplane/function-sdk-go v0.1.0
github.com/google/go-cmp v0.6.0
github.com/google/martian v2.1.0+incompatible
github.com/pkg/errors v0.9.1
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/apimachinery v0.28.4
kcl-lang.io/krm-kcl v0.7.0
kcl-lang.io/krm-kcl v0.7.1-0.20231220065806-3885e2535f64
sigs.k8s.io/controller-tools v0.13.0
sigs.k8s.io/yaml v1.4.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1319,8 +1319,8 @@ kcl-lang.io/kcl-go v0.7.1 h1:qulSVPT7Kpn68ol35WGABnidhitnC5vGhRsmzEKlnuM=
kcl-lang.io/kcl-go v0.7.1/go.mod h1:byAAoC83Lfk88n33pgADSJAwF6iJcxCl5Z2gbCbKyGw=
kcl-lang.io/kpm v0.4.3 h1:a97/l8ZSV1GQYWl2AEFOHfEDxvW406oQYKwNoZaeGuE=
kcl-lang.io/kpm v0.4.3/go.mod h1:Yu8uUz7R1NeiP8vJrLQytGVYo+EewJi4EU+rfhYiiYc=
kcl-lang.io/krm-kcl v0.7.0 h1:aE48xI899ZTu9CaOqqIp/VBwdXHCEhefw7Ih7MN0HHk=
kcl-lang.io/krm-kcl v0.7.0/go.mod h1:V+mb/3OPwfFORL1kFtb99GgiGVr06d5HUVpc5G9cTEg=
kcl-lang.io/krm-kcl v0.7.1-0.20231220065806-3885e2535f64 h1:8QXTU3Tjqg4MDUlIFJ3cA3Afa92CB7xDNUdao30E0h0=
kcl-lang.io/krm-kcl v0.7.1-0.20231220065806-3885e2535f64/go.mod h1:V+mb/3OPwfFORL1kFtb99GgiGVr06d5HUVpc5G9cTEg=
kcl-lang.io/lib v0.7.3 h1:YsKBo5jrICQ3fJobywkB9dFfVIW/ixCAkqmYQ5Z2lqQ=
kcl-lang.io/lib v0.7.3/go.mod h1:ubsalGXxJaa5II/EsHmsI/tL2EluYHIcW+BwzQPt+uY=
oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY=
Expand Down
23 changes: 5 additions & 18 deletions input/v1beta1/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"kcl-lang.io/crossplane-kcl/pkg/resource"
)

// This isn't a custom resource, in the sense that we never install its CRD.
Expand All @@ -33,10 +34,10 @@ func (in KCLInput) Validate() error {

switch in.Spec.Target {
// Allowed targets
case PatchDesired, Resources, XR:
case PatchResources:
case resource.PatchDesired, resource.Resources, resource.XR:
case resource.PatchResources:
if len(in.Spec.Resources) == 0 {
return field.Required(field.NewPath("spec.Resources"), fmt.Sprintf("%s target requires at least one resource", PatchResources))
return field.Required(field.NewPath("spec.Resources"), fmt.Sprintf("%s target requires at least one resource", resource.PatchResources))
}

for i, r := range in.Spec.Resources {
Expand Down Expand Up @@ -66,23 +67,9 @@ type RunSpec struct {
// Target determines what object the export output should be applied to
// +kubebuilder:default:=Resources
// +kubebuilder:validation:Enum:=PatchDesired;PatchResources;Resources;XR
Target Target `json:"target"`
Target resource.Target `json:"target"`
}

type Target string

const (
// PatchDesired targets existing Resources on the Desired XR
PatchDesired Target = "PatchDesired"
// PatchResources targets existing KCLInput.spec.Resources
// These resources are then created similar to the Resources target
PatchResources Target = "PatchResources"
// Resources creates new resources that are added to the DesiredComposed Resources
Resources Target = "Resources"
// XR targets the existing Observed XR itself
XR Target = "XR"
)

type ResourceList []Resource

type Resource struct {
Expand Down
2 changes: 1 addition & 1 deletion package/input/template.fn.crossplane.io_kclinputs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ spec:
- name: v1beta1
schema:
openAPIV3Schema:
description: Input can be used to provide input to this Function.
description: KCLInput can be used to provide input to this Function.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
Expand Down
Loading

0 comments on commit cca0f99

Please sign in to comment.