crossplane-function-pythonic 0.2.0__py3-none-any.whl → 0.3.0__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.
@@ -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,13 +81,14 @@ 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
83
88
 
84
89
  async def run(self):
85
90
  grpc.aio.init_grpc_aio()
86
- grpc_runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns)
91
+ grpc_runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns, self.args.crossplane_v1)
87
92
  grpc_server = grpc.aio.server()
88
93
  grpcv1.add_FunctionRunnerServiceServicer_to_server(grpc_runner, grpc_server)
89
94
  if self.args.insecure:
@@ -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.__dict__['_parent'] = parent
62
- self.__dict__['_key'] = key
63
- self.__dict__['_descriptor'] = descriptor
64
- self.__dict__['_message'] = message
65
- self.__dict__['_readOnly'] = readOnly
66
- self.__dict__['_cache'] = {}
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.__dict__['_parent'] = parent
214
- self.__dict__['_key'] = key
215
- self.__dict__['_field'] = field
216
- self.__dict__['_messages'] = messages
217
- self.__dict__['_readOnly'] = readOnly
218
- self.__dict__['_cache'] = {}
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,41 +550,75 @@ 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.__dict__['_parent'] = parent
521
- self.__dict__['_key'] = key
522
- self.__dict__['_kind'] = kind
523
- self.__dict__['_value'] = value
565
+ self._parent = parent
566
+ self._key = key
567
+ self._kind = kind
568
+ self._value = value
524
569
 
525
570
  def __bool__(self):
526
- return bool(self._value)
571
+ return self._value is not _Unknown
527
572
 
528
573
  def __len__(self):
574
+ if self._value is _Unknown:
575
+ return 0
529
576
  return len(self._value)
530
577
 
531
578
  def __contains__(self, key):
579
+ if self._value is _Unknown:
580
+ return False
532
581
  return key in self._value
533
582
 
534
583
  def __hash__(self):
584
+ if self._value is _Unknown:
585
+ return 0
535
586
  return hash(self._value)
536
587
 
537
588
  def __eq__(self, other):
589
+ if self._value is _Unknown:
590
+ return False
538
591
  if isinstance(other, FieldMessage):
539
592
  return self._value == other._value
540
593
  return self._value == other
541
594
 
595
+ def __bytes__(self):
596
+ if self._value is _Unknown:
597
+ return None
598
+ if isinstance(self._value, str):
599
+ return self._value.encode('utf-8')
600
+ return bytes(self._value)
601
+
542
602
  def __str__(self):
603
+ if self._value is _Unknown:
604
+ return None
605
+ if isinstance(self._value, bytes):
606
+ return self._value.decode('utf-8')
543
607
  return str(self._value)
544
608
 
545
609
  def __format__(self, spec=''):
610
+ if self._value is _Unknown:
611
+ return None
546
612
  return format(self._value, spec)
547
613
 
548
614
  def __int__(self):
615
+ if self._value is _Unknown:
616
+ return None
549
617
  return int(self._value)
550
618
 
551
619
  def __float__(self):
620
+ if self._value is _Unknown:
621
+ return None
552
622
  return float(self._value)
553
623
 
554
624
  def _fullName(self, key=None):
@@ -576,16 +646,16 @@ class ProtobufValue:
576
646
 
577
647
  class Value:
578
648
  def __init__(self, parent, key, value=_Unknown, readOnly=None):
579
- self.__dict__['_parent'] = parent
580
- self.__dict__['_key'] = key
581
- self.__dict__['_dependencies'] = {}
582
- self.__dict__['_unknowns'] = {}
583
- self.__dict__['_cache'] = {}
584
- self.__dict__['_readOnly'] = None
649
+ self._set_attribute('_parent', parent)
650
+ self._set_attribute('_key', key)
651
+ self._set_attribute('_dependencies', {})
652
+ self._set_attribute('_unknowns', {})
653
+ self._set_attribute('_cache', {})
654
+ self._set_attribute('_readOnly', None)
585
655
  if isinstance(value, (google.protobuf.struct_pb2.Value, google.protobuf.struct_pb2.Struct, google.protobuf.struct_pb2.ListValue)) or value is _Unknown:
