Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,27 @@ jobs:
# python-version: ${{ env.PYTHON_VERSION }}

# - name: Setup Hatch
# run: pipx install hatch==1.7.0
# run: pipx install hatch==1.14.1

# - name: Lint
# run: hatch run lint:check

# unit-test:
# runs-on: ubuntu-24.04
# steps:
# - name: Checkout
# uses: actions/checkout@v4
unit-test:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4

# - name: Setup Python
# uses: actions/setup-python@v5
# with:
# python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

# - name: Setup Hatch
# run: pipx install hatch==1.7.0
- name: Setup Hatch
run: pipx install hatch==1.14.1

# - name: Run Unit Tests
# run: hatch run test:unit
- name: Run Unit Tests
run: hatch run test:unit

# We want to build most packages for the amd64 and arm64 architectures. To
# speed this up we build single-platform packages in parallel. We then upload
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ The following functions are provided to create Protobuf structures:
| List | Create a new Protobuf list |
| Yaml | Create a new Protobuf structure from a yaml string |
| Json | Create a new Protobuf structure from a json string |
| Base64Encode | Encode a string into base 64 |
| Base64Decode | Decode a string from base 64 |

The following items are supported in all the Protobuf Message wrapper classes: `bool`,
`len`, `contains`, `iter`, `hash`, `==`, `str`, `format`
Expand Down
2 changes: 1 addition & 1 deletion examples/function-go-templating/inline/composition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ spec:
user = f"test-user-{ix}"
r = self.resources[user]('iam.aws.upbound.io/v1beta1', 'User')
r.metadata.labels['testing.upbound.io/example-name'] = user
r.metadata.labels.dummy = r.observed.resource.metadata.labels.dummy or random.choice(['foo', 'bar', 'baz'])
r.metadata.labels.dummy = r.observed.metadata.labels.dummy or random.choice(['foo', 'bar', 'baz'])
r = self.resources[f"sample-access-key-{ix}"]('iam.aws.upbound.io/v1beta1', 'AccessKey')
r.spec.forProvider.userSelector.matchLabels['testing.upbound.io/example-name'] = user
r.spec.writeConnectionSecretToRef.namespace = 'crossplane.system'
Expand Down
51 changes: 38 additions & 13 deletions examples/helm-copy-secret/composition.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: argocds.pythonic.fortra.com
name: xclusters.example.joebowbeer.com
spec:
compositeTypeRef:
apiVersion: pythonic.fortra.com/v1alpha1
kind: ArgoCD
apiVersion: example.joebowbeer.com/v1alpha1
kind: XCluster
mode: Pipeline
pipeline:
- step: pythonic
Expand All @@ -15,16 +15,41 @@ spec:
apiVersion: pythonic.fn.fortra.com/v1alpha1
kind: Composite
composite: |
def argcd_secret_config(secret):
config = Map()
config.tlsClientConfig.insecure = True
config.tlsClientConfig.caData = B64Decode(secret.data['certificate-authority'])
config.tlsClientConfig.certData = B64Decode(secret.data['client-certificate'])
config.tlsClientConfig.keyData = B64Decode(secret.data['client-key'])
return config

class Composite(BaseComposite):
def compose(self):
argocd = self.resources.Release('helm.crossplane.io/v1beta1', 'Release')
argocd.externalName('argocd')
argocd.spec.forProvider.namespace = 'argocd'
argocd.spec.forProvider.chart.repository = 'https://argoproj.github.io/argo-helm'
argocd.spec.forProvider.chart.name = 'argo-cd'
argocd.spec.forProvider.chart.version = '8.0.7'
name = self.metadata.name
namespace = name

release = self.resources.release('helm.crossplane.io/v1beta1', 'Release', name=name)
release.spec.rollbackLimit = 1
release.spec.forProvider.chart.repository = 'https://charts.loft.sh'
release.spec.forProvider.chart.name = 'vcluster'
release.spec.forProvider.chart.version = '0.26.0'
release.spec.forProvider.namespace = namespace
release.spec.forProvider.values.controlPlane.proxy.extraSANs[0] = f'{name}.{namespace}'

# This will work once crossplane-sdk-python is updated to the V2 function api
#secret = self.requireds.Secret('v1', 'Secret', 'argocd', 'argocd-secret')[0]
secret = self.requireds.Secret('v1', 'Secret', labels={'app.kubernetes.io/name':'argocd-secret'})[0]
self.resources.Secret('v1', 'Secret', 'default', 'argocd-secret').data = secret.data
secret_name = f'vc-{name}'
# This will work once crossplane-sdk-python is updated to the v2 function api
#vcluster_secrets = self.requireds.Secret('v1', 'Secret', namespace, secret_name)
vcluster_secrets = self.requireds.Secret('v1', 'Secret', labels={'vcluster-name':name})
for secret in vcluster_secrets:
if secret.metadata.name != secret_name:
continue
argocd_secret = self.resources.secret('v1', 'Secret', 'argocd', secret_name)
argocd_secret.metadata.labels['argocd.argoproj.io/secret-type'] = 'cluster'
argocd_secret.type = 'Opaque'
argocd_secret.data.name = B64Encode(name)
argocd_secret.data.server = B64Encode(f'https://{name}.{namespace}:443')
argocd_secret.data.config = B64Encode(format(argcd_secret_config(secret), 'json'))
argocd_secret.ready = argocd_secret.observed.data
break
else:
self.ready = False
17 changes: 0 additions & 17 deletions examples/helm-copy-secret/definition.yaml

