Skip to content

Commit 9cf3fda

Browse files
committed
Remove single use XRD and Composition, Crossplane does not like them in function packages
Signed-off-by: Patrick J. McNerthney <pat@mcnerthney.com>
1 parent 3b0079a commit 9cf3fda

20 files changed

Lines changed: 215 additions & 289 deletions

.github/workflows/ci.yaml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ on:
1515
env:
1616
# Common versions
1717
PYTHON_VERSION: '3.14.0' # TODO: Used?
18-
HATCH_VERSION: '1.14.2'
18+
HATCH_VERSION: 'v1.29.0'
1919
DOCKER_BUILDX_VERSION: 'v0.26.1'
2020

2121
# These environment variables are important to the Crossplane CLI install.sh
2222
# script. They determine what version it installs.
2323
XP_CHANNEL: stable
2424
# v2.2.0 does not allow functions to include anything but CRDs.
25-
XP_VERSION: v2.1.0
25+
XP_VERSION: v2.2.0
2626

2727
# This CI job will automatically push new builds to xpkg.upbound.io if the
2828
# XPKG_ACCESS_ID and XPKG_TOKEN secrets are set in the GitHub respository (or
@@ -52,8 +52,8 @@ jobs:
5252
# with:
5353
# python-version: ${{ env.PYTHON_VERSION }}
5454

55-
# - name: Setup Hatch
56-
# run: pipx install hatch==${{ env.HATCH_VERSION }}
55+
# - name: Install Hatch
56+
# uses: pypa/hatch@$hatch-{{ env.HATCH_VERSION }}
5757

5858
# - name: Lint
5959
# run: hatch run lint:check
@@ -69,8 +69,8 @@ jobs:
6969
with:
7070
python-version: ${{ env.PYTHON_VERSION }}
7171

72-
- name: Setup Hatch
73-
run: pipx install hatch==${{ env.HATCH_VERSION }}
72+
- name: Install Hatch
73+
uses: pypa/hatch@hatch-${{ env.HATCH_VERSION }}
7474

7575
- name: Run Unit Tests
7676
run: hatch run test:ci
@@ -102,8 +102,8 @@ jobs:
102102
with:
103103
python-version: ${{ env.PYTHON_VERSION }}
104104

105-
- name: Setup Hatch
106-
run: pipx install hatch==${{ env.HATCH_VERSION }}
105+
- name: Install Hatch
106+
uses: pypa/hatch@hatch-${{ env.HATCH_VERSION }}
107107

108108
# If a version wasn't explicitly passed as a workflow_dispatch input we
109109
# default to version v0.0.0+<git-commit-date>-<git-short-sha>, for example
@@ -205,8 +205,8 @@ jobs:
205205
name: dist
206206
path: dist
207207

208-
- name: Setup Hatch
209-
run: pipx install hatch==${{ env.HATCH_VERSION }}
208+
- name: Install Hatch
209+
uses: pypa/hatch@hatch-${{ env.HATCH_VERSION }}
210210

211211
- name: Publish to PyPI
212212
env:

README.md

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -385,25 +385,78 @@ optionally to the Claim.
385385
| Result.message | String | Human-readable details about the result |
386386
| Result.claim | Boolean | Also apply the result to the claim |
387387

388-
## Single use Composites
388+
## Inlined Composites
389389

390390
Tired of creating a CompositeResourceDefinition, a Composition, and a Composite
391-
just to run that Composition once in a single use or initialize task?
391+
just to run that Composition once in a setup or initialize task?
392392

