diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25bc69c..7441eec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,10 @@ on: env: # Common versions - GO_VERSION: '1.21.3' - GOLANGCI_VERSION: 'v1.54.2' - DOCKER_BUILDX_VERSION: 'v0.11.2' + GO_VERSION: '1.22.4' + GOLANGCI_VERSION: 'v1.59.1' + DOCKER_BUILDX_VERSION: 'v0.15.1' + PKL_VERSION: '0.26.0' # These environment variables are important to the Crossplane CLI install.sh # script. They determine what version it installs. @@ -49,7 +50,7 @@ jobs: cache: false # The golangci-lint action does its own caching. - name: Lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v6.0.1 with: version: ${{ env.GOLANGCI_VERSION }} @@ -64,6 +65,11 @@ jobs: with: go-version: ${{ env.GO_VERSION }} + - name: Install pkl + uses: pkl-community/setup-pkl@v0 + with: + pkl-version: ${{ env.PKL_VERSION }} + - name: Run Unit Tests run: go test -v -cover ./... diff --git a/go.mod b/go.mod index d0021cb..f8575fe 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/crossplane-contrib/function-pkl -go 1.22.1 - -toolchain go1.22.3 +go 1.22.4 require ( github.com/alecthomas/kong v0.9.0 diff --git a/input/v1beta1/input.go b/input/v1beta1/input.go index 8e22d61..e4911eb 100644 --- a/input/v1beta1/input.go +++ b/input/v1beta1/input.go @@ -25,7 +25,7 @@ import ( // This isn't a custom resource, in the sense that we never install its CRD. // It is a KRM-like object, so we generate a CRD to describe its schema. -// Input can be used to provide input to this Function. +// Pkl struct can be used to provide input to this Function. // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:resource:categories=crossplane @@ -37,12 +37,13 @@ type Pkl struct { Spec PklSpec `json:"spec,omitempty"` } +// PklSpec specifies references for the function type PklSpec struct { // +kubebuilder:validation:Enum=uri;inline;local Type string `json:"type,omitempty"` // Use URI Scheme to load Project/Package - Uri string `json:"uri,omitempty"` + URI string `json:"uri,omitempty"` // Contains a stringified Pkl file Inline string `json:"inline,omitempty"` diff --git a/internal/extraresources.go b/internal/extraresources.go deleted file mode 100644 index 46dfd57..0000000 --- a/internal/extraresources.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -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 internal - -import ( - fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1" -) - -type ExtraResourceSelectors struct { - ExtraResources map[string]*ExtraResourceSelector `json:"extraResourceSelectors,omitempty"` -} - -func (e *ExtraResourceSelectors) ToResourceSelectors() map[string]*fnv1beta1.ResourceSelector { - out := map[string]*fnv1beta1.ResourceSelector{} - for name, extraResourceSelector := range e.ExtraResources { - out[name] = extraResourceSelector.ToResourceSelector() - } - return out -} - -type ExtraResourceSelector struct { - ApiVersion string `json:"apiVersion"` - Kind string `json:"kind"` - MatchLabels map[string]string `json:"matchLabels,omitempty"` - Name string `json:"name,omitempty"` -} - -func (e *ExtraResourceSelector) ToResourceSelector() *fnv1beta1.ResourceSelector { - out := &fnv1beta1.ResourceSelector{ - ApiVersion: e.ApiVersion, - Kind: e.Kind, - } - if len(e.MatchLabels) == 0 { - out.Match = &fnv1beta1.ResourceSelector_MatchName{ - MatchName: e.Name, - } - return out - } - - out.Match = &fnv1beta1.ResourceSelector_MatchLabels{ - MatchLabels: &fnv1beta1.MatchLabels{Labels: e.MatchLabels}, - } - return out -} diff --git a/internal/function/fn.go b/internal/function/fn.go index 1ad03d7..9a99906 100644 --- a/internal/function/fn.go +++ b/internal/function/fn.go @@ -12,22 +12,25 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package function package handles the gRPC calls package function import ( "context" - "google.golang.org/protobuf/types/known/durationpb" "github.com/apple/pkl-go/pkl" - "github.com/crossplane-contrib/function-pkl/input/v1beta1" - "github.com/crossplane-contrib/function-pkl/internal/helper" - "github.com/crossplane-contrib/function-pkl/internal/pkl/reader" + "google.golang.org/protobuf/types/known/durationpb" + "sigs.k8s.io/yaml" + "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/logging" fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1" "github.com/crossplane/function-sdk-go/request" "github.com/crossplane/function-sdk-go/response" - "sigs.k8s.io/yaml" + + "github.com/crossplane-contrib/function-pkl/input/v1beta1" + "github.com/crossplane-contrib/function-pkl/internal/helper" + "github.com/crossplane-contrib/function-pkl/internal/pkl/reader" ) // Function returns whatever response you ask it to. @@ -90,8 +93,7 @@ func (f *Function) RunFunction(ctx context.Context, req *fnv1beta1.RunFunctionRe return rsp, nil } defer func(evaluator pkl.Evaluator) { - err := evaluator.Close() - if err != nil { + if err := evaluator.Close(); err != nil { f.Log.Info("evaluator could not be closed correctly:", err) } }(evaluator) @@ -137,10 +139,10 @@ func convertExtraResources(extraResources map[string]*helper.ResourceSelector) m out := make(map[string]*fnv1beta1.ResourceSelector) for name, fixedrs := range extraResources { rs := &fnv1beta1.ResourceSelector{ - ApiVersion: fixedrs.ApiVersion, + ApiVersion: fixedrs.APIVersion, Kind: fixedrs.Kind, } - if fixedrs.Match.MatchLabels != nil && len(fixedrs.Match.MatchLabels.Labels) > 0 { + if fixedrs.Match.MatchLabels != nil && len(fixedrs.Match.MatchLabels.GetLabels()) > 0 { rs.Match = &fnv1beta1.ResourceSelector_MatchLabels{ MatchLabels: &fnv1beta1.MatchLabels{ Labels: fixedrs.Match.MatchLabels.GetLabels(), @@ -159,10 +161,10 @@ func convertExtraResources(extraResources map[string]*helper.ResourceSelector) m func getModuleSource(pklSpec v1beta1.PklSpec) (*pkl.ModuleSource, error) { switch pklSpec.Type { case "uri": - if pklSpec.Uri == "" { + if pklSpec.URI == "" { return nil, errors.New("manifest type is uri but uri is empty") } - return pkl.UriSource(pklSpec.Uri), nil + return pkl.UriSource(pklSpec.URI), nil case "inline": if pklSpec.Inline == "" { diff --git a/internal/function/fn_test.go b/internal/function/fn_test.go index 58253b5..7dcb3d3 100644 --- a/internal/function/fn_test.go +++ b/internal/function/fn_test.go @@ -21,7 +21,6 @@ import ( "testing" "github.com/apple/pkl-go/pkl" - "github.com/crossplane-contrib/function-pkl/input/v1beta1" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/protobuf/testing/protocmp" @@ -30,6 +29,8 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/logging" fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1" "github.com/crossplane/function-sdk-go/resource" + + "github.com/crossplane-contrib/function-pkl/input/v1beta1" ) var ( @@ -181,8 +182,7 @@ func TestRunFunction(t *testing.T) { t.Run(name, func(t *testing.T) { evaluatorManager := pkl.NewEvaluatorManager() defer func(evaluatorManager pkl.EvaluatorManager) { - err := evaluatorManager.Close() - if err != nil { + if err := evaluatorManager.Close(); err != nil { t.Error(err) } }(evaluatorManager) diff --git a/internal/helper/composition.go b/internal/helper/composition.go index 2816207..a651d5d 100644 --- a/internal/helper/composition.go +++ b/internal/helper/composition.go @@ -12,34 +12,40 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package helper contains hacky alternative implementations of resources that would otherwise not be able to be converted to yaml package helper import fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1" +// CompositionRequest is a drop-in for RunFunctionRequest to replace ExtraResources type CompositionRequest struct { *fnv1beta1.RunFunctionRequest ExtraResources map[string]*fnv1beta1.Resources `protobuf:"bytes,6,rep,name=extra_resources,json=extraResources,proto3" json:"extraResources,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } +// CompositionResponse is a drop-in for RunFunctionResponse to replace Requirements type CompositionResponse struct { fnv1beta1.RunFunctionResponse Requirements *Requirements `json:"requirements,omitempty"` } +// Requirements is a drop-in for the version provided by fnv1beta1 type Requirements struct { - //fnv1beta1.Requirements + // fnv1beta1.Requirements ExtraResources map[string]*ResourceSelector `protobuf:"bytes,1,rep,name=extra_resources,json=extraResources,proto3" json:"extraResources,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } +// ResourceSelector allows to select Kubernetes Resources type ResourceSelector struct { - ApiVersion string `protobuf:"bytes,1,opt,name=api_version,json=apiVersion,proto3" json:"apiVersion,omitempty"` + APIVersion string `protobuf:"bytes,1,opt,name=api_version,json=apiVersion,proto3" json:"apiVersion,omitempty"` Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` Match Match `json:"match,omitempty"` } +// Match specifies the method used for the Lookup type Match struct { MatchName string `protobuf:"bytes,3,opt,name=match_name,json=matchName,proto3,oneof" json:"matchName,omitempty"` MatchLabels *fnv1beta1.MatchLabels `protobuf:"bytes,4,opt,name=match_labels,json=matchLabels,proto3,oneof" json:"matchLabels,omitempty"` diff --git a/internal/pkl/reader/crossplane_reader.go b/internal/pkl/reader/crossplane_reader.go index 4900315..f83b973 100644 --- a/internal/pkl/reader/crossplane_reader.go +++ b/internal/pkl/reader/crossplane_reader.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package reader Contains Pkl Custom Readers package reader import ( @@ -20,11 +21,14 @@ import ( "net/url" "github.com/apple/pkl-go/pkl" - "github.com/crossplane-contrib/function-pkl/internal/helper" - "github.com/crossplane/function-sdk-go/logging" "sigs.k8s.io/yaml" + + "github.com/crossplane/function-sdk-go/logging" + + "github.com/crossplane-contrib/function-pkl/internal/helper" ) +// CrossplaneReader implements the "crossplane" scheme type CrossplaneReader struct { Request *helper.CompositionRequest ReaderScheme string @@ -32,14 +36,17 @@ type CrossplaneReader struct { Ctx context.Context } +// Scheme implements the https://pkg.go.dev/github.com/apple/pkl-go/pkl#Reader interface func (f *CrossplaneReader) Scheme() string { return f.ReaderScheme } +// IsGlobbable implements https://pkg.go.dev/github.com/apple/pkl-go/pkl#Reader func (f *CrossplaneReader) IsGlobbable() bool { return false } +// HasHierarchicalUris implements https://pkg.go.dev/github.com/apple/pkl-go/pkl#Reader func (f *CrossplaneReader) HasHierarchicalUris() bool { return false } @@ -49,7 +56,7 @@ func (f *CrossplaneReader) HasHierarchicalUris() bool { // available values. // // This method is only called if it is hierarchical and local, or if it is globbable. -func (f *CrossplaneReader) ListElements(url url.URL) ([]pkl.PathElement, error) { +func (f *CrossplaneReader) ListElements(_ url.URL) ([]pkl.PathElement, error) { out := []pkl.PathElement{ pkl.NewPathElement("request", false), } @@ -62,17 +69,12 @@ type crossplaneModuleReader struct { *CrossplaneReader } +// IsLocal implements https://pkg.go.dev/github.com/apple/pkl-go/pkl#ModuleReader func (f crossplaneModuleReader) IsLocal() bool { return true } -var evaluatorManager pkl.EvaluatorManager = pkl.NewEvaluatorManager() - -// TODO find better solution -func Close() error { - return evaluatorManager.Close() -} - +// WithCrossplane activates the CrossplaneReader for the Evaluator var WithCrossplane = func(crossplaneReader *CrossplaneReader) func(opts *pkl.EvaluatorOptions) { return func(opts *pkl.EvaluatorOptions) { reader := crossplaneReader @@ -81,6 +83,7 @@ var WithCrossplane = func(crossplaneReader *CrossplaneReader) func(opts *pkl.Eva } } +// BaseRead contains the business logic of what happens when the scheme gets a request func (f *CrossplaneReader) BaseRead(url url.URL) ([]byte, error) { switch url.Opaque { case "request": @@ -96,7 +99,7 @@ func (f *CrossplaneReader) BaseRead(url url.URL) ([]byte, error) { } } -// Expects an URL like /observed/composition/resource and evaluates the RunFunctionRequest for the state of the desired field and returns it as a pkl file +// Read Expects a URL like /observed/composition/resource and evaluates the RunFunctionRequest for the state of the desired field and returns it as a pkl file func (f crossplaneModuleReader) Read(url url.URL) (string, error) { out, err := f.BaseRead(url) if err != nil { @@ -111,6 +114,7 @@ type crossplaneResourceReader struct { *CrossplaneReader } +// Read implements https://pkg.go.dev/github.com/apple/pkl-go/pkl#ResourceReader func (f crossplaneResourceReader) Read(url url.URL) ([]byte, error) { out, err := f.BaseRead(url) if err != nil { diff --git a/main.go b/main.go index d092e81..7331bb7 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,6 @@ import ( "github.com/crossplane/function-sdk-go" fn "github.com/crossplane-contrib/function-pkl/internal/function" - "github.com/crossplane-contrib/function-pkl/internal/pkl/reader" ) // CLI of this Function. @@ -43,8 +42,11 @@ func (c *CLI) Run() error { } evaluatorManager := pkl.NewEvaluatorManager() - defer evaluatorManager.Close() - defer reader.Close() + defer func(evaluatorManager pkl.EvaluatorManager) { + if err := evaluatorManager.Close(); err != nil { + log.Info("error closing evaluatorManager", err) + } + }(evaluatorManager) return function.Serve(&fn.Function{Log: log, EvaluatorManager: evaluatorManager}, function.Listen(c.Network, c.Address),