crossplane-function-pythonic 0.6.1__py3-none-any.whl → 0.6.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crossplane/pythonic/__about__.py +8 -2
- crossplane/pythonic/__init__.py +21 -13
- crossplane/pythonic/__main__.py +1 -0
- crossplane/pythonic/auto_ready.py +89 -41
- crossplane/pythonic/command.py +43 -39
- crossplane/pythonic/composite.py +93 -67
- crossplane/pythonic/diff.py +880 -0
- crossplane/pythonic/function.py +194 -99
- crossplane/pythonic/grpc.py +85 -62
- crossplane/pythonic/main.py +13 -4
- crossplane/pythonic/packages.py +77 -50
- crossplane/pythonic/proto/v1/run_function_pb2.py +3 -3
- crossplane/pythonic/proto/v1/run_function_pb2_grpc.py +4 -4
- crossplane/pythonic/protobuf.py +281 -214
- crossplane/pythonic/render.py +413 -201
- crossplane/pythonic/version.py +2 -3
- {crossplane_function_pythonic-0.6.1.dist-info → crossplane_function_pythonic-0.6.3.dist-info}/METADATA +23 -25
- crossplane_function_pythonic-0.6.3.dist-info/RECORD +22 -0
- {crossplane_function_pythonic-0.6.1.dist-info → crossplane_function_pythonic-0.6.3.dist-info}/WHEEL +1 -1
- {crossplane_function_pythonic-0.6.1.dist-info → crossplane_function_pythonic-0.6.3.dist-info}/entry_points.txt +1 -0
- crossplane_function_pythonic-0.6.1.dist-info/RECORD +0 -22
- crossplane_function_pythonic-0.6.1.dist-info/licenses/LICENSE +0 -201
crossplane/pythonic/__about__.py
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
__version__ = version(
|
|
5
|
+
"crossplane-function-pythonic"
|
|
6
|
+
) # Use the exact [project.name] from pyproject.toml
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
__version__ = "0.0.0"
|
crossplane/pythonic/__init__.py
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
from .composite import BaseComposite
|
|
4
|
-
from .protobuf import
|
|
2
|
+
from .protobuf import (
|
|
3
|
+
B64Decode,
|
|
4
|
+
B64Encode,
|
|
5
|
+
Json,
|
|
6
|
+
List,
|
|
7
|
+
Map,
|
|
8
|
+
Unknown,
|
|
9
|
+
Yaml,
|
|
10
|
+
YamlAll,
|
|
11
|
+
append,
|
|
12
|
+
)
|
|
5
13
|
|
|
6
14
|
__all__ = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
"B64Decode",
|
|
16
|
+
"B64Encode",
|
|
17
|
+
"BaseComposite",
|
|
18
|
+
"Json",
|
|
19
|
+
"List",
|
|
20
|
+
"Map",
|
|
21
|
+
"Unknown",
|
|
22
|
+
"Yaml",
|
|
23
|
+
"YamlAll",
|
|
24
|
+
"append",
|
|
17
25
|
]
|
crossplane/pythonic/__main__.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
def resource_ready(resource):
|
|
3
2
|
if not resource.observed:
|
|
4
3
|
return None
|
|
5
|
-
return _checks.get(
|
|
4
|
+
return _checks.get(
|
|
5
|
+
(resource.observed.apiVersion, resource.observed.kind), _check_default
|
|
6
|
+
).ready(resource)
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class ReadyCondition:
|
|
@@ -16,35 +17,42 @@ class ReadyCondition:
|
|
|
16
17
|
return resource.status.notReadyCondition[ready.reason]
|
|
17
18
|
return resource.status.notReadyCondition
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
_checks = {}
|
|
20
22
|
_check_default = ReadyCondition()
|
|
21
23
|
|
|
24
|
+
|
|
22
25
|
class Check:
|
|
23
26
|
@classmethod
|
|
24
27
|
def __init_subclass__(cls, **kwargs):
|
|
25
28
|
super().__init_subclass__(**kwargs)
|
|
26
|
-
if hasattr(cls,
|
|
29
|
+
if hasattr(cls, "apiVersion"):
|
|
27
30
|
_checks[(cls.apiVersion, cls.__name__)] = cls()
|
|
28
31
|
|
|
29
32
|
def ready(self, resource):
|
|
30
33
|
raise NotImplementedError()
|
|
31
34
|
|
|
35
|
+
|
|
32
36
|
class AlwaysReady(Check):
|
|
33
37
|
def ready(self, resource):
|
|
34
38
|
return resource.observed.metadata.name
|
|
35
39
|
|
|
36
40
|
|
|
37
41
|
class ClusterRole(AlwaysReady):
|
|
38
|
-
apiVersion =
|
|
42
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
43
|
+
|
|
39
44
|
|
|
40
45
|
class ClusterRoleBinding(AlwaysReady):
|
|
41
|
-
apiVersion =
|
|
46
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
47
|
+
|
|
42
48
|
|
|
43
49
|
class ConfigMap(AlwaysReady):
|
|
44
|
-
apiVersion =
|
|
50
|
+
apiVersion = "v1"
|
|
51
|
+
|
|
45
52
|
|
|
46
53
|
class CronJob(Check):
|
|
47
|
-
apiVersion =
|
|
54
|
+
apiVersion = "batch/v1"
|
|
55
|
+
|
|
48
56
|
def ready(self, resource):
|
|
49
57
|
if resource.observed.spec.suspend and len(resource.observed.spec.suspend):
|
|
50
58
|
return resource.observed.metadata.name
|
|
@@ -54,17 +62,21 @@ class CronJob(Check):
|
|
|
54
62
|
return resource.observed.metadata.name
|
|
55
63
|
if not resource.status.lastSuccessfulTime:
|
|
56
64
|
return resource.status.lastSuccessfulTime
|
|
57
|
-
if str(resource.status.lastSuccessfulTime) < str(
|
|
65
|
+
if str(resource.status.lastSuccessfulTime) < str(
|
|
66
|
+
resource.status.lastScheduleTime
|
|
67
|
+
):
|
|
58
68
|
return resource.status.successfulBeforeSchedule
|
|
59
69
|
return resource.observed.metadata.name
|
|
60
70
|
|
|
71
|
+
|
|
61
72
|
class DaemonSet(Check):
|
|
62
|
-
apiVersion =
|
|
73
|
+
apiVersion = "apps/v1"
|
|
74
|
+
|
|
63
75
|
def ready(self, resource):
|
|
64
76
|
scheduled = resource.status.desiredNumberScheduled
|
|
65
77
|
if not scheduled:
|
|
66
78
|
return scheduled
|
|
67
|
-
for field in (
|
|
79
|
+
for field in ("numberReady", "updatedNumberScheduled", "numberAvailable"):
|
|
68
80
|
value = resource.status[field]
|
|
69
81
|
if not value:
|
|
70
82
|
return value
|
|
@@ -72,16 +84,18 @@ class DaemonSet(Check):
|
|
|
72
84
|
return resource.status[f"{field}NotScheduled"]
|
|
73
85
|
return resource.observed.metadata.name
|
|
74
86
|
|
|
87
|
+
|
|
75
88
|
class Deployment(Check):
|
|
76
|
-
apiVersion =
|
|
89
|
+
apiVersion = "apps/v1"
|
|
90
|
+
|
|
77
91
|
def ready(self, resource):
|
|
78
92
|
replicas = resource.observed.spec.replicas or 1
|
|
79
|
-
for field in (
|
|
93
|
+
for field in ("updatedReplicas", "availableReplicas"):
|
|
80
94
|
value = resource.status[field]
|
|
81
95
|
if not value:
|
|
82
96
|
return value
|
|
83
97
|
if replicas != value:
|
|
84
|
-
return resource.status[
|
|
98
|
+
return resource.status[f"{field}NotReplicas"]
|
|
85
99
|
available = resource.conditions.Available
|
|
86
100
|
if not available:
|
|
87
101
|
return resource.status.notAvailable
|
|
@@ -91,28 +105,39 @@ class Deployment(Check):
|
|
|
91
105
|
return resource.status.notAvailable
|
|
92
106
|
return resource.observed.metadata.name
|
|
93
107
|
|
|
108
|
+
|
|
94
109
|
class HorizontalPodAutoscaler(Check):
|
|
95
|
-
apiVersion =
|
|
110
|
+
apiVersion = "autoscaling/v2"
|
|
111
|
+
|
|
96
112
|
def ready(self, resource):
|
|
97
|
-
for type in (
|
|
113
|
+
for type in (
|
|
114
|
+
"FailedGetScale",
|
|
115
|
+
"FailedUpdateScale",
|
|
116
|
+
"FailedGetResourceMetric",
|
|
117
|
+
"InvalidSelector",
|
|
118
|
+
):
|
|
98
119
|
if resource.conditions[type].status:
|
|
99
120
|
return resource.status[f"is{type}"]
|
|
100
|
-
for type in (
|
|
121
|
+
for type in ("ScalingActive", "ScalingLimited"):
|
|
101
122
|
if resource.conditions[type].status:
|
|
102
123
|
return resource.observed.metadata.name
|
|
103
124
|
return resource.status.notScalingActiveOrLimiited
|
|
104
125
|
|
|
126
|
+
|
|
105
127
|
class Ingress(Check):
|
|
106
|
-
apiVersion =
|
|
128
|
+
apiVersion = "networking.k8s.io/v1"
|
|
129
|
+
|
|
107
130
|
def ready(self, resource):
|
|
108
131
|
if not len(resource.status.loadBalancer.ingress):
|
|
109
132
|
return resource.status.noLoadBalanceIngresses
|
|
110
133
|
return resource.observed.metadata.name
|
|
111
134
|
|
|
135
|
+
|
|
112
136
|
class Job(Check):
|
|
113
|
-
apiVersion =
|
|
137
|
+
apiVersion = "batch/v1"
|
|
138
|
+
|
|
114
139
|
def ready(self, resource):
|
|
115
|
-
for type in (
|
|
140
|
+
for type in ("Failed", "Suspended"):
|
|
116
141
|
if resource.conditions[type].status:
|
|
117
142
|
return resource.status[f"is{type}"]
|
|
118
143
|
complete = resource.conditions.Complete
|
|
@@ -124,71 +149,94 @@ class Job(Check):
|
|
|
124
149
|
return resource.status.notComplete
|
|
125
150
|
return resource.observed.metadata.name
|
|
126
151
|
|
|
152
|
+
|
|
127
153
|
class Namespace(AlwaysReady):
|
|
128
|
-
apiVersion =
|
|
154
|
+
apiVersion = "v1"
|
|
155
|
+
|
|
129
156
|
|
|
130
157
|
class PersistentVolumeClaim(Check):
|
|
131
|
-
apiVersion =
|
|
158
|
+
apiVersion = "v1"
|
|
159
|
+
|
|
132
160
|
def ready(self, resource):
|
|
133
|
-
if resource.status.phase !=
|
|
161
|
+
if resource.status.phase != "Bound":
|
|
134
162
|
return resource.status.phaseNotBound
|
|
135
163
|
return resource.observed.metadata.name
|
|
136
164
|
|
|
165
|
+
|
|
137
166
|
class Pod(Check):
|
|
138
|
-
apiVersion =
|
|
167
|
+
apiVersion = "v1"
|
|
168
|
+
|
|
139
169
|
def ready(self, resource):
|
|
140
|
-
if resource.status.phase ==
|
|
170
|
+
if resource.status.phase == "Succeeded":
|
|
171
|
+
return resource.observed.metadata.name
|
|
172
|
+
if (
|
|
173
|
+
resource.status.phase == "Running"
|
|
174
|
+
and resource.observed.spec.restartPolicy == "Always"
|
|
175
|
+
and resource.conditions.Ready.status
|
|
176
|
+
):
|
|
141
177
|
return resource.observed.metadata.name
|
|
142
|
-
if resource.status.phase == 'Running':
|
|
143
|
-
if resource.observed.spec.restartPolicy == 'Always':
|
|
144
|
-
if resource.conditions.Ready.status:
|
|
145
|
-
return resource.observed.metadata.name
|
|
146
178
|
return resource.status.notSucceededOrRunning
|
|
147
179
|
|
|
180
|
+
|
|
148
181
|
class ReplicaSet(Check):
|
|
149
|
-
apiVersion =
|
|
182
|
+
apiVersion = "v1"
|
|
183
|
+
|
|
150
184
|
def ready(self, resource):
|
|
151
|
-
if int(resource.status.observedGeneration) < int(
|
|
185
|
+
if int(resource.status.observedGeneration) < int(
|
|
186
|
+
resource.observed.metadata.generation
|
|
187
|
+
):
|
|
152
188
|
return resource.status.priorObservedGeneration
|
|
153
189
|
if resource.conditions.ReplicaFailure.status:
|
|
154
190
|
if resource.conditions.ReplicaFailure.reason:
|
|
155
|
-
return resource.status.isReplicaFailure[
|
|
191
|
+
return resource.status.isReplicaFailure[
|
|
192
|
+
resource.conditions.ReplicaFailure.reason
|
|
193
|
+
]
|
|
156
194
|
return resource.status.isReplicaFailure
|
|
157
|
-
if int(resource.status.availableReplicas) < int(
|
|
195
|
+
if int(resource.status.availableReplicas) < int(
|
|
196
|
+
resource.observed.spec.replicas or 1
|
|
197
|
+
):
|
|
158
198
|
return resource.status.tooFewavailableReplicas
|
|
159
199
|
return resource.observed.metadata.name
|
|
160
200
|
|
|
201
|
+
|
|
161
202
|
class Role(AlwaysReady):
|
|
162
|
-
apiVersion =
|
|
203
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
204
|
+
|
|
163
205
|
|
|
164
206
|
class RoleBinding(AlwaysReady):
|
|
165
|
-
apiVersion =
|
|
207
|
+
apiVersion = "rbac.authorization.k8s.io/v1"
|
|
208
|
+
|
|
166
209
|
|
|
167
210
|
class Secret(AlwaysReady):
|
|
168
|
-
apiVersion =
|
|
211
|
+
apiVersion = "v1"
|
|
212
|
+
|
|
169
213
|
|
|
170
214
|
class Service(Check):
|
|
171
|
-
apiVersion =
|
|
215
|
+
apiVersion = "v1"
|
|
216
|
+
|
|
172
217
|
def ready(self, resource):
|
|
173
|
-
if resource.observed.spec.type !=
|
|
218
|
+
if resource.observed.spec.type != "LoadBalancer":
|
|
174
219
|
return resource.observed.metadata.name
|
|
175
220
|
if not len(resource.status.loadBalancer.ingress):
|
|
176
221
|
return resource.status.noLoadBalancerIngresses
|
|
177
222
|
return resource.observed.metadata.name
|
|
178
223
|
|
|
224
|
+
|
|
179
225
|
class ServiceAccount(AlwaysReady):
|
|
180
|
-
apiVersion =
|
|
226
|
+
apiVersion = "v1"
|
|
227
|
+
|
|
181
228
|
|
|
182
229
|
class StatefulSet(Check):
|
|
183
|
-
apiVersion =
|
|
230
|
+
apiVersion = "apps/v1"
|
|
231
|
+
|
|
184
232
|
def ready(self, resource):
|
|
185
233
|
replicas = resource.observed.spec.replicas or 1
|
|
186
|
-
for field in (
|
|
234
|
+
for field in ("readyReplicas", "currentReplicas"):
|
|
187
235
|
value = resource.status[field]
|
|
188
236
|
if not value:
|
|
189
237
|
return value
|
|
190
238
|
if replicas != value:
|
|
191
|
-
return resource.status[
|
|
239
|
+
return resource.status[f"{field}NotReplicas"]
|
|
192
240
|
if resource.status.currentRevision != resource.status.updateRevision:
|
|
193
241
|
return resource.status.currentRevisionNotUpdateReivsion
|
|
194
242
|
return resource.observed.metadata.name
|
crossplane/pythonic/command.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import logging
|
|
3
2
|
import pathlib
|
|
4
3
|
import sys
|
|
@@ -11,7 +10,9 @@ class Command:
|
|
|
11
10
|
|
|
12
11
|
@classmethod
|
|
13
12
|
def create(cls, subparsers):
|
|
14
|
-
parser = subparsers.add_parser(
|
|
13
|
+
parser = subparsers.add_parser(
|
|
14
|
+
cls.name, help=cls.help, description=cls.description
|
|
15
|
+
)
|
|
15
16
|
parser.set_defaults(command=cls)
|
|
16
17
|
cls.add_parser_arguments(parser)
|
|
17
18
|
|
|
@@ -22,45 +23,47 @@ class Command:
|
|
|
22
23
|
@classmethod
|
|
23
24
|
def add_function_arguments(cls, parser):
|
|
24
25
|
parser.add_argument(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
"--debug",
|
|
27
|
+
"-d",
|
|
28
|
+
action="store_true",
|
|
29
|
+
help="Emit debug logs.",
|
|
28
30
|
)
|
|
29
31
|
parser.add_argument(
|
|
30
|
-
|
|
32
|
+
"--log-name-width",
|
|
31
33
|
type=int,
|
|
32
34
|
default=40,
|
|
33
|
-
metavar=
|
|
34
|
-
help=
|
|
35
|
+
metavar="WIDTH",
|
|
36
|
+
help="Width of the logger name in the log output, default 40.",
|
|
35
37
|
)
|
|
36
38
|
parser.add_argument(
|
|
37
|
-
|
|
38
|
-
action=
|
|
39
|
+
"--logger-level",
|
|
40
|
+
action="append",
|
|
39
41
|
default=[],
|
|
40
|
-
metavar=
|
|
41
|
-
help=
|
|
42
|
+
metavar="LOGGER=LEVEL",
|
|
43
|
+
help="Logger level, for example: botocore.hooks=INFO",
|
|
42
44
|
)
|
|
43
45
|
parser.add_argument(
|
|
44
|
-
|
|
45
|
-
action=
|
|
46
|
+
"--python-path",
|
|
47
|
+
action="append",
|
|
46
48
|
default=[],
|
|
47
|
-
metavar=
|
|
48
|
-
help=
|
|
49
|
+
metavar="DIRECTORY",
|
|
50
|
+
help="Filing system directories to add to the python path.",
|
|
49
51
|
)
|
|
50
52
|
parser.add_argument(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
"--render-unknowns",
|
|
54
|
+
"-u",
|
|
55
|
+
action="store_true",
|
|
56
|
+
help="Render resources with unknowns, useful during local development.",
|
|
54
57
|
)
|
|
55
58
|
parser.add_argument(
|
|
56
|
-
|
|
57
|
-
action=
|
|
58
|
-
help=
|
|
59
|
+
"--allow-oversize-protos",
|
|
60
|
+
action="store_true",
|
|
61
|
+
help="Allow oversized protobuf messages",
|
|
59
62
|
)
|
|
60
63
|
parser.add_argument(
|
|
61
|
-
|
|
62
|
-
action=
|
|
63
|
-
help=
|
|
64
|
+
"--crossplane-v1",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="Enable Crossplane V1 compatibility mode",
|
|
64
67
|
)
|
|
65
68
|
|
|
66
69
|
def __init__(self, args=None):
|
|
@@ -78,14 +81,14 @@ class Command:
|
|
|
78
81
|
logger.handlers = [handler]
|
|
79
82
|
logger.setLevel(logging.DEBUG if self.args.debug else logging.INFO)
|
|
80
83
|
# Suppress noisy libraries, these can be overriden using --logger-level
|
|
81
|
-
logging.getLogger(
|
|
82
|
-
logging.getLogger(
|
|
83
|
-
logging.getLogger(
|
|
84
|
-
logging.getLogger(
|
|
85
|
-
logging.getLogger(
|
|
86
|
-
for
|
|
87
|
-
for logger_level in
|
|
88
|
-
logger_level = logger_level.split(
|
|
84
|
+
logging.getLogger("asyncio").setLevel(logging.INFO)
|
|
85
|
+
logging.getLogger("grpc").setLevel(logging.INFO)
|
|
86
|
+
logging.getLogger("httpcore").setLevel(logging.INFO)
|
|
87
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
88
|
+
logging.getLogger("kr8s").setLevel(logging.INFO)
|
|
89
|
+
for logger_levels in self.args.logger_level:
|
|
90
|
+
for logger_level in logger_levels.split(","):
|
|
91
|
+
logger_level = logger_level.split("=")
|
|
89
92
|
if len(logger_level) == 2:
|
|
90
93
|
logging.getLogger(logger_level[0]).setLevel(logger_level[1].upper())
|
|
91
94
|
|
|
@@ -94,6 +97,7 @@ class Command:
|
|
|
94
97
|
|
|
95
98
|
if self.args.allow_oversize_protos:
|
|
96
99
|
from google.protobuf.internal import api_implementation
|
|
100
|
+
|
|
97
101
|
if api_implementation._c_module:
|
|
98
102
|
api_implementation._c_module.SetAllowOversizeProtos(True)
|
|
99
103
|
|
|
@@ -103,10 +107,10 @@ class Command:
|
|
|
103
107
|
|
|
104
108
|
class Formatter(logging.Formatter):
|
|
105
109
|
def __init__(self, name_width):
|
|
106
|
-
super(
|
|
110
|
+
super().__init__(
|
|
107
111
|
f"[{{asctime}}.{{msecs:03.0f}}] {{sname:{name_width}.{name_width}}} [{{levelname:8.8}}] {{message}}",
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
"%Y-%m-%d %H:%M:%S",
|
|
113
|
+
"{",
|
|
110
114
|
)
|
|
111
115
|
self.name_width = name_width
|
|
112
116
|
|
|
@@ -114,12 +118,12 @@ class Formatter(logging.Formatter):
|
|
|
114
118
|
record.sname = record.name
|
|
115
119
|
extra = len(record.sname) - self.name_width
|
|
116
120
|
if extra > 0:
|
|
117
|
-
names = record.sname.split(
|
|
121
|
+
names = record.sname.split(".")
|
|
118
122
|
for ix, name in enumerate(names):
|
|
119
123
|
if len(name) > extra:
|
|
120
124
|
names[ix] = name[extra:]
|
|
121
125
|
break
|
|
122
126
|
names[ix] = name[:1]
|
|
123
127
|
extra -= len(name) - 1
|
|
124
|
-
record.sname =
|
|
125
|
-
return super(
|
|
128
|
+
record.sname = ".".join(names)
|
|
129
|
+
return super().format(record)
|