586
- self.__dict__['_value'] = value
656
+ self._set_attribute('_value', value)
587
657
  else:
588
- self.__dict__['_value'] = google.protobuf.struct_pb2.Value()
658
+ self._set_attribute('_value', google.protobuf.struct_pb2.Value())
589
659
  if value is None:
590
660
  self._value.null_value = 0
591
661
  elif isinstance(value, dict):
@@ -604,12 +674,16 @@ class Value:
604
674
  self._value.string_value = value
605
675
  else:
606
676
  raise ValueError(f"Unexpected Value type: {value.__class__}")
607
- self.__dict__['_readOnly'] = readOnly
677
+ self._set_attribute('_readOnly', readOnly)
678
+
679
+ def _set_attribute(self, key, value):
680
+ self.__dict__[key] = value
608
681
 
609
682
  def __getattr__(self, key):
610
683
  return self[key]
611
684
 
612
685
  def __getitem__(self, key):
686
+ key = self._validate_key(key)
613
687
  if key in self._cache:
614
688
  return self._cache[key]
615
689
  if key in self._unknowns:
@@ -641,7 +715,7 @@ class Value:
641
715
  case _:
642
716
  raise ValueError(f"Invalid key \"{key}\" for kind: {self._kind}")
643
717
  else:
644
- raise ValueError(f"Unexpected key type: {key.__class__}")
718
+ raise NotImplementedError()
645
719
  value = Value(self, key, value, self._readOnly)
646
720
  self._cache[key] = value
647
721
  return value
@@ -659,6 +733,10 @@ class Value:
659
733
  return len(self._value.list_value.values) + len(self._unknowns)
660
734
  case 'ListValue':
661
735
  return len(self._value.values) + len(self._unknowns)
736
+ case 'string_value':
737
+ return len(self._value.string_value)
738
+ case 'bool_value':
739
+ return 1 if self._value.bool_value else 0
662
740
  return 0
663
741
 
664
742
  def __contains__(self, item):
@@ -860,6 +938,7 @@ class Value:
860
938
  def __setitem__(self, key, value):
861
939
  if self._readOnly:
862
940
  raise ValueError(f"{self._readOnly} is read only")
941
+ key = self._validate_key(key)
863
942
  if isinstance(key, str):
864
943
  if self._ensure_map() == 'struct_value':
865
944
  values = self._value.struct_value.fields
@@ -877,7 +956,7 @@ class Value:
877
956
  while key >= len(values):
878
957
  values.add()
879
958
  else:
880
- raise ValueError('Unexpected key type')
959
+ raise NotImplementedError()
881
960
  self._cache.pop(key, None)
882
961
  self._dependencies.pop(key, None)
883
962
  self._unknowns.pop(key, None)
@@ -887,6 +966,8 @@ class Value:
887
966
  values[key].null_value = 0
888
967
  elif isinstance(value, bool): # Must be before int check
889
968
  values[key].bool_value = value
969
+ elif isinstance(value, bytes):
970
+ values[key].string_value = value._value.decode('utf-8')
890
971
  elif isinstance(value, str):
891
972
  values[key].string_value = value
892
973
  elif isinstance(value, (int, float)):
@@ -995,6 +1076,7 @@ class Value:
995
1076
  kind = self._kind
996
1077
  if kind == 'Unknown':
997
1078
  return
1079
+ key = self._validate_key(key)
998
1080
  if isinstance(key, str):
999
1081
  match kind:
1000
1082
  case 'struct_value':
@@ -1036,11 +1118,12 @@ class Value:
1036
1118
  break
1037
1119
  del values[ix]
1038
1120
  else:
1039
- raise ValueError('Unexpected key type')
1121
+ raise NotImplementedError()
1040
1122
 
1041
1123
  def _create_child(self, key):
