crossplane-function-pythonic 0.2.0__py3-none-any.whl → 0.2.1__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 +1 -1
- crossplane/pythonic/composite.py +2 -0
- crossplane/pythonic/grpc.py +5 -0
- crossplane/pythonic/protobuf.py +99 -29
- crossplane/pythonic/render.py +72 -42
- crossplane/pythonic/version.py +1 -1
- {crossplane_function_pythonic-0.2.0.dist-info → crossplane_function_pythonic-0.2.1.dist-info}/METADATA +14 -12
- crossplane_function_pythonic-0.2.1.dist-info/RECORD +17 -0
- crossplane_function_pythonic-0.2.0.dist-info/RECORD +0 -17
- {crossplane_function_pythonic-0.2.0.dist-info → crossplane_function_pythonic-0.2.1.dist-info}/WHEEL +0 -0
- {crossplane_function_pythonic-0.2.0.dist-info → crossplane_function_pythonic-0.2.1.dist-info}/entry_points.txt +0 -0
- {crossplane_function_pythonic-0.2.0.dist-info → crossplane_function_pythonic-0.2.1.dist-info}/licenses/LICENSE +0 -0
crossplane/pythonic/__about__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# This is set at build time, using "hatch version"
|
|
2
|
-
__version__ = "0.2.
|
|
2
|
+
__version__ = "0.2.1"
|
crossplane/pythonic/composite.py
CHANGED
|
@@ -40,6 +40,7 @@ class BaseComposite:
|
|
|
40
40
|
observed = self.request.observed.composite
|
|
41
41
|
desired = self.response.desired.composite
|
|
42
42
|
self.observed = observed.resource
|
|
43
|
+
self.observed._set_attribute('connection', self.request.observed.composite.connection_details)
|
|
43
44
|
self.desired = desired.resource
|
|
44
45
|
self.apiVersion = self.observed.apiVersion
|
|
45
46
|
self.kind = self.observed.kind
|
|
@@ -434,6 +435,7 @@ class RequiredResource:
|
|
|
434
435
|
self.data = self.observed.data
|
|
435
436
|
self.status = self.observed.status
|
|
436
437
|
self.conditions = Conditions(resource)
|
|
438
|
+
self.connection = self.observed.connection_details
|
|
437
439
|
|
|
438
440
|
def __bool__(self):
|
|
439
441
|
return bool(self.observed)
|
crossplane/pythonic/grpc.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import asyncio
|
|
3
|
+
import logging
|
|
3
4
|
import os
|
|
4
5
|
import pathlib
|
|
5
6
|
import shlex
|
|
@@ -10,10 +11,13 @@ import crossplane.function.proto.v1.run_function_pb2_grpc as grpcv1
|
|
|
10
11
|
import grpc
|
|
11
12
|
|
|
12
13
|
from . import (
|
|
14
|
+
__about__,
|
|
13
15
|
command,
|
|
14
16
|
function,
|
|
15
17
|
)
|
|
16
18
|
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
class Command(command.Command):
|
|
19
23
|
name = 'grpc'
|
|
@@ -77,6 +81,7 @@ class Command(command.Command):
|
|
|
77
81
|
pip._internal.cli.main.main(['install', '--user', *shlex.split(self.args.pip_install)])
|
|
78
82
|
|
|
79
83
|
self.initialize_function()
|
|
84
|
+
logger.info(f"Version: {__about__.__version__}")
|
|
80
85
|
|
|
81
86
|
# enables read only volumes or mismatched uid volumes
|
|
82
87
|
sys.dont_write_bytecode = True
|
crossplane/pythonic/protobuf.py
CHANGED
|
@@ -58,17 +58,21 @@ def B64Decode(string):
|
|
|
58
58
|
|
|
59
59
|
class Message:
|
|
60
60
|
def __init__(self, parent, key, descriptor, message=_Unknown, readOnly=False):
|
|
61
|
-
self.
|
|
62
|
-
self.
|
|
63
|
-
self.
|
|
64
|
-
self.
|
|
65
|
-
self.
|
|
66
|
-
self.
|
|
61
|
+
self._set_attribute('_parent', parent)
|
|
62
|
+
self._set_attribute('_key', key)
|
|
63
|
+
self._set_attribute('_descriptor', descriptor)
|
|
64
|
+
self._set_attribute('_message', message)
|
|
65
|
+
self._set_attribute('_readOnly', readOnly)
|
|
66
|
+
self._set_attribute('_cache', {})
|
|
67
|
+
|
|
68
|
+
def _set_attribute(self, key, value):
|
|
69
|
+
self.__dict__[key] = value
|
|
67
70
|
|
|
68
71
|
def __getattr__(self, key):
|
|
69
72
|
return self[key]
|
|
70
73
|
|
|
71
74
|
def __getitem__(self, key):
|
|
75
|
+
key = self._validate_key(key)
|
|
72
76
|
if key in self._cache:
|
|
73
77
|
return self._cache[key]
|
|
74
78
|
field = self._descriptor.fields_by_name.get(key)
|
|
@@ -156,6 +160,7 @@ class Message:
|
|
|
156
160
|
def _create_child(self, key):
|
|
157
161
|
if self._readOnly:
|
|
158
162
|
raise ValueError(f"{self._readOnly} is read only")
|
|
163
|
+
key = self._validate_key(key)
|
|
159
164
|
if self._message is _Unknown:
|
|
160
165
|
self.__dict__['_message'] = self._parent._create_child(self._key)
|
|
161
166
|
return getattr(self._message, key)
|
|
@@ -177,6 +182,7 @@ class Message:
|
|
|
177
182
|
def __setitem__(self, key, value):
|
|
178
183
|
if self._readOnly:
|
|
179
184
|
raise ValueError(f"{self._readOnly} is read only")
|
|
185
|
+
key = self._validate_key(key)
|
|
180
186
|
if key not in self._descriptor.fields_by_name:
|
|
181
187
|
raise AttributeError(obj=self, name=key)
|
|
182
188
|
field = self._descriptor.fields_by_name[key]
|
|
@@ -201,26 +207,40 @@ class Message:
|
|
|
201
207
|
def __delitem__(self, key):
|
|
202
208
|
if self._readOnly:
|
|
203
209
|
raise ValueError(f"{self._readOnly} is read only")
|
|
210
|
+
key = self._validate_key(key)
|
|
204
211
|
if key not in self._descriptor.fields_by_name:
|
|
205
212
|
raise AttributeError(obj=self, name=key)
|
|
206
213
|
if self._message is not _Unknown:
|
|
207
214
|
self._message.ClearField(key)
|
|
208
215
|
self._cache.pop(key, None)
|
|
209
216
|
|
|
217
|
+
def _validate_key(self, key):
|
|
218
|
+
if isinstance(key, FieldMessage):
|
|
219
|
+
key = key._value
|
|
220
|
+
elif isinstance(key, Value):
|
|
221
|
+
key = key._raw
|
|
222
|
+
if not isinstance(key, str):
|
|
223
|
+
raise TypeError(f"Unexpected key type: {key.__class__}")
|
|
224
|
+
return key
|
|
225
|
+
|
|
210
226
|
|
|
211
227
|
class MapMessage:
|
|
212
228
|
def __init__(self, parent, key, field, messages=_Unknown, readOnly=False):
|
|
213
|
-
self.
|
|
214
|
-
self.
|
|
215
|
-
self.
|
|
216
|
-
self.
|
|
217
|
-
self.
|
|
218
|
-
self.
|
|
229
|
+
self._set_attribute('_parent', parent)
|
|
230
|
+
self._set_attribute('_key', key)
|
|
231
|
+
self._set_attribute('_field', field)
|
|
232
|
+
self._set_attribute('_messages', messages)
|
|
233
|
+
self._set_attribute('_readOnly', readOnly)
|
|
234
|
+
self._set_attribute('_cache', {})
|
|
235
|
+
|
|
236
|
+
def _set_attribute(self, key, value):
|
|
237
|
+
self.__dict__[key] = value
|
|
219
238
|
|
|
220
239
|
def __getattr__(self, key):
|
|
221
240
|
return self[key]
|
|
222
241
|
|
|
223
242
|
def __getitem__(self, key):
|
|
243
|
+
key = self._validate_key(key)
|
|
224
244
|
if key in self._cache:
|
|
225
245
|
return self._cache[key]
|
|
226
246
|
if self._messages is _Unknown or key not in self._messages:
|
|
@@ -304,6 +324,7 @@ class MapMessage:
|
|
|
304
324
|
def _create_child(self, key):
|
|
305
325
|
if self._readOnly:
|
|
306
326
|
raise ValueError(f"{self._readOnly} is read only")
|
|
327
|
+
key = self._validate_key(key)
|
|
307
328
|
if self._messages is _Unknown:
|
|
308
329
|
self.__dict__['_messages'] = self._parent._create_child(self._key)
|
|
309
330
|
return self._messages[key]
|
|
@@ -325,6 +346,7 @@ class MapMessage:
|
|
|
325
346
|
def __setitem__(self, key, message):
|
|
326
347
|
if self._readOnly:
|
|
327
348
|
raise ValueError(f"{self._readOnly} is read only")
|
|
349
|
+
key = self._validate_key(key)
|
|
328
350
|
if self._messages is _Unknown:
|
|
329
351
|
self._messages = self._parent._create_child(self._key)
|
|
330
352
|
if isinstance(message, Message):
|
|
@@ -349,11 +371,21 @@ class MapMessage:
|
|
|
349
371
|
def __delitem__(self, key):
|
|
350
372
|
if self._readOnly:
|
|
351
373
|
raise ValueError(f"{self._readOnly} is read only")
|
|
374
|
+
key = self._validate_key(key)
|
|
352
375
|
if self._messages is not _Unknown:
|
|
353
376
|
if key in self._messages:
|
|
354
377
|
del self._messages[key]
|
|
355
378
|
self._cache.pop(key, None)
|
|
356
379
|
|
|
380
|
+
def _validate_key(self, key):
|
|
381
|
+
if isinstance(key, FieldMessage):
|
|
382
|
+
key = key._value
|
|
383
|
+
elif isinstance(key, Value):
|
|
384
|
+
key = key._raw
|
|
385
|
+
if not isinstance(key, str):
|
|
386
|
+
raise TypeError(f"Unexpected key type: {key.__class__}")
|
|
387
|
+
return key
|
|
388
|
+
|
|
357
389
|
|
|
358
390
|
class RepeatedMessage:
|
|
359
391
|
def __init__(self, parent, key, field, messages=_Unknown, readOnly=False):
|
|
@@ -365,6 +397,7 @@ class RepeatedMessage:
|
|
|
365
397
|
self._cache = {}
|
|
366
398
|
|
|
367
399
|
def __getitem__(self, key):
|
|
400
|
+
key = self._validate_key(key)
|
|
368
401
|
if key in self._cache:
|
|
369
402
|
return self._cache[key]
|
|
370
403
|
if self._messages is _Unknown or key >= len(self._messages):
|
|
@@ -447,6 +480,7 @@ class RepeatedMessage:
|
|
|
447
480
|
def _create_child(self, key):
|
|
448
481
|
if self._readOnly:
|
|
449
482
|
raise ValueError(f"{self._readOnly} is read only")
|
|
483
|
+
key = self._validate_key(key)
|
|
450
484
|
if self._messages is _Unknown:
|
|
451
485
|
self.__dict__['_messages'] = self._parent._create_child(self._key)
|
|
452
486
|
if key == append:
|
|
@@ -471,6 +505,7 @@ class RepeatedMessage:
|
|
|
471
505
|
def __setitem__(self, key, message):
|
|
472
506
|
if self._readOnly:
|
|
473
507
|
raise ValueError(f"{self._readOnly} is read only")
|
|
508
|
+
key = self._validate_key(key)
|
|
474
509
|
if self._messages is _Unknown:
|
|
475
510
|
self._messages = self._parent._create_child(self._key)
|
|
476
511
|
if key < 0:
|
|
@@ -499,6 +534,7 @@ class RepeatedMessage:
|
|
|
499
534
|
def __delitem__(self, key):
|
|
500
535
|
if self._readOnly:
|
|
501
536
|
raise ValueError(f"{self._readOnly} is read only")
|
|
537
|
+
key = self._validate_key(key)
|
|
502
538
|
if self._messages is not _Unknown:
|
|
503
539
|
del self._messages[key]
|
|
504
540
|
self._cache.pop(key, None)
|
|
@@ -514,13 +550,22 @@ class RepeatedMessage:
|
|
|
514
550
|
message = self._messages.append(message)
|
|
515
551
|
return self[len(self._messages) - 1]
|
|
516
552
|
|
|
553
|
+
def _validate_key(self, key):
|
|
554
|
+
if isinstance(key, FieldMessage):
|
|
555
|
+
key = key._value
|
|
556
|
+
elif isinstance(key, Value):
|
|
557
|
+
key = key._raw
|
|
558
|
+
if not isinstance(key, int):
|
|
559
|
+
raise TypeError(f"Unexpected key type: {key.__class__}")
|
|
560
|
+
return key
|
|
561
|
+
|
|
517
562
|
|
|
518
563
|
class FieldMessage:
|
|
519
564
|
def __init__(self, parent, key, kind, value):
|
|
520
|
-
self.
|
|
521
|
-
self.
|
|
522
|
-
self.
|
|
523
|
-
self.
|
|
565
|
+
self._parent = parent
|
|
566
|
+
self._key = key
|
|
567
|
+
self._kind = kind
|
|
568
|
+
self._value = value
|
|
524
569
|
|
|
525
570
|
def __bool__(self):
|
|
526
571
|
return bool(self._value)
|
|
@@ -539,7 +584,14 @@ class FieldMessage:
|
|
|
539
584
|
return self._value == other._value
|
|
540
585
|
return self._value == other
|
|
541
586
|
|
|
587
|
+
def __bytes__(self):
|
|
588
|
+
if isinstance(self._value, str):
|
|
589
|
+
return self._value.encode('utf-8')
|
|
590
|
+
return bytes(self._value)
|
|
591
|
+
|
|
542
592
|
def __str__(self):
|
|
593
|
+
if isinstance(self._value, bytes):
|
|
594
|
+
return self._value.decode('utf-8')
|
|
543
595
|
return str(self._value)
|
|
544
596
|
|
|
545
597
|
def __format__(self, spec=''):
|
|
@@ -576,16 +628,16 @@ class ProtobufValue:
|
|
|
576
628
|
|
|
577
629
|
class Value:
|
|
578
630
|
def __init__(self, parent, key, value=_Unknown, readOnly=None):
|
|
579
|
-
self.
|
|
580
|
-
self.
|
|
581
|
-
self.
|
|
582
|
-
self.
|
|
583
|
-
self.
|
|
584
|
-
self.
|
|
631
|
+
self._set_attribute('_parent', parent)
|
|
632
|
+
self._set_attribute('_key', key)
|
|
633
|
+
self._set_attribute('_dependencies', {})
|
|
634
|
+
self._set_attribute('_unknowns', {})
|
|
635
|
+
self._set_attribute('_cache', {})
|
|
636
|
+
self._set_attribute('_readOnly', None)
|
|
585
637
|
if isinstance(value, (google.protobuf.struct_pb2.Value, google.protobuf.struct_pb2.Struct, google.protobuf.struct_pb2.ListValue)) or value is _Unknown:
|
|
586
|
-
self.
|
|
638
|
+
self._set_attribute('_value', value)
|
|
587
639
|
else:
|
|
588
|
-
self.
|
|
640
|
+
self._set_attribute('_value', google.protobuf.struct_pb2.Value())
|
|
589
641
|
if value is None:
|
|
590
642
|
self._value.null_value = 0
|
|
591
643
|
elif isinstance(value, dict):
|
|
@@ -604,12 +656,16 @@ class Value:
|
|
|
604
656
|
self._value.string_value = value
|
|
605
657
|
else:
|
|
606
658
|
raise ValueError(f"Unexpected Value type: {value.__class__}")
|
|
607
|
-
self.
|
|
659
|
+
self._set_attribute('_readOnly', readOnly)
|
|
660
|
+
|
|
661
|
+
def _set_attribute(self, key, value):
|
|
662
|
+
self.__dict__[key] = value
|
|
608
663
|
|
|
609
664
|
def __getattr__(self, key):
|
|
610
665
|
return self[key]
|
|
611
666
|
|
|
612
667
|
def __getitem__(self, key):
|
|
668
|
+
key = self._validate_key(key)
|
|
613
669
|
if key in self._cache:
|
|
614
670
|
return self._cache[key]
|
|
615
671
|
if key in self._unknowns:
|
|
@@ -641,7 +697,7 @@ class Value:
|
|
|
641
697
|
case _:
|
|
642
698
|
raise ValueError(f"Invalid key \"{key}\" for kind: {self._kind}")
|
|
643
699
|
else:
|
|
644
|
-
raise
|
|
700
|
+
raise NotImplementedError()
|
|
645
701
|
value = Value(self, key, value, self._readOnly)
|
|
646
702
|
self._cache[key] = value
|
|
647
703
|
return value
|
|
@@ -860,6 +916,7 @@ class Value:
|
|
|
860
916
|
def __setitem__(self, key, value):
|
|
861
917
|
if self._readOnly:
|
|
862
918
|
raise ValueError(f"{self._readOnly} is read only")
|
|
919
|
+
key = self._validate_key(key)
|
|
863
920
|
if isinstance(key, str):
|
|
864
921
|
if self._ensure_map() == 'struct_value':
|
|
865
922
|
values = self._value.struct_value.fields
|
|
@@ -877,7 +934,7 @@ class Value:
|
|
|
877
934
|
while key >= len(values):
|
|
878
935
|
values.add()
|
|
879
936
|
else:
|
|
880
|
-
raise
|
|
937
|
+
raise NotImplementedError()
|
|
881
938
|
self._cache.pop(key, None)
|
|
882
939
|
self._dependencies.pop(key, None)
|
|
883
940
|
self._unknowns.pop(key, None)
|
|
@@ -887,6 +944,8 @@ class Value:
|
|
|
887
944
|
values[key].null_value = 0
|
|
888
945
|
elif isinstance(value, bool): # Must be before int check
|
|
889
946
|
values[key].bool_value = value
|
|
947
|
+
elif isinstance(value, bytes):
|
|
948
|
+
values[key].string_value = value._value.decode('utf-8')
|
|
890
949
|
elif isinstance(value, str):
|
|
891
950
|
values[key].string_value = value
|
|
892
951
|
elif isinstance(value, (int, float)):
|
|
@@ -995,6 +1054,7 @@ class Value:
|
|
|
995
1054
|
kind = self._kind
|
|
996
1055
|
if kind == 'Unknown':
|
|
997
1056
|
return
|
|
1057
|
+
key = self._validate_key(key)
|
|
998
1058
|
if isinstance(key, str):
|
|
999
1059
|
match kind:
|
|
1000
1060
|
case 'struct_value':
|
|
@@ -1036,11 +1096,12 @@ class Value:
|
|
|
1036
1096
|
break
|
|
1037
1097
|
del values[ix]
|
|
1038
1098
|
else:
|
|
1039
|
-
raise
|
|
1099
|
+
raise NotImplementedError()
|
|
1040
1100
|
|
|
1041
1101
|
def _create_child(self, key):
|
|
1042
1102
|
if self._readOnly:
|
|
1043
1103
|
raise ValueError(f"{self._readOnly} is read only")
|
|
1104
|
+
key = self._validate_key(key)
|
|
1044
1105
|
if isinstance(key, str):
|
|
1045
1106
|
if self._ensure_map() == 'struct_value':
|
|
1046
1107
|
fields = self._value.struct_value.fields
|
|
@@ -1061,7 +1122,16 @@ class Value:
|
|
|
1061
1122
|
values.add()
|
|
1062
1123
|
values[key].Clear()
|
|
1063
1124
|
return values[key]
|
|
1064
|
-
raise
|
|
1125
|
+
raise NotImplementedError()
|
|
1126
|
+
|
|
1127
|
+
def _validate_key(self, key):
|
|
1128
|
+
if isinstance(key, FieldMessage):
|
|
1129
|
+
key = key._value
|
|
1130
|
+
elif isinstance(key, Value):
|
|
1131
|
+
key = key._raw
|
|
1132
|
+
if not isinstance(key, (str, int)):
|
|
1133
|
+
raise TypeError(f"Unexpected key type: {key.__class__}")
|
|
1134
|
+
return key
|
|
1065
1135
|
|
|
1066
1136
|
def _ensure_map(self):
|
|
1067
1137
|
kind = self._kind
|
crossplane/pythonic/render.py
CHANGED
|
@@ -53,14 +53,6 @@ class Command(command.Command):
|
|
|
53
53
|
metavar='PATH',
|
|
54
54
|
help='A YAML file or directory of YAML files specifying the observed state of composed resources.'
|
|
55
55
|
)
|
|
56
|
-
parser.add_argument(
|
|
57
|
-
'--extra-resources',
|
|
58
|
-
action='append',
|
|
59
|
-
type=pathlib.Path,
|
|
60
|
-
default=[],
|
|
61
|
-
metavar='PATH',
|
|
62
|
-
help='A YAML file or directory of YAML files specifying required resources (deprecated, use --required-resources).',
|
|
63
|
-
)
|
|
64
56
|
parser.add_argument(
|
|
65
57
|
'--required-resources', '-e',
|
|
66
58
|
action='append',
|
|
@@ -70,18 +62,23 @@ class Command(command.Command):
|
|
|
70
62
|
help='A YAML file or directory of YAML files specifying required resources to pass to the Function pipeline.',
|
|
71
63
|
)
|
|
72
64
|
parser.add_argument(
|
|
73
|
-
'--
|
|
65
|
+
'--secret-store', '-s',
|
|
74
66
|
action='append',
|
|
75
67
|
type=pathlib.Path,
|
|
76
68
|
default=[],
|
|
77
69
|
metavar='PATH',
|
|
78
|
-
help='A YAML file or directory of YAML files specifying
|
|
70
|
+
help='A YAML file or directory of YAML files specifying Secrets to use to resolve connections and credentials.',
|
|
79
71
|
)
|
|
80
72
|
parser.add_argument(
|
|
81
73
|
'--include-full-xr', '-x',
|
|
82
74
|
action='store_true',
|
|
83
75
|
help="Include a direct copy of the input XR's spedc and metadata fields in the rendered output.",
|
|
84
76
|
)
|
|
77
|
+
parser.add_argument(
|
|
78
|
+
'--include-connection-xr',
|
|
79
|
+
action='store_true',
|
|
80
|
+
help="Include the Composite connection values in the rendered output as a resource of kind: Connection.",
|
|
81
|
+
)
|
|
85
82
|
parser.add_argument(
|
|
86
83
|
'--include-function-results', '-r',
|
|
87
84
|
action='store_true',
|
|
@@ -158,22 +155,26 @@ class Command(command.Command):
|
|
|
158
155
|
sys.exit(1)
|
|
159
156
|
request.context[key_value[0]] = protobuf.Yaml(key_value[1])
|
|
160
157
|
|
|
161
|
-
#
|
|
162
|
-
|
|
158
|
+
# Collect specified required/extra resources. Sort for stable order when processed.
|
|
159
|
+
requireds = sorted(
|
|
160
|
+
self.collect_resources(self.args.required_resources),
|
|
161
|
+
key=lambda required: str(resource.metadata.name),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Collect specified connection and credential secrets.
|
|
165
|
+
secrets = []
|
|
166
|
+
for secret in self.collect_resources(self.args.secret_store):
|
|
167
|
+
if secret.apiVersion == 'v1' and secret.kind == 'Secret':
|
|
168
|
+
secrets.append(secret)
|
|
169
|
+
|
|
170
|
+
# Establish the request observed composite.
|
|
171
|
+
self.setup_resource(composite, secrets, request.observed.composite)
|
|
172
|
+
|
|
173
|
+
# Establish the configured observed resources.
|
|
163
174
|
for resource in self.collect_resources(self.args.observed_resources):
|
|
164
175
|
name = resource.metadata.annotations['crossplane.io/composition-resource-name']
|
|
165
176
|
if name:
|
|
166
|
-
request.observed.resources[
|
|
167
|
-
|
|
168
|
-
# Collect specified required/extra resources.
|
|
169
|
-
requireds = [resource for resource in self.collect_resources(self.args.required_resources)]
|
|
170
|
-
requireds += [resource for resource in self.collect_resources(self.args.extra_resources)]
|
|
171
|
-
|
|
172
|
-
# Collect specified credential secrets.
|
|
173
|
-
credentials = []
|
|
174
|
-
for credential in self.collect_resources(self.args.function_credentials):
|
|
175
|
-
if credential.apiVersion == 'v1' and credential.kind == 'Secret':
|
|
176
|
-
credentials.append(credential)
|
|
177
|
+
self.setup_resource(resource, secrets, request.observed.resources[name])
|
|
177
178
|
|
|
178
179
|
# These will hold the response conditions and results.
|
|
179
180
|
conditions = protobuf.List()
|
|
@@ -194,18 +195,21 @@ class Command(command.Command):
|
|
|
194
195
|
|
|
195
196
|
# Supply step requested credentials.
|
|
196
197
|
request.credentials()
|
|
197
|
-
for
|
|
198
|
-
if
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
data
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
198
|
+
for credential in step.credentials:
|
|
199
|
+
if credential.source == 'Secret' and credential.secretRef:
|
|
200
|
+
namespace = credential.secretRef.namespace
|
|
201
|
+
name = credential.secretRef.name
|
|
202
|
+
if namespace and name:
|
|
203
|
+
for secret in secrets:
|
|
204
|
+
if secret.metadata.namespace == namespace and secret.metadata.name == name:
|
|
205
|
+
data = request.credentials[credential.name].credential_data.data
|
|
206
|
+
data()
|
|
207
|
+
for key, value in secret.data:
|
|
208
|
+
data[key] = protobuf.B64Decode(value)
|
|
209
|
+
break
|
|
210
|
+
else:
|
|
211
|
+
print(f"Step \"{step.step}\" secret not found: {namespace}/{name}", file=sys.stderr)
|
|
212
|
+
sys.exit(1)
|
|
209
213
|
|
|
210
214
|
# Track what extra/required resources have been processed.
|
|
211
215
|
requirements = protobuf.Message(None, 'requirements', fnv1.Requirements.DESCRIPTOR, fnv1.Requirements())
|
|
@@ -213,14 +217,14 @@ class Command(command.Command):
|
|
|
213
217
|
# Fetch the step bootstrap resources specified.
|
|
214
218
|
request.required_resources()
|
|
215
219
|
for requirement in step.requirements:
|
|
216
|
-
self.fetch_requireds(requireds, requirement.requirementName, requirement, request.required_resources)
|
|
220
|
+
self.fetch_requireds(requireds, secrets, requirement.requirementName, requirement, request.required_resources)
|
|
217
221
|
# Fetch the required resources requested.
|
|
218
222
|
for name, selector in requirements.resources:
|
|
219
|
-
self.fetch_requireds(requireds, name, selector, request.required_resources)
|
|
223
|
+
self.fetch_requireds(requireds, secrets, name, selector, request.required_resources)
|
|
220
224
|
# Fetch the now deprecated extra resources requested.
|
|
221
225
|
request.extra_resources()
|
|
222
226
|
for name, selector in requirements.extra_resources:
|
|
223
|
-
self.fetch_requireds(requireds, name, selector, request.extra_resources)
|
|
227
|
+
self.fetch_requireds(requireds, secrets, name, selector, request.extra_resources)
|
|
224
228
|
# Run the step using the function-pythonic function runner.
|
|
225
229
|
response = protobuf.Message(
|
|
226
230
|
None,
|
|
@@ -330,15 +334,29 @@ class Command(command.Command):
|
|
|
330
334
|
# Print the composite.
|
|
331
335
|
print('---')
|
|
332
336
|
print(str(composite), end='')
|
|
337
|
+
|
|
338
|
+
# Print Composite connection if requested.
|
|
339
|
+
if self.args.include_connection_xr:
|
|
340
|
+
connection = protobuf.Map(
|
|
341
|
+
apiVersion = 'render.crossplane.io/v1beta1',
|
|
342
|
+
kind = 'Connection',
|
|
343
|
+
)
|
|
344
|
+
for key, value in request.desired.composite.connection_details:
|
|
345
|
+
connection.values[key] = value
|
|
346
|
+
print('---')
|
|
347
|
+
print(str(connection), end='')
|
|
348
|
+
|
|
333
349
|
# Print the composed resources.
|
|
334
350
|
for resource in sorted(resources, key=lambda resource: str(resource.metadata.annotations['crossplane.io/composition-resource-name'])):
|
|
335
351
|
print('---')
|
|
336
352
|
print(str(resource), end='')
|
|
353
|
+
|
|
337
354
|
# Print the results (AKA events) if requested.
|
|
338
355
|
if self.args.include_function_results:
|
|
339
356
|
for result in results:
|
|
340
357
|
print('---')
|
|
341
358
|
print(str(result), end='')
|
|
359
|
+
|
|
342
360
|
# Print the final context if requested.
|
|
343
361
|
if self.args.include_context:
|
|
344
362
|
print('---')
|
|
@@ -346,7 +364,7 @@ class Command(command.Command):
|
|
|
346
364
|
str(protobuf.Map(
|
|
347
365
|
apiVersion = 'render.crossplane.io/v1beta1',
|
|
348
366
|
kind = 'Context',
|
|
349
|
-
|
|
367
|
+
values = request.context,
|
|
350
368
|
)),
|
|
351
369
|
end='',
|
|
352
370
|
)
|
|
@@ -382,7 +400,19 @@ class Command(command.Command):
|
|
|
382
400
|
for document in yaml.safe_load_all(file.read_text()):
|
|
383
401
|
yield protobuf.Value(None, None, document)
|
|
384
402
|
|
|
385
|
-
def
|
|
403
|
+
def setup_resource(self, source, secrets, resource):
|
|
404
|
+
resource.resource = source
|
|
405
|
+
namespace = source.spec.writeConnectionSecretToRef.namespace or source.metadata.namespace
|
|
406
|
+
name = source.spec.writeConnectionSecretToRef.name
|
|
407
|
+
if namespace and name:
|
|
408
|
+
for secret in secrets:
|
|
409
|
+
if secret.metadata.namespace == namespace and secret.metadata.name == name:
|
|
410
|
+
resource.connection_details()
|
|
411
|
+
for key, value in secret.data:
|
|
412
|
+
resource.connection_details[key] = protobuf.B64Decode(value)
|
|
413
|
+
break
|
|
414
|
+
|
|
415
|
+
def fetch_requireds(self, requireds, secrets, name, selector, resources):
|
|
386
416
|
if not name:
|
|
387
417
|
return
|
|
388
418
|
name = str(name)
|
|
@@ -391,13 +421,13 @@ class Command(command.Command):
|
|
|
391
421
|
for required in requireds:
|
|
392
422
|
if selector.api_version == required.apiVersion and selector.kind == required.kind:
|
|
393
423
|
if selector.match_name == required.metadata.name:
|
|
394
|
-
items[protobuf.append]
|
|
424
|
+
self.setup_resource(required, secrets, items[protobuf.append])
|
|
395
425
|
elif selector.match_labels.labels:
|
|
396
426
|
for key, value in selector.match_labels.labels:
|
|
397
427
|
if value != required.metadata.labels[key]:
|
|
398
428
|
break
|
|
399
429
|
else:
|
|
400
|
-
items[protobuf.append]
|
|
430
|
+
self.setup_resource(required, secrets, items[protobuf.append])
|
|
401
431
|
|
|
402
432
|
def copy_resource(self, source, destination):
|
|
403
433
|
destination.resource = source.resource
|
crossplane/pythonic/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crossplane-function-pythonic
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A Python centric Crossplane Function
|
|
5
5
|
Project-URL: Documentation, https://github.com/crossplane-contrib/function-pythonic#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
|
|
@@ -81,7 +81,7 @@ kind: Function
|
|
|
81
81
|
metadata:
|
|
82
82
|
name: function-pythonic
|
|
83
83
|
spec:
|
|
84
|
-
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.2.
|
|
84
|
+
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.2.1
|
|
85
85
|
```
|
|
86
86
|
## Composed Resource Dependencies
|
|
87
87
|
|
|
@@ -228,8 +228,9 @@ The BaseComposite class provides the following fields for manipulating the Compo
|
|
|
228
228
|
| self.status | Map | The composite desired and observed status, read from observed if not in desired |
|
|
229
229
|
| self.conditions | Conditions | The composite desired and observed conditions, read from observed if not in desired |
|
|
230
230
|
| self.results | Results | Returned results applied to the Composite and optionally on the Claim |
|
|
231
|
-
| self.connection |
|
|
231
|
+
| self.connection | Map | The composite desired connection detials |
|
|
232
232
|
| self.ready | Boolean | The composite desired ready state |
|
|
233
|
+
| self.observed.connection | Map | The composite observed connection detials |
|
|
233
234
|
|
|
234
235
|
The BaseComposite also provides access to the following Crossplane Function level features:
|
|
235
236
|
|
|
@@ -270,7 +271,7 @@ Resource class:
|
|
|
270
271
|
| Resource.data | Map | The resource data |
|
|
271
272
|
| Resource.status | Map | The resource status |
|
|
272
273
|
| Resource.conditions | Conditions | The resource conditions |
|
|
273
|
-
| Resource.connection |
|
|
274
|
+
| Resource.connection | Map | The resource observed connection details |
|
|
274
275
|
| Resource.ready | Boolean | The resource ready state |
|
|
275
276
|
| Resource.unknownsFatal | Boolean | Terminate the composition if this resource has been created and is assigned unknown values, default is Composite.unknownsFatal |
|
|
276
277
|
| Resource.usages | Boolean | Generate Crossplane Usages for this resource, default is Composite.autoReady |
|
|
@@ -310,6 +311,7 @@ Each resource in the list is the following RequiredResource class:
|
|
|
310
311
|
| RequiredResource.data | Map | The required resource data |
|
|
311
312
|
| RequiredResource.status | Map | The required resource status |
|
|
312
313
|
| RequiredResource.conditions | Map | The required resource conditions |
|
|
314
|
+
| RequiredResource.connection | Map | The required resource connection details |
|
|
313
315
|
|
|
314
316
|
### Conditions
|
|
315
317
|
|
|
@@ -372,11 +374,11 @@ $ pip install crossplane-function-pythonic
|
|
|
372
374
|
Then to render function-pythonic Compositions, use the `function-pythonic render ...`
|
|
373
375
|
command.
|
|
374
376
|
```shell
|
|
375
|
-
$ function-pythonic render
|
|
377
|
+
$ function-pythonic render -h
|
|
376
378
|
usage: Crossplane Function Pythonic render [-h] [--debug] [--log-name-width WIDTH] [--python-path DIRECTORY] [--render-unknowns]
|
|
377
379
|
[--allow-oversize-protos] [--context-files KEY=PATH] [--context-values KEY=VALUE]
|
|
378
|
-
[--observed-resources PATH] [--
|
|
379
|
-
[--
|
|
380
|
+
[--observed-resources PATH] [--required-resources PATH] [--secret-store PATH] [--include-full-xr]
|
|
381
|
+
[--include-connection-xr] [--include-function-results] [--include-context]
|
|
380
382
|
PATH [PATH/CLASS]
|
|
381
383
|
|
|
382
384
|
positional arguments:
|
|
@@ -400,14 +402,14 @@ options:
|
|
|
400
402
|
Context key-value pairs to pass to the Function pipeline. Values must be YAML/JSON. Keys take precedence over --context-files.
|
|
401
403
|
--observed-resources, -o PATH
|
|
402
404
|
A YAML file or directory of YAML files specifying the observed state of composed resources.
|
|
403
|
-
--extra-resources PATH
|
|
404
|
-
A YAML file or directory of YAML files specifying required resources (deprecated, use --required-resources).
|
|
405
405
|
--required-resources, -e PATH
|
|
406
406
|
A YAML file or directory of YAML files specifying required resources to pass to the Function pipeline.
|
|
407
|
-
--
|
|
408
|
-
A YAML file or directory of YAML files specifying
|
|
407
|
+
--secret-store, -s PATH
|
|
408
|
+
A YAML file or directory of YAML files specifying Secrets to use to resolve connections and credentials.
|
|
409
409
|
--include-full-xr, -x
|
|
410
410
|
Include a direct copy of the input XR's spedc and metadata fields in the rendered output.
|
|
411
|
+
--include-connection-xr
|
|
412
|
+
Include the Composite connection values in the rendered output as a resource of kind: Connection.
|
|
411
413
|
--include-function-results, -r
|
|
412
414
|
Include informational and warning messages from Functions in the rendered output as resources of kind: Result..
|
|
413
415
|
--include-context, -c
|
|
@@ -545,7 +547,7 @@ kind: Function
|
|
|
545
547
|
metadata:
|
|
546
548
|
name: function-pythonic
|
|
547
549
|
spec:
|
|
548
|
-
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.2.
|
|
550
|
+
package: xpkg.upbound.io/crossplane-contrib/function-pythonic:v0.2.1
|
|
549
551
|
runtimeConfigRef:
|
|
550
552
|
name: function-pythonic
|
|
551
553
|
---
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
crossplane/pythonic/__about__.py,sha256=QBcrW1XnbdqF5kVtJfA-yvTFAU-t00iGaaoo6kjjIDY,73
|
|
2
|
+
crossplane/pythonic/__init__.py,sha256=A9U4-azc4DjSsOnOnjQxCkoTzsZMRBb_AvqzR_Bd95A,268
|
|
3
|
+
crossplane/pythonic/__main__.py,sha256=6vYRlYDJtqFgLyiTamnl3htiNOtz8QlDl5WlIP98I8o,31
|
|
4
|
+
crossplane/pythonic/command.py,sha256=s69oOVCgbm39gowvTy7ieaE-Vosckf24vcznJ6fO0Q4,3146
|
|
5
|
+
crossplane/pythonic/composite.py,sha256=PJD3uRbanrkUgCUxnN_lKhg5n3QO1OpGfLC71UkEwRg,22129
|
|
6
|
+
crossplane/pythonic/function.py,sha256=ShHhSJYblTB9BRx7NEtHgiyOconax7Lro0tLzzRyZdY,16564
|
|
7
|
+
crossplane/pythonic/grpc.py,sha256=yc-hVnw7K0iuuJS25z6aZkfXjUuvciWfGCdHnWv4I78,4443
|
|
8
|
+
crossplane/pythonic/main.py,sha256=ujUa_FYElQSGqnhZ-0NJrD3kSyYjfRbIp79FV2Yl7hs,599
|
|
9
|
+
crossplane/pythonic/packages.py,sha256=4TxyT6V79R0m4tJbC8R1gwU_vgHGLXKSBzeTTKd8xGo,5120
|
|
10
|
+
crossplane/pythonic/protobuf.py,sha256=Wha3hMSDbJPALtMu-ZcZEHaV5CosfYW8SJh7ueAbqVk,50528
|
|
11
|
+
crossplane/pythonic/render.py,sha256=yqqk9DambTBRgQ51ZEQWOLg1xvos1KGETHei6rimus8,22238
|
|
12
|
+
crossplane/pythonic/version.py,sha256=-RiB0p146ayqJj0SXfYxTNv49u9Fx9pPgm59Ji2blhc,214
|
|
13
|
+
crossplane_function_pythonic-0.2.1.dist-info/METADATA,sha256=pIsTA_rzQ_HBpVlYZZI67AmHz27qcqAfxYoY5j1iNW0,28659
|
|
14
|
+
crossplane_function_pythonic-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
+
crossplane_function_pythonic-0.2.1.dist-info/entry_points.txt,sha256=jJ4baywFDviB9WyAhyhNYF2VOCb6XtbRSjKf7bnBwhg,68
|
|
16
|
+
crossplane_function_pythonic-0.2.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
+
crossplane_function_pythonic-0.2.1.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
crossplane/pythonic/__about__.py,sha256=gXUN1UoV7TWafLuqL_UsLD_UlnDY6JmtTV7bvwphX6Q,73
|
|
2
|
-
crossplane/pythonic/__init__.py,sha256=A9U4-azc4DjSsOnOnjQxCkoTzsZMRBb_AvqzR_Bd95A,268
|
|
3
|
-
crossplane/pythonic/__main__.py,sha256=6vYRlYDJtqFgLyiTamnl3htiNOtz8QlDl5WlIP98I8o,31
|
|
4
|
-
crossplane/pythonic/command.py,sha256=s69oOVCgbm39gowvTy7ieaE-Vosckf24vcznJ6fO0Q4,3146
|
|
5
|
-
crossplane/pythonic/composite.py,sha256=SVgyhyg4MDNm_rGSrHUi81bd9Zqgnv3zaMKnaCMKB2s,21967
|
|
6
|
-
crossplane/pythonic/function.py,sha256=ShHhSJYblTB9BRx7NEtHgiyOconax7Lro0tLzzRyZdY,16564
|
|
7
|
-
crossplane/pythonic/grpc.py,sha256=Ts_-JywIr4Xzjjf_hr2uOlPK0O8A1SNN6ZaEjRckjL8,4318
|
|
8
|
-
crossplane/pythonic/main.py,sha256=ujUa_FYElQSGqnhZ-0NJrD3kSyYjfRbIp79FV2Yl7hs,599
|
|
9
|
-
crossplane/pythonic/packages.py,sha256=4TxyT6V79R0m4tJbC8R1gwU_vgHGLXKSBzeTTKd8xGo,5120
|
|
10
|
-
crossplane/pythonic/protobuf.py,sha256=Wf2VJteKR_0S8l96JKLoOI78yrcH258DVj03re_epFI,48163
|
|
11
|
-
crossplane/pythonic/render.py,sha256=4mq-slOSGoGbSPJiioeepWbdImSyVbas_hIEnJO_p5s,21069
|
|
12
|
-
crossplane/pythonic/version.py,sha256=jUi8Wk7malaNDdW3FemkcsqlSGygQCrRFvgWB3Sve8M,214
|
|
13
|
-
crossplane_function_pythonic-0.2.0.dist-info/METADATA,sha256=w5BMWBdP1wvuPR-zeE28frxeBi-S8f9UfDE2gcj2OHk,28576
|
|
14
|
-
crossplane_function_pythonic-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
-
crossplane_function_pythonic-0.2.0.dist-info/entry_points.txt,sha256=jJ4baywFDviB9WyAhyhNYF2VOCb6XtbRSjKf7bnBwhg,68
|
|
16
|
-
crossplane_function_pythonic-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
-
crossplane_function_pythonic-0.2.0.dist-info/RECORD,,
|
{crossplane_function_pythonic-0.2.0.dist-info → crossplane_function_pythonic-0.2.1.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|