Skip to content

Commit aa41a39

Browse files
renovate[bot]iciclespider
authored andcommitted
Integrate render command with an active cluster
1 parent 4f53ce9 commit aa41a39

File tree

12 files changed

+596
-281
lines changed

12 files changed

+596
-281
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ The BaseComposite class provides the following fields for manipulating the Compo
236236
| self.conditions | Conditions | The composite desired and observed conditions, read from observed if not in desired |
237237
| self.results | Results | Returned results applied to the Composite and optionally on the Claim |
238238
| self.connectionSecret | Map | The name, namespace, and resourceName to use when generating the connection secret in Crossplane v2 |
239-
| self.connection | Map | The composite desired connection detials |
240-
| self.connection.observed | Map | The composite observed connection detials |
239+
| self.connection | Map | The composite desired connection details |
240+
| self.connection.observed | Map | The composite observed connection details |
241241
| self.ready | Boolean | The composite desired ready state |
242242

243243
The BaseComposite also provides access to the following Crossplane Function level features:
@@ -254,9 +254,9 @@ The BaseComposite also provides access to the following Crossplane Function leve
254254
| self.environment | Map | The response environment, initialized from the request context environment |
255255
| self.requireds | Requireds | Request and read additional local Kubernetes resources |
256256
| self.resources | Resources | Define and process composed resources |
257-
| self.unknownsFatal | Boolean | Terminate the composition if already created resources are assigned unknown values, default True |
258257
| self.usages| Boolean | Generate Crossplane Usages for resource dependencies, default False |
259258
| self.autoReady | Boolean | Perform auto ready processing on all composed resources, default True |
259+
| self.unknownsFatal | Boolean | Terminate the composition if already created resources are assigned unknown values, default False |
260260

261261
### Composed Resources
262262

@@ -281,9 +281,10 @@ Resource class:
281281
| Resource.conditions | Conditions | The resource conditions |
282282
| Resource.connection | Map | The resource observed connection details |
283283
| Resource.ready | Boolean | The resource ready state |
284-
| Resource.unknownsFatal | Boolean | Terminate the composition if this resource has been created and is assigned unknown values, default is Composite.unknownsFatal |
284+
| Resource.setReadyCondition | Method | Set Resource.ready to the Ready Condition status |
285285
| Resource.usages | Boolean | Generate Crossplane Usages for this resource, default is Composite.autoReady |
286286
| Resource.autoReady | Boolean | Perform auto ready processing on this resource, default is Composite.autoReady |
287+
| Resource.unknownsFatal | Boolean | Terminate the composition if this resource has been created and is assigned unknown values, default is Composite.unknownsFatal |
287288

288289
### Required Resources
289290

crossplane/pythonic/auto_ready.py

Lines changed: 96 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
11

22

3-
def process(composite):
4-
for name, resource in composite.resources:
5-
if resource.observed:
6-
if resource.autoReady or (resource.autoReady is None and composite.autoReady):
7-
if resource.ready is None:
8-
if _checks.get((resource.apiVersion, resource.kind), _check_default).ready(resource):
9-
resource.ready = True
3+
#def process_compsite(composite):
4+
# for name, resource in composite.resources:
5+
# resource.ready
6+
# if resource.autoReady or (resource.autoReady is None and composite.autoReady):
7+
# if resource.observed:
8+
# if resource.ready is None:
9+
# if _checks.get((resource.observed.apiVersion, resource.observed.kind), _check_default).ready(resource):
10+
# resource.ready = True
1011

1112

12-
class ConditionReady:
13+
def resource_ready(resource):
14+
if not resource.observed:
15+
return None
16+
return _checks.get((resource.observed.apiVersion, resource.observed.kind), _check_default).ready(resource)
17+
18+
19+
class ReadyCondition:
1320
def ready(self, resource):
14-
return bool(resource.conditions.Ready.status)
21+
ready = resource.conditions.Ready
22+
if not ready._find_condition():
23+
return None
24+
if ready.status:
25+
return resource.observed.metadata.name
26+
if ready.reason:
27+
return resource.status.notReadyCondition[ready.reason]
28+
return resource.status.notReadyCondition
1529

1630
_checks = {}
17-
_check_default = ConditionReady()
31+
_check_default = ReadyCondition()
1832

1933
class Check:
2034
@classmethod
@@ -28,7 +42,7 @@ def ready(self, resource):
2842