1042
1124
  if self._readOnly:
1043
1125
  raise ValueError(f"{self._readOnly} is read only")
1126
+ key = self._validate_key(key)
1044
1127
  if isinstance(key, str):
1045
1128
  if self._ensure_map() == 'struct_value':
1046
1129
  fields = self._value.struct_value.fields
@@ -1061,7 +1144,16 @@ class Value:
1061
1144
  values.add()
1062
1145
  values[key].Clear()
1063
1146
  return values[key]
1064
- raise ValueError('Unexpected key type')
1147
+ raise NotImplementedError()
1148
+
1149
+ def _validate_key(self, key):
1150
+ if isinstance(key, FieldMessage):
1151
+ key = key._value
1152
+ elif isinstance(key, Value):
1153
+ key = key._raw
1154
+ if not isinstance(key, (str, int)):
1155
+ raise TypeError(f"Unexpected key type: {key.__class__}")
1156
+ return key
1065
1157
 
1066
1158
  def _ensure_map(self):
1067
1159
  kind = self._kind
@@ -1154,13 +1246,14 @@ class Value:
1154
1246
  for key, value in self:
1155
1247
  if isinstance(value, Value) and len(value):
1156
1248
  patch = patches[key]
1157
- if isinstance(patch, Value) and patch._type == value._type and len(patch):
1249
+ print(patch.__class__, str(patch))
1250
+ if isinstance(patch, Value) and patch._kind == value._kind and len(patch):
1158
1251
  value._patchUnknowns(patch)
1159
1252
  elif self._isList:
1160
1253
  for ix, value in enumerate(self):
1161
1254
  if isinstance(value, Value) and len(value):
1162
1255
  patch = patches[ix]
1163
- if isinstance(patch, Value) and patch._type == value._type and len(patch):
1256
+ if isinstance(patch, Value) and patch._kind == value._kind and len(patch):
1164
1257
  value._patchUnknowns(patch)
1165
1258
 
1166
1259
  def _renderUnknowns(self, trimFullName):
@@ -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
- '--function-credentials',
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 credentials to use for Functions to render the XR.',
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,29 +155,33 @@ class Command(command.Command):
158
155
  sys.exit(1)
159
156
  request.context[key_value[0]] = protobuf.Yaml(key_value[1])
160
157
 
161
- # Establish the request observed composite and specifed observed resources.
162
- request.observed.composite.resource = composite
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(required.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[str(name)].resource = resource
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()
180
181
  results = protobuf.List()
181
182
 
182
183
  # Create a function-pythonic function runner used to run pipeline steps.
183
- runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns)
184
+ runner = function.FunctionRunner(self.args.debug, self.args.render_unknowns, self.args.crossplane_v1)
184
185
  fatal = False
185
186
 
186
187
  # Process the composition pipeline steps.
@@ -194,18 +195,21 @@ class Command(command.Command):
194
195
 
195
196
  # Supply step requested credentials.
196
197
  request.credentials()
197
- for fn_credential in step.credentials:
198
- if fn_credential.source == 'Secret' and fn_credential.secretRef:
199
- for credential in credentials:
200
- if credential.metadata.namespace == fn_credential.secretRef.namespace and credential.metadata.name == fn_credential.secretRef.name:
201
- data = request.credentials[str(fn_credential.name)].credential_data.data
202
- data()
203
- for key, value in credential.data:
204
- data[key] = protobuf.B64Decode(value)
205
- break
206
- else:
207
- print(f"Step \"{step.step}\" secret not found: {fn_credential.secretRef.namespace} {fn_credential.secretRef.name}", file=sys.stderr)
208
- sys.exit(1)
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
- fields = request.context,
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 fetch_requireds(self, requireds, name, selector, resources):
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].resource = required
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].resource = required
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
@@ -1,7 +1,7 @@
1
1
 
2
2
  from . import (
3
- command,
4
3
  __about__,
4
+ command,
5
5
  )
6
6
 
7
7