This file was deleted.

6 changes: 3 additions & 3 deletions examples/helm-copy-secret/xr.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
apiVersion: pythonic.fortra.com/v1alpha1
kind: ArgoCD
apiVersion: example.joebowbeer.com/v1alpha1
kind: XCluster
metadata:
name: argocd
name: xc1
spec: {}
File renamed without changes.
10 changes: 5 additions & 5 deletions function/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def ready(self):
def ready(self, ready):
if ready:
ready = fnv1.Ready.READY_TRUE
elif ready == None or (isinstance(ready, function.protobuf.Values) and ready._type == function.protobuf.Values.Type.UNKNOWN):
elif ready == None or (isinstance(ready, function.protobuf.Values) and ready._isUnknown):
ready = fnv1.Ready.READY_UNSPECIFIED
else:
ready = fnv1.Ready.READY_FALSE
Expand Down Expand Up @@ -209,7 +209,7 @@ def ready(self):
def ready(self, ready):
if ready:
ready = fnv1.Ready.READY_TRUE
elif ready == None or (isinstance(ready, function.protobuf.Values) and ready._type == function.protobuf.Values.Type.UNKNOWN):
elif ready == None or (isinstance(ready, function.protobuf.Values) and ready._isUnknown):
ready = fnv1.Ready.READY_UNSPECIFIED
else:
ready = fnv1.Ready.READY_FALSE
Expand Down Expand Up @@ -415,7 +415,7 @@ def claim(self, claim):
if bool(self):
if claim:
self._result.target = fnv1.Target.TARGET_COMPOSITE_AND_CLAIM
elif claim == None or (isinstance(claim, function.protobuf.Values) and claim._type == function.protobuf.Values.Type.UNKNOWN):
elif claim == None or (isinstance(claim, function.protobuf.Values) and claim._isUnknown):
self._result.target = fnv1.Target.TARGET_UNSPECIFIED
else:
self._result.target = fnv1.Target.TARGET_COMPOSITE
Expand Down Expand Up @@ -502,7 +502,7 @@ def status(self, status):
condition.status = fnv1.Status.STATUS_CONDITION_TRUE
elif status == None:
condition.status = fnv1.Status.STATUS_CONDITION_UNKNOWN
elif isinstance(ready, function.protobuf.Values) and ready._type == function.protobuf.Values.Type.UNKNOWN:
elif isinstance(status, function.protobuf.Values) and status._isUnknown:
condition.status = fnv1.Status.STATUS_CONDITION_UNSPECIFIED
else:
condition.status = fnv1.Status.STATUS_CONDITION_FALSE
Expand Down Expand Up @@ -549,7 +549,7 @@ def claim(self, claim):
condition = self._find_condition(True)
if claim:
condition.target = fnv1.Target.TARGET_COMPOSITE_AND_CLAIM
elif claim == None or (isinstance(claim, function.protobuf.Values) and claim._type == function.protobuf.Values.Type.UNKNOWN):
elif claim == None or (isinstance(claim, function.protobuf.Values) and claim._isUnknown):
condition.target = fnv1.Target.TARGET_UNSPECIFIED
else:
condition.target = fnv1.Target.TARGET_COMPOSITE
Expand Down
12 changes: 4 additions & 8 deletions function/fn.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""A Crossplane composition function."""

import asyncio
import base64
import inspect

import grpc
Expand All @@ -21,15 +22,8 @@ def __init__(self):
self.modules = {}

async def RunFunction(
self, request: fnv1.RunFunctionRequest, context: grpc.aio.ServicerContext
self, request: fnv1.RunFunctionRequest, _: grpc.aio.ServicerContext
) -> fnv1.RunFunctionResponse:
try:
return await self.run(request, context)
except Exception as e:
self.logger.exception('Error during RunFuction')
raise

async def run(self, request, context):
composite = request.observed.composite.resource
logger = self.logger.bind(
apiVersion=composite['apiVersion'],
Expand Down Expand Up @@ -114,3 +108,5 @@ def __init__(self):
self.List = function.protobuf.List
self.Yaml = function.protobuf.Yaml
self.Json = function.protobuf.Json
self.B64Encode = lambda s: base64.b64encode(s.encode('utf-8')).decode('utf-8')
self.B64Decode = lambda s: base64.b64decode(s.encode('utf-8')).decode('utf-8')
Loading
Loading