2943
class AlwaysReady(Check):
3044
def ready(self, resource):
31-
return True
45+
return resource.observed.metadata.name
3246

3347

3448
class ClusterRole(AlwaysReady):
@@ -44,85 +58,116 @@ class CronJob(Check):
4458
apiVersion = 'batch/v1'
4559
def ready(self, resource):
4660
if resource.observed.spec.suspend and len(resource.observed.spec.suspend):
47-
return True
61+
return resource.observed.metadata.name
4862
if not resource.status.lastScheduleTime:
49-
return False
63+
return resource.status.lastScheduleTime
5064
if resource.status.active:
51-
return True
65+
return resource.observed.metadata.name
5266
if not resource.status.lastSuccessfulTime:
53-
return False
54-
return str(resource.status.lastSuccessfulTime) >= str(resource.status.lastScheduleTime)
67+
return resource.status.lastSuccessfulTime
68+
if str(resource.status.lastSuccessfulTime) < str(resource.status.lastScheduleTime):
69+
return resource.status.successfulBeforeSchedule
70+
return resource.observed.metadata.name
5571

5672
class DaemonSet(Check):
5773
apiVersion = 'apps/v1'
5874
def ready(self, resource):
59-
if not resource.status.desiredNumberScheduled:
60-
return False
6175
scheduled = resource.status.desiredNumberScheduled
62-
return (scheduled == resource.status.numberReady and
63-
scheduled == resource.status.updatedNumberScheduled and
64-
scheduled == resource.status.numberAvailable
65-
)
76+
if not scheduled:
77+
return scheduled
78+
for field in ('numberReady', 'updatedNumberScheduled', 'numberAvailable'):
79+
value = resource.status[field]
80+
if not value:
81+
return value
82+
if scheduled != value:
83+
return resource.status[F"{field}NotDesired"]
84+
return resource.observed.metadata.name
6685

6786
class Deployment(Check):
6887
apiVersion = 'apps/v1'
6988
def ready(self, resource):
7089
replicas = resource.observed.spec.replicas or 1
71-
if resource.status.updatedReplicas != replicas or resource.status.availableReplicas != replicas:
72-
return False
73-
return bool(resource.conditions.Available.status)
90+
for field in ('updatedReplicas', 'availableReplicas'):
91+
value = resource.status[field]
92+
if not value:
93+
return value
94+
if replicas != value:
95+
return resource.status[F"{field}NotReplicas"]
96+
available = resource.conditions.Available
97+
if not available:
98+
return resource.status.notAvailable
99+
if not available.status:
100+
if available.reason:
101+
return resource.status.notAvailable[available.reason]
102+
return resource.status.notAvailable
103+
return resource.observed.metadata.name
74104

75105
class HorizontalPodAutoscaler(Check):
76106
apiVersion = 'autoscaling/v2'
77107
def ready(self, resource):
78108
for type in ('FailedGetScale', 'FailedUpdateScale', 'FailedGetResourceMetric', 'InvalidSelector'):
79109
if resource.conditions[type].status:
80-
return False
110+
return resource.status[f"is{type}"]
81111
for type in ('ScalingActive', 'ScalingLimited'):
82112
if resource.conditions[type].status:
83-
return True
84-
return False
113+
return resource.observed.metadata.name
114+
return resource.status.notScalingActiveOrLimiited
85115

86116
class Ingress(Check):
87117
apiVersion = 'networking.k8s.io/v1'
88118
def ready(self, resource):
89-
return len(resource.status.loadBalancer.ingress) > 0
119+
if not len(resource.status.loadBalancer.ingress):
120+
return resource.status.noLoadBalanceIngresses
121+
return resource.observed.metadata.name
90122

91123
class Job(Check):
92124
apiVersion = 'batch/v1'
93125
def ready(self, resource):
94126
for type in ('Failed', 'Suspended'):
95127
if resource.conditions[type].status:
96-
return False
97-
return bool(resource.conditions.Complete.status)
128+
return resource.status[f"is{type}"]
129+
complete = resource.conditions.Complete
130+
if not complete:
131+
return resource.status.notComplete
132+
if not complete.status:
133+
if complete.reason:
134+
return resource.status.notComplete[complete.reason]
135+
return resource.status.notComplete
136+
return resource.observed.metadata.name
98137