393-
function-pythonic installs a `Composite` CompositeResourceDefinition that enables
394-
creating such tasks using a single Composite resource:
393+
function-pythonic supports "inlined" Compositions, where the python module
394+
from a field in the Composite's spec.
395395
```yaml
396-
apiVersion: pythonic.fn.crossplane.io/v1alpha1
397-
kind: Composite
396+
apiVersion: inlined.example.org/v1alpha1
397+
kind: Step
398398
metadata:
399-
name: composite-example
399+
name: inlined-example
400400
spec:
401401
composite: |
402402
class HelloComposite(BaseComposite):
403403
def compose(self):
404-
self.status.composite = 'Hello, World!'
404+
self.status.step = 'Hello, World!'
405+
```
406+
The CompositeResourceDefinition and Composition to support the above example:
407+
```yaml
408+
apiVersion: apiextensions.crossplane.io/v1
409+
kind: CompositeResourceDefinition
410+
metadata:
411+
name: inlined.example.org/v1alpha1
412+
spec:
413+
group: inlined.example.org
414+
names:
415+
kind: Step
416+
plural: steps
417+
defaultCompositionRef:
418+
name: steps.inlined.example.org
419+
versions:
420+
- name: v1alpha1
421+
served: true
422+
referenceable: true
423+
schema:
424+
openAPIV3Schema:
425+
type: object
426+
properties:
427+
spec:
428+
type: object
429+
properties:
430+
composite:
431+
type: string
432+
description: 'A Python module that defines a class with the signature: class Composite(BaseComposite)'
433+
required:
434+
- composite
435+
status:
436+
type: object
437+
properties:
438+
composite:
439+
x-kubernetes-preserve-unknown-fields: true
440+
```
441+
```yaml
442+
apiVersion: apiextensions.crossplane.io/v1
443+
kind: Composition
444+
metadata:
445+
name: steps.inlined.example.org
446+
spec:
447+
compositeTypeRef:
448+
apiVersion: inlined.example.org/v1alpha1
449+
kind: Step
450+
mode: Pipeline
451+
pipeline:
452+
- step: inlined
453+
functionRef:
454+
name: function-pythonic
455+
input:
456+
apiVersion: pythonic.fn.crossplane.io/v1alpha1
457+
kind: Composite
458+
inlined: composite
405459
```
406-
407460
## Quick Start Development
408461

409462
function-pythonic includes a pure python implementation of the `crossplane render ...`

crossplane/pythonic/composite.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __set__(self, composite, ready):
8989

9090

9191
class BaseComposite:
92-
def __init__(self, crossplane_v1, request, single_use, logger):
92+
def __init__(self, crossplane_v1, request, logger):
9393
self.crossplane_v1 = crossplane_v1
9494
self.request = protobuf.Message(None, 'request', request.DESCRIPTOR, request, 'Function Request')
9595
response = fnv1.RunFunctionResponse(
@@ -104,10 +104,7 @@ def __init__(self, crossplane_v1, request, single_use, logger):
104104
)
105105
self.response = protobuf.Message(None, 'response', response.DESCRIPTOR, response)
106106
self.logger = logger
107-
if single_use:
108-
self.parameters = self.request.observed.composite.resource.spec.parameters
109-
else:
110-
self.parameters = self.request.input.parameters
107+
self.parameters = self.request.input.parameters
111108
self.credentials = Credentials(self.request)
112109
self.context = self.response.context
113110
self.environment = self.context['apiextensions.crossplane.io/environment']

crossplane/pythonic/function.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,13 @@ async def run_function(self, request):
4848
name.append(composite['metadata']['name'])
4949
logger = logging.getLogger('.'.join(name))
5050

51-
if composite['apiVersion'] in ('pythonic.crossplane.io/v1alpha1', 'pythonic.fortra.com/v1alpha1') and composite['kind'] == 'Composite':
52-
if 'spec' not in composite or 'composite' not in composite['spec']:
53-
return self.fatal(request, logger, 'Missing spec "composite"')
54-
single_use = True
55-
composite = composite['spec']['composite']
51+
if 'inlined' in request.input and request.input['inlined']:
52+
if 'spec' not in composite or request.input['inlined'] not in composite['spec']:
53+
return self.fatal(request, logger, f"Missing inlined spec.{request.input['inlined']}")
54+
composite = composite['spec'][request.input['inlined']]
5655
else:
5756
if 'composite' not in request.input:
5857
return self.fatal(request, logger, 'Missing input "composite"')
59-
single_use = False
6058
composite = request.input['composite']
6159

6260
# Ideally this is something the Function API provides
@@ -100,7 +98,7 @@ async def run_function(self, request):
10098
self.clazzes[composite] = clazz
10199

102100
try:
103-
composite = clazz(self.crossplane_v1, request, single_use, logger)
101+
composite = clazz(self.crossplane_v1, request, logger)
104102
except Exception as e:
105103
return self.fatal(request, logger, 'Instantiate', e)
106104

crossplane/pythonic/protobuf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,8 +796,10 @@ def __len__(self):
796796
def __contains__(self, item):
797797
match self._kind:
798798
case 'struct_value':
799+
item = self._validate_key(item)
799800
return item in self._value.struct_value.fields or item in self._unknowns
800801
case 'Struct':
802+
item = self._validate_key(item)
801803
return item in self._value.fields or item in self._unknowns
802804
case 'list_value' | 'ListValue':
803805
for value in self:

0 commit comments

Comments
 (0)