-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from negz/bootstrap
Add the beginnings of a Function SDK
- Loading branch information
Showing
7 changed files
with
597 additions
and
8 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
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
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,80 @@ | ||
/* | ||
Copyright 2023 The Crossplane Authors. | ||
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 request contains utilities for working with RunFunctionRequests. | ||
package request | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime" | ||
|
||
"github.com/crossplane/crossplane-runtime/pkg/errors" | ||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composed" | ||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite" | ||
|
||
"github.com/crossplane/function-sdk-go/proto/v1beta1" | ||
"github.com/crossplane/function-sdk-go/resource" | ||
) | ||
|
||
// GetInput from the supplied request. Input is loaded into the supplied object. | ||
func GetInput(req *v1beta1.RunFunctionRequest, into runtime.Object) error { | ||
return errors.Wrap(resource.AsObject(req.GetInput(), into), "cannot get Function input %T from %T, into, req") | ||
} | ||
|
||
// GetObservedCompositeResource from the supplied request. | ||
func GetObservedCompositeResource(req *v1beta1.RunFunctionRequest) (*resource.Composite, error) { | ||
xr := &resource.Composite{ | ||
Resource: composite.New(), | ||
ConnectionDetails: req.GetObserved().GetComposite().GetConnectionDetails(), | ||
} | ||
err := resource.AsObject(req.GetObserved().GetComposite().GetResource(), xr.Resource) | ||
return xr, err | ||
} | ||
|
||
// GetObservedComposedResources from the supplied request. | ||
func GetObservedComposedResources(req *v1beta1.RunFunctionRequest) (resource.ObservedComposedResources, error) { | ||
ocds := resource.ObservedComposedResources{} | ||
for name, r := range req.GetObserved().GetResources() { | ||
ocd := resource.ObservedComposed{Resource: composed.New(), ConnectionDetails: r.GetConnectionDetails()} | ||
if err := resource.AsObject(r.GetResource(), ocd.Resource); err != nil { | ||
return nil, err | ||
} | ||
ocds[resource.Name(name)] = ocd | ||
} | ||
return ocds, nil | ||
} | ||
|
||
// GetDesiredCompositeResource from the supplied request. | ||
func GetDesiredCompositeResource(req *v1beta1.RunFunctionRequest) (*resource.Composite, error) { | ||
xr := &resource.Composite{ | ||
Resource: composite.New(), | ||
ConnectionDetails: req.GetDesired().GetComposite().GetConnectionDetails(), | ||
} | ||
err := resource.AsObject(req.GetDesired().GetComposite().GetResource(), xr.Resource) | ||
return xr, err | ||
} | ||
|
||
// GetDesiredComposedResources from the supplied request. | ||
func GetDesiredComposedResources(req *v1beta1.RunFunctionRequest) (resource.DesiredComposedResources, error) { | ||
ocds := resource.DesiredComposedResources{} | ||
for name, r := range req.GetDesired().GetResources() { | ||
ocd := resource.DesiredComposed{Resource: composed.New()} | ||
if err := resource.AsObject(r.GetResource(), ocd.Resource); err != nil { | ||
return nil, err | ||
} | ||
ocds[resource.Name(name)] = ocd | ||
} | ||
return ocds, nil | ||
} |
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,144 @@ | ||
/* | ||
Copyright 2021 The Crossplane Authors. | ||
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 resource contains utilities to convert protobuf representations of | ||
// Crossplane resources to unstructured Go types, often with convenient getters | ||
// and setters. | ||
package resource | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
"google.golang.org/protobuf/encoding/protojson" | ||
"google.golang.org/protobuf/types/known/structpb" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
|
||
"github.com/crossplane/crossplane-runtime/pkg/errors" | ||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composed" | ||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite" | ||
) | ||
|
||
// ConnectionDetails created or updated during an operation on an external | ||
// resource, for example usernames, passwords, endpoints, ports, etc. | ||
type ConnectionDetails map[string][]byte | ||
|
||
// A Composite resource - aka an XR. | ||
type Composite struct { | ||
Resource *composite.Unstructured | ||
ConnectionDetails ConnectionDetails | ||
} | ||
|
||
// A Name uniquely identifies a composed resource within a Composition Function | ||
// pipeline. It's not the resource's metadata.name. | ||
type Name string | ||
|
||
// DesiredComposed reflects the desired state of a composed resource. | ||
type DesiredComposed struct { | ||
Resource *composed.Unstructured | ||
|
||
Ready Ready | ||
} | ||
|
||
// Ready indicates whether a composed resource should be considered ready. | ||
type Ready int | ||
|
||
// Composed resource readiness. | ||
const ( | ||
ReadyUnspecified Ready = iota | ||
ReadyTrue | ||
ReadyFalse | ||
) | ||
|
||
// NewDesiredComposedResource returns a new, empty desired composed resource. | ||
func NewDesiredComposedResource() DesiredComposed { | ||
return DesiredComposed{Resource: composed.New()} | ||
} | ||
|
||
// DesiredComposedResources indexed by resource name. | ||
type DesiredComposedResources map[Name]DesiredComposed | ||
|
||
// ObservedComposed reflects the observed state of a composed resource. | ||
type ObservedComposed struct { | ||
Resource *composed.Unstructured | ||
ConnectionDetails ConnectionDetails | ||
} | ||
|
||
// ObservedComposedResources indexed by resource name. | ||
type ObservedComposedResources map[Name]ObservedComposed | ||
|
||
// AsObject gets the supplied Kubernetes object from the supplied struct. | ||
func AsObject(s *structpb.Struct, o runtime.Object) error { | ||
// We try to avoid a JSON round-trip if o is backed by unstructured data. | ||
switch u := o.(type) { | ||
case *unstructured.Unstructured: | ||
u.Object = s.AsMap() | ||
return nil | ||
case wrapped: | ||
u.GetUnstructured().Object = s.AsMap() | ||
return nil | ||
default: | ||
b, err := protojson.Marshal(s) | ||
if err != nil { | ||
return errors.Wrapf(err, "cannot marshal %T to JSON", s) | ||
} | ||
return errors.Wrapf(json.Unmarshal(b, o), "cannot unmarshal JSON from %T into %T", s, o) | ||
} | ||
} | ||
|
||
// AsStruct gets the supplied struct from the supplied Kubernetes object. | ||
func AsStruct(o runtime.Object) (*structpb.Struct, error) { | ||
// We try to avoid a JSON round-trip if o is backed by unstructured data. | ||
switch u := o.(type) { | ||
case *unstructured.Unstructured: | ||
s, err := structpb.NewStruct(u.Object) | ||
return s, errors.Wrapf(err, "cannot create new Struct from %T", u) | ||
case wrapped: | ||
s, err := structpb.NewStruct(u.GetUnstructured().Object) | ||
return s, errors.Wrapf(err, "cannot create new Struct from %T", u) | ||
default: | ||
b, err := json.Marshal(o) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "cannot marshal %T to JSON", o) | ||
} | ||
s := &structpb.Struct{} | ||
return s, errors.Wrapf(protojson.Unmarshal(b, s), "cannot unmarshal JSON from %T into %T", o, s) | ||
} | ||
} | ||
|
||
type wrapped interface { | ||
GetUnstructured() *unstructured.Unstructured | ||
} | ||
|
||
// MustStructObject is intended only for use in tests. It returns the supplied | ||
// object as a struct. It panics if it can't. | ||
func MustStructObject(o runtime.Object) *structpb.Struct { | ||
s, err := AsStruct(o) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return s | ||
} | ||
|
||
// MustStructJSON is intended only for use in tests. It returns the supplied | ||
// JSON string as a struct. It panics if it can't. | ||
func MustStructJSON(j string) *structpb.Struct { | ||
s := &structpb.Struct{} | ||
if err := protojson.Unmarshal([]byte(j), s); err != nil { | ||
panic(err) | ||
} | ||
return s | ||
} |
Oops, something went wrong.