Skip to content

Commit 78164e8

Browse files
authored
Merge pull request #14 from upbound/feature/go-functions
feat(languages): add different composition for languages + go
2 parents 5ec12a3 + 3c8591a commit 78164e8

File tree

13 files changed

+1320
-2
lines changed

13 files changed

+1320
-2
lines changed

apis/go/composition.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: apiextensions.crossplane.io/v1
2+
kind: Composition
3+
metadata:
4+
name: xstoragebuckets.go.platform.example.com
5+
labels:
6+
language: go
7+
spec:
8+
compositeTypeRef:
9+
apiVersion: platform.example.com/v1alpha1
10+
kind: XStorageBucket
11+
mode: Pipeline
12+
pipeline:
13+
- functionRef:
14+
name: upbound-example-project-awscompose-bucket-go
15+
step: compose-bucket-kcl
16+
- functionRef:
17+
name: crossplane-contrib-function-auto-ready
18+
step: crossplane-contrib-function-auto-ready

apis/xstoragebuckets/composition.yaml renamed to apis/kcl/composition.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
apiVersion: apiextensions.crossplane.io/v1
22
kind: Composition
33
metadata:
4-
name: xstoragebuckets.platform.example.com
4+
name: xstoragebuckets.kcl.platform.example.com
5+
labels:
6+
language: kcl
57
spec:
68
compositeTypeRef:
79
apiVersion: platform.example.com/v1alpha1

apis/python/composition.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: apiextensions.crossplane.io/v1
2+
kind: Composition
3+
metadata:
4+
name: xstoragebuckets.python.platform.example.com
5+
labels:
6+
language: python
7+
spec:
8+
compositeTypeRef:
9+
apiVersion: platform.example.com/v1alpha1
10+
kind: XStorageBucket
11+
mode: Pipeline
12+
pipeline:
13+
- functionRef:
14+
name: upbound-example-project-awscompose-bucket-python
15+
step: compose-bucket-kcl
16+
- functionRef:
17+
name: crossplane-contrib-function-auto-ready
18+
step: crossplane-contrib-function-auto-ready

examples/go/example.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: platform.example.com/v1alpha1
2+
kind: XStorageBucket
3+
metadata:
4+
name: example-go
5+
spec:
6+
compositionSelector:
7+
matchLabels:
8+
language: go
9+
parameters:
10+
region: us-west-1
11+
versioning: true
12+
acl: public-read

examples/kcl/example.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: platform.example.com/v1alpha1
2+
kind: XStorageBucket
3+
metadata:
4+
name: example-kcl
5+
spec:
6+
compositionSelector:
7+
matchLabels:
8+
language: kcl
9+
parameters:
10+
region: us-west-1
11+
versioning: true
12+
acl: public-read

examples/providerconfig.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: aws.upbound.io/v1beta1
2+
kind: ProviderConfig
3+
metadata:
4+
name: default
5+
spec:
6+
credentials:
7+
source: Secret
8+
secretRef:
9+
namespace: crossplane-system
10+
name: aws-secret
11+
key: my-aws-secret

examples/python/example.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: platform.example.com/v1alpha1
2+
kind: XStorageBucket
3+
metadata:
4+
name: example-python
5+
spec:
6+
compositionSelector:
7+
matchLabels:
8+
language: python
9+
parameters:
10+
region: us-west-1
11+
versioning: true
12+
acl: public-read