99138
class Namespace(AlwaysReady):
100139
apiVersion = 'v1'
101140

102141
class PersistentVolumeClaim(Check):
103142
apiVersion = 'v1'
104143
def ready(self, resource):
105-
return resource.status.phase == 'Bound'
144+
if resource.status.phase != 'Bound':
145+
return resource.status.phaseNotBound
146+
return resource.observed.metadata.name
106147

107148
class Pod(Check):
108149
apiVersion = 'v1'
109150
def ready(self, resource):
110151
if resource.status.phase == 'Succeeded':
111-
return True
152+
return resource.observed.metadata.name
112153
if resource.status.phase == 'Running':
113154
if resource.observed.spec.restartPolicy == 'Always':
114155
if resource.conditions.Ready.status:
115-
return True
116-
return False
156+
return resource.observed.metadata.name
157+
return resource.status.notSucceededOrRunning
117158

118159
class ReplicaSet(Check):
119160
apiVersion = 'v1'
120161
def ready(self, resource):
121162
if int(resource.status.observedGeneration) < int(resource.observed.metadata.generation):
122-
return False
163+
return resource.status.priorObservedGeneration
123164
if resource.conditions.ReplicaFailure.status:
124-
return False
125-
return int(resource.status.availableReplicas) >= int(resource.observed.spec.replicas or 1)
165+
if resource.conditions.ReplicaFailure.reason:
166+
return resource.status.isReplicaFailure[resource.conditions.ReplicaFailure.reason]
167+
return resource.status.isReplicaFailure
168+
if int(resource.status.availableReplicas) < int(resource.observed.spec.replicas or 1):
169+
return resource.status.tooFewavailableReplicas
170+
return resource.observed.metadata.name
126171

127172
class Role(AlwaysReady):
128173
apiVersion = 'rbac.authorization.k8s.io/v1'
@@ -137,8 +182,10 @@ class Service(Check):
137182
apiVersion = 'v1'
138183
def ready(self, resource):
139184
if resource.observed.spec.type != 'LoadBalancer':
140-
return True
141-
return len(resource.status.loadBalancer.ingress) > 0
185+
return resource.observed.metadata.name
186+
if not len(resource.status.loadBalancer.ingress):
187+
return resource.status.noLoadBalancerIngresses
188+
return resource.observed.metadata.name
142189

143190
class ServiceAccount(AlwaysReady):
144191
apiVersion = 'v1'
@@ -147,7 +194,12 @@ class StatefulSet(Check):
147194
apiVersion = 'apps/v1'
148195
def ready(self, resource):
149196
replicas = resource.observed.spec.replicas or 1
150-
return (resource.status.readyReplicas == replicas and
151-
resource.status.currentReplicas == replicas and
152-
resource.status.currentRevision == resource.status.updateRevision
153-
)
197+
for field in ('readyReplicas', 'currentReplicas'):
198+
value = resource.status[field]
199+
if not value:
200+
return value
201+
if replicas != value:
202+
return resource.status[F"{field}NotReplicas"]
203+
if resource.status.currentRevision != resource.status.updateRevision:
204+
return resource.status.currentRevisionNotUpdateReivsion
205+
return resource.observed.metadata.name

crossplane/pythonic/command.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ def add_function_arguments(cls, parser):
3333
metavar='WIDTH',
3434
help='Width of the logger name in the log output, default 40.',
3535
)
36+
parser.add_argument(
37+
'--logger-level',
38+
action='append',
39+
default=[],
40+
metavar='LOGGER=LEVEL',
41+
help='Logger level, for example: botocore.hooks=INFO',
42+
)
3643
parser.add_argument(
3744
'--python-path',
3845
action='append',
@@ -70,6 +77,11 @@ def initialize_function(self):
7077
logger = logging.getLogger()
7178
logger.handlers = [handler]
7279
logger.setLevel(logging.DEBUG if self.args.debug else logging.INFO)
80+
for logger_level in self.args.logger_level:
81+
for logger_level in logger_level.split(','):
82+
logger_level = logger_level.split('=')
83+
if len(logger_level) == 2:
84+
logging.getLogger(logger_level[0]).setLevel(logger_level[1].upper())
7385

7486
for path in reversed(self.args.python_path):
7587
sys.path.insert(0, str(pathlib.Path(path).expanduser().resolve()))

0 commit comments

Comments
 (0)