Compare commits
No commits in common. "main" and "v0.6.0" have entirely different histories.
|
|
@ -120,7 +120,7 @@ jobs:
|
|||
path: "dist"
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||
uses: pypa/gh-action-pypi-publish@v1.12.3
|
||||
with:
|
||||
# Note that this is currently being pushed to the 'crossplane' PyPI
|
||||
# user (not org). See @negz if you need access - PyPI requires 2FA to
|
||||
|
|
|
|||
22
CODEOWNERS
22
CODEOWNERS
|
|
@ -1,22 +0,0 @@
|
|||
|
||||
# SPDX-FileCopyrightText: 2025 The Crossplane Authors <https://crossplane.io>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# This file controls automatic PR reviewer assignment. See the following docs:
|
||||
#
|
||||
# * https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||
# * https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team
|
||||
#
|
||||
# The goal of this file is for most PRs to automatically and fairly have one
|
||||
# maintainer set as PR reviewers. All maintainers have permission to approve
|
||||
# and merge PRs. All PRs must be approved by at least one maintainer before being merged.
|
||||
#
|
||||
# Where possible, prefer explicitly specifying a maintainer who is a subject
|
||||
# matter expert for a particular part of the codebase rather than using fallback
|
||||
# owners. Fallback owners are listed at the bottom of this file.
|
||||
#
|
||||
# See also OWNERS.md for governance details
|
||||
|
||||
# Fallback owners
|
||||
* @negz @bobh66
|
||||
18
OWNERS.md
18
OWNERS.md
|
|
@ -1,18 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2025 The Crossplane Authors <https://crossplane.io>
|
||||
|
||||
SPDX-License-Identifier: CC-BY-4.0
|
||||
-->
|
||||
|
||||
# OWNERS
|
||||
|
||||
This page lists all maintainers for **this** repository. Each repository in the
|
||||
[Crossplane Contrib organization](https://github.com/crossplane-contrib/) will list their
|
||||
repository maintainers in their own `OWNERS.md` file.
|
||||
|
||||
## Maintainers
|
||||
* Nic Cope <negz@upbound.com> ([negz](https://github.com/negz))
|
||||
* Bob Haddleton <bob.haddleton@nokia.com> ([bobh66](https://github.com/bobh66))
|
||||
|
||||
|
||||
See [CODEOWNERS](./CODEOWNERS) for automatic PR assignment.
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# NO CHECKED-IN PROTOBUF GENCODE
|
||||
# source: crossplane/function/proto/v1/run_function.proto
|
||||
# Protobuf Python Version: 5.29.0
|
||||
# Protobuf Python Version: 5.27.2
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
|
|
@ -12,8 +12,8 @@ from google.protobuf.internal import builder as _builder
|
|||
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||
_runtime_version.Domain.PUBLIC,
|
||||
5,
|
||||
29,
|
||||
0,
|
||||
27,
|
||||
2,
|
||||
'',
|
||||
'crossplane/function/proto/v1/run_function.proto'
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import warnings
|
|||
|
||||
from crossplane.function.proto.v1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1_dot_run__function__pb2
|
||||
|
||||
GRPC_GENERATED_VERSION = '1.71.0'
|
||||
GRPC_GENERATED_VERSION = '1.66.0'
|
||||
GRPC_VERSION = grpc.__version__
|
||||
_version_not_supported = False
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# NO CHECKED-IN PROTOBUF GENCODE
|
||||
# source: crossplane/function/proto/v1beta1/run_function.proto
|
||||
# Protobuf Python Version: 5.29.0
|
||||
# Protobuf Python Version: 5.27.2
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
|
|
@ -12,8 +12,8 @@ from google.protobuf.internal import builder as _builder
|
|||
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||
_runtime_version.Domain.PUBLIC,
|
||||
5,
|
||||
29,
|
||||
0,
|
||||
27,
|
||||
2,
|
||||
'',
|
||||
'crossplane/function/proto/v1beta1/run_function.proto'
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import warnings
|
|||
|
||||
from crossplane.function.proto.v1beta1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1beta1_dot_run__function__pb2
|
||||
|
||||
GRPC_GENERATED_VERSION = '1.71.0'
|
||||
GRPC_GENERATED_VERSION = '1.66.0'
|
||||
GRPC_VERSION = grpc.__version__
|
||||
_version_not_supported = False
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import dataclasses
|
|||
import datetime
|
||||
|
||||
import pydantic
|
||||
from google.protobuf import json_format
|
||||
from google.protobuf import struct_pb2 as structpb
|
||||
|
||||
import crossplane.function.proto.v1.run_function_pb2 as fnv1
|
||||
|
|
@ -45,8 +44,8 @@ def update(r: fnv1.Resource, source: dict | structpb.Struct | pydantic.BaseModel
|
|||
# apiVersion is set to its default value 's3.aws.upbound.io/v1beta2'
|
||||
# (and not explicitly provided during initialization), it will be
|
||||
# excluded from the serialized output.
|
||||
data["apiVersion"] = source.apiVersion
|
||||
data["kind"] = source.kind
|
||||
data['apiVersion'] = source.apiVersion
|
||||
data['kind'] = source.kind
|
||||
r.resource.update(data)
|
||||
case structpb.Struct():
|
||||
# TODO(negz): Use struct_to_dict and update to match other semantics?
|
||||
|
|
@ -66,7 +65,9 @@ def dict_to_struct(d: dict) -> structpb.Struct:
|
|||
function makes it possible to work with a Python dict, then convert it to a
|
||||
struct in a RunFunctionResponse.
|
||||
"""
|
||||
return json_format.ParseDict(d, structpb.Struct())
|
||||
s = structpb.Struct()
|
||||
s.update(d)
|
||||
return s
|
||||
|
||||
|
||||
def struct_to_dict(s: structpb.Struct) -> dict:
|
||||
|
|
@ -76,7 +77,10 @@ def struct_to_dict(s: structpb.Struct) -> dict:
|
|||
protobuf struct. This function makes it possible to convert resources to a
|
||||
dictionary.
|
||||
"""
|
||||
return json_format.MessageToDict(s, preserving_proto_field_name=True)
|
||||
return {
|
||||
k: (struct_to_dict(v) if isinstance(v, structpb.Struct) else v)
|
||||
for k, v in s.items()
|
||||
}
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import asyncio
|
||||
import os
|
||||
import signal
|
||||
|
||||
import grpc
|
||||
from grpc_reflection.v1alpha import reflection
|
||||
|
|
@ -32,8 +31,6 @@ SERVICE_NAMES = (
|
|||
fnv1beta1.DESCRIPTOR.services_by_name["FunctionRunnerService"].full_name,
|
||||
)
|
||||
|
||||
SHUTDOWN_GRACE_PERIOD_SECONDS = 5
|
||||
|
||||
|
||||
def load_credentials(tls_certs_dir: str) -> grpc.ServerCredentials:
|
||||
"""Load TLS credentials for a composition function gRPC server.
|
||||
|
|
@ -93,11 +90,6 @@ def serve(
|
|||
|
||||
server = grpc.aio.server()
|
||||
|
||||
loop.add_signal_handler(
|
||||
signal.SIGTERM,
|
||||
lambda: asyncio.ensure_future(server.stop(grace=SHUTDOWN_GRACE_PERIOD_SECONDS)),
|
||||
)
|
||||
|
||||
grpcv1.add_FunctionRunnerServiceServicer_to_server(function, server)
|
||||
grpcv1beta1.add_FunctionRunnerServiceServicer_to_server(
|
||||
BetaFunctionRunner(wrapped=function), server
|
||||
|
|
@ -124,7 +116,7 @@ def serve(
|
|||
try:
|
||||
loop.run_until_complete(start())
|
||||
finally:
|
||||
loop.run_until_complete(server.stop(grace=SHUTDOWN_GRACE_PERIOD_SECONDS))
|
||||
loop.run_until_complete(server.stop(grace=5))
|
||||
loop.close()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,15 +14,14 @@ classifiers = [
|
|||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"grpcio==1.73.1",
|
||||
"grpcio==1.*",
|
||||
"grpcio-reflection==1.*",
|
||||
"protobuf==6.31.1", # Must be compatible with grpcio-tools.
|
||||
"protobuf==5.29.3",
|
||||
"pydantic==2.*",
|
||||
"structlog==25.*",
|
||||
"structlog==24.*",
|
||||
]
|
||||
|
||||
dynamic = ["version"]
|
||||
|
|
@ -39,16 +38,13 @@ validate-bump = false # Allow going from 0.0.0.dev0+x to 0
|
|||
[tool.hatch.envs.default]
|
||||
type = "virtual"
|
||||
path = ".venv-default"
|
||||
dependencies = ["ipython==9.4.0"]
|
||||
dependencies = ["ipython==8.31.0"]
|
||||
|
||||
[tool.hatch.envs.generate]
|
||||
type = "virtual"
|
||||
detached = true
|
||||
path = ".venv-generate"
|
||||
dependencies = [
|
||||
"grpcio-tools==1.73.1",
|
||||
"protobuf==6.31.1",
|
||||
]
|
||||
dependencies = ["grpcio-tools==1.69.0"]
|
||||
|
||||
[tool.hatch.envs.generate.scripts]
|
||||
protoc = "python -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. crossplane/function/proto/v1beta1/run_function.proto crossplane/function/proto/v1/run_function.proto"
|
||||
|
|
@ -66,8 +62,8 @@ packages = ["crossplane"]
|
|||
|
||||
# This special environment is used by hatch fmt.
|
||||
[tool.hatch.envs.hatch-static-analysis]
|
||||
dependencies = ["ruff==0.12.5"]
|
||||
config-path = "none" # Disable Hatch's default Ruff config.
|
||||
dependencies = ["ruff==0.9.0"]
|
||||
config-path = "none" # Disable Hatch's default Ruff config.
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py311"
|
||||
|
|
|
|||
|
|
@ -248,66 +248,6 @@ class TestResource(unittest.TestCase):
|
|||
dataclasses.asdict(case.want), dataclasses.asdict(got), "-want, +got"
|
||||
)
|
||||
|
||||
def test_dict_to_struct(self) -> None:
|
||||
@dataclasses.dataclass
|
||||
class TestCase:
|
||||
reason: str
|
||||
d: dict
|
||||
want: structpb.Struct
|
||||
|
||||
cases = [
|
||||
TestCase(
|
||||
reason="Convert an empty dictionary to a struct.",
|
||||
d={},
|
||||
want=structpb.Struct(),
|
||||
),
|
||||
TestCase(
|
||||
reason="Convert a dictionary with a single field to a struct.",
|
||||
d={"foo": "bar"},
|
||||
want=structpb.Struct(
|
||||
fields={"foo": structpb.Value(string_value="bar")}
|
||||
),
|
||||
),
|
||||
TestCase(
|
||||
reason="Convert a nested dictionary to a struct.",
|
||||
d={"foo": {"bar": "baz"}},
|
||||
want=structpb.Struct(
|
||||
fields={
|
||||
"foo": structpb.Value(
|
||||
struct_value=structpb.Struct(
|
||||
fields={"bar": structpb.Value(string_value="baz")}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
),
|
||||
TestCase(
|
||||
reason="Convert a nested dictionary containing lists to a struct.",
|
||||
d={"foo": {"bar": ["baz", "qux"]}},
|
||||
want=structpb.Struct(
|
||||
fields={
|
||||
"foo": structpb.Value(
|
||||
struct_value=structpb.Struct(
|
||||
fields={
|
||||
"bar": structpb.Value(
|
||||
list_value=structpb.ListValue(
|
||||
values=[
|
||||
structpb.Value(string_value="baz"),
|
||||
structpb.Value(string_value="qux"),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
),
|
||||
]
|
||||
for case in cases:
|
||||
got = resource.dict_to_struct(case.d)
|
||||
self.assertEqual(case.want, got, "-want, +got")
|
||||
|
||||
def test_struct_to_dict(self) -> None:
|
||||
@dataclasses.dataclass
|
||||
class TestCase:
|
||||
|
|
@ -339,28 +279,6 @@ class TestResource(unittest.TestCase):
|
|||
),
|
||||
want={"foo": {"bar": "baz"}},
|
||||
),
|
||||
TestCase(
|
||||
reason="Convert a nested struct containing ListValues to a dictionary.",
|
||||
s=structpb.Struct(
|
||||
fields={
|
||||
"foo": structpb.Value(
|
||||
struct_value=structpb.Struct(
|
||||
fields={
|
||||
"bar": structpb.Value(
|
||||
list_value=structpb.ListValue(
|
||||
values=[
|
||||
structpb.Value(string_value="baz"),
|
||||
structpb.Value(string_value="qux"),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
want={"foo": {"bar": ["baz", "qux"]}},
|
||||
),
|
||||
]
|
||||
|
||||
for case in cases:
|
||||
|
|
|
|||
Loading…
Reference in New Issue