functions/compose-bucket-go/fn.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"dev.upbound.io/models/com/example/platform/v1alpha1"
8+
"dev.upbound.io/models/io/upbound/aws/s3/v1beta1"
9+
"k8s.io/utils/ptr"
10+
11+
"github.com/crossplane/crossplane-runtime/pkg/logging"
12+
"github.com/crossplane/function-sdk-go/errors"
13+
fnv1 "github.com/crossplane/function-sdk-go/proto/v1"
14+
"github.com/crossplane/function-sdk-go/request"
15+
"github.com/crossplane/function-sdk-go/resource"
16+
"github.com/crossplane/function-sdk-go/resource/composed"
17+
"github.com/crossplane/function-sdk-go/response"
18+
)
19+
20+
// Function is your composition function.
21+
type Function struct {
22+
fnv1.UnimplementedFunctionRunnerServiceServer
23+
24+
log logging.Logger
25+
}
26+
27+
// RunFunction runs the Function.
28+
func (f *Function) RunFunction(_ context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) {
29+
f.log.Info("Running function", "tag", req.GetMeta().GetTag())
30+
rsp := response.To(req, response.DefaultTTL)
31+
32+
observedComposite, err := request.GetObservedCompositeResource(req)
33+
if err != nil {
34+
response.Fatal(rsp, errors.Wrap(err, "cannot get xr"))
35+
return rsp, nil
36+
}
37+
38+
observedComposed, err := request.GetObservedComposedResources(req)
39+
if err != nil {
40+
response.Fatal(rsp, errors.Wrap(err, "cannot get observed resources"))
41+
return rsp, nil
42+
}
43+
44+
var xr v1alpha1.XStorageBucket
45+
if err := convertViaJSON(&xr, observedComposite.Resource); err != nil {
46+
response.Fatal(rsp, errors.Wrap(err, "cannot convert xr"))
47+
return rsp, nil
48+
}
49+
50+
params := xr.Spec.Parameters
51+
if params.Region == nil || *params.Region == "" {
52+
response.Fatal(rsp, errors.Wrap(err, "missing region"))
53+
return rsp, nil
54+
}
55+
56+
// We'll collect our desired composed resources into this map, then convert
57+
// them to the SDK's types and set them in the response when we return.
58+
desiredComposed := make(map[resource.Name]any)
59+
defer func() {
60+
desiredComposedResources, err := request.GetDesiredComposedResources(req)
61+
if err != nil {
62+
response.Fatal(rsp, errors.Wrap(err, "cannot get desired resources"))
63+
return
64+
}
65+
66+
for name, obj := range desiredComposed {
67+
c := composed.New()
68+
if err := convertViaJSON(c, obj); err != nil {
69+
response.Fatal(rsp, errors.Wrapf(err, "cannot convert %s to unstructured", name))
70+
return
71+
}
72+
desiredComposedResources[name] = &resource.DesiredComposed{Resource: c}
73+
}
74+
75+
if err := response.SetDesiredComposedResources(rsp, desiredComposedResources); err != nil {
76+
response.Fatal(rsp, errors.Wrap(err, "cannot set desired resources"))
77+
return
78+
}
79+
}()
80+
81+
bucket := &v1beta1.Bucket{
82+
APIVersion: ptr.To("s3.aws.upbound.io/v1beta1"),
83+
Kind: ptr.To("Bucket"),
84+
Spec: &v1beta1.BucketSpec{
85+
ForProvider: &v1beta1.BucketSpecForProvider{
86+
Region: params.Region,
87+
},
88+
},
89+
}
90+
desiredComposed["bucket"] = bucket
91+
92+
// Return early if Crossplane hasn't observed the bucket yet. This means it
93+
// hasn't been created yet. This function will be called again after it is.
94+
observedBucket, ok := observedComposed["bucket"]
95+
if !ok {
96+
response.Normal(rsp, "waiting for bucket to be created").TargetCompositeAndClaim()
97+
return rsp, nil
98+
}
99+
100+
// The desired ACL, encryption, and versioning resources all need to refer
101+
// to the bucket by its external name, which is stored in its external name
102+
// annotation. Return early if the Bucket's external-name annotation isn't
103+
// set yet.
104+
bucketExternalName := observedBucket.Resource.GetAnnotations()["crossplane.io/external-name"]
105+
if bucketExternalName == "" {
106+
response.Normal(rsp, "waiting for bucket to be created").TargetCompositeAndClaim()
107+
return rsp, nil
108+
}
109+
110+
acl := &v1beta1.BucketACL{
111+
APIVersion: ptr.To("s3.aws.upbound.io/v1beta1"),
112+
Kind: ptr.To("BucketACL"),
113+
Spec: &v1beta1.BucketACLSpec{
114+
ForProvider: &v1beta1.BucketACLSpecForProvider{
115+
Bucket: &bucketExternalName,
116+
Region: params.Region,
117+
ACL: params.ACL,
118+
},
119+
},
120+
}
121+
desiredComposed["acl"] = acl
122+
123+
boc := &v1beta1.BucketOwnershipControls{
124+
APIVersion: ptr.To("s3.aws.upbound.io/v1beta1"),
125+
Kind: ptr.To("BucketOwnershipControls"),
126+
Spec: &v1beta1.BucketOwnershipControlsSpec{
127+
ForProvider: &v1beta1.BucketOwnershipControlsSpecForProvider{
128+
Bucket: &bucketExternalName,
129+
Region: params.Region,
130+
Rule: &[]v1beta1.BucketOwnershipControlsSpecForProviderRule{{
131+
ObjectOwnership: ptr.To("BucketOwnerPreferred"),
132+
}},
133+
},
134+
},
135+
}
136+
desiredComposed["boc"] = boc
137+
138+
pab := &v1beta1.BucketPublicAccessBlock{
139+
APIVersion: ptr.To("s3.aws.upbound.io/v1beta1"),
140+
Kind: ptr.To("BucketPublicAccessBlock"),
141+
Spec: &v1beta1.BucketPublicAccessBlockSpec{
142+
ForProvider: &v1beta1.BucketPublicAccessBlockSpecForProvider{
143+
Bucket: &bucketExternalName,
144+
Region: params.Region,
145+
BlockPublicAcls: ptr.To(false),
146+
RestrictPublicBuckets: ptr.To(false),
147+
IgnorePublicAcls: ptr.To(false),
148+
BlockPublicPolicy: ptr.To(false),
149+
},
150+
},
151+
}
152+
desiredComposed["pab"] = pab
153+
154+
sse := &v1beta1.BucketServerSideEncryptionConfiguration{
155+
APIVersion: ptr.To("s3.aws.upbound.io/v1beta1"),
156+
Kind: ptr.To("BucketServerSideEncryptionConfiguration"),
157+
Spec: &v1beta1.BucketServerSideEncryptionConfigurationSpec{
158+
ForProvider: &v1beta1.BucketServerSideEncryptionConfigurationSpecForProvider{
159+
Bucket: &bucketExternalName,
160+
Region: params.Region,
161+
Rule: &[]v1beta1.BucketServerSideEncryptionConfigurationSpecForProviderRule{{
162+
ApplyServerSideEncryptionByDefault: &[]v1beta1.BucketServerSideEncryptionConfigurationSpecForProviderRuleApplyServerSideEncryptionByDefault{{
163+
SseAlgorithm: ptr.To("AES256"),
164+
}},
165+
BucketKeyEnabled: ptr.To(true),
166+
}},
167+
},
168+
},
169+
}
170+
desiredComposed["sse"] = sse
171+
172+
if params.Versioning != nil && *params.Versioning {
173+
versioning := &v1beta1.BucketVersioning{
174+
APIVersion: ptr.To("s3.aws.upbound.io/v1beta1"),
175+
Kind: ptr.To("BucketVersioning"),
176+
Spec: &v1beta1.BucketVersioningSpec{
177+
ForProvider: &v1beta1.BucketVersioningSpecForProvider{
178+
Bucket: &bucketExternalName,
179+
Region: params.Region,
180+
VersioningConfiguration: &[]v1beta1.BucketVersioningSpecForProviderVersioningConfiguration{{
181+
Status: ptr.To("Enabled"),
182+
}},
183+
},
184+
},
185+
}
186+
desiredComposed["versioning"] = versioning
187+
}
188+
189+
return rsp, nil
190+
}
191+
192+
func convertViaJSON(to, from any) error {
193+
bs, err := json.Marshal(from)
194+
if err != nil {
195+
return err
196+
}
197+
return json.Unmarshal(bs, to)
198+
}

0 commit comments

Comments
 (0)