Skip to content

Commit 740c810

Browse files
committed
Rework Crossplane v2 handling
1 parent d2c28d1 commit 740c810

36 files changed

Lines changed: 1228 additions & 96 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,4 @@ crossplane/pythonic/__version__.py
215215
pocs/
216216
pythonic-packages/
217217
tests/protobuf/pytest_pb2*
218+
scripts/.aws-credentials

crossplane/pythonic/auto_ready.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
2+
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+
check = _checks.get((resource.apiVersion, resource.kind))
9+
if check:
10+
if check.ready(resource):
11+
resource.ready = True
12+
else:
13+
if resource.conditions.Ready.status:
14+
resource.ready = True
15+
16+
17+
_checks = {}
18+
19+
20+
class Check:
21+
apiVersion = None
22+
23+
@classmethod
24+
def __init_subclass__(cls, **kwargs):
25+
super().__init_subclass__(**kwargs)
26+
_checks[(cls.apiVersion, cls.__name__)] = cls()
27+
28+
def ready(self, resource):
29+
return True
30+
31+
32+
class ConfigMap(Check):
33+
apiVersion = 'v1'
34+
35+
class CronJob(Check):
36+
apiVersion = 'batch/v1'
37+
38+
def ready(self, resource):
39+
if resource.observed.spec.suspend:
40+
return True
41+
if not resource.status.lastScheduleTime:
42+
return False
43+
if resource.status.active:
44+
return True
45+
if not resource.status.lastSuccessfulTime:
46+
return False
47+
return resource.status.lastSuccessfulTime >= resource.status.lastScheduleTime
48+
49+
class DaemonSet(Check):
50+
apiVersion = 'apps/v1'
51+
52+
def ready(self, resource):
53+
if not resource.status.desiredNumberScheduled:
54+
return False
55+
scheduled = resource.status.desiredNumberScheduled
56+
return (scheduled == resource.status.numberReady and
57+
scheduled == resource.status.updatedNumberScheduled and
58+
scheduled == resource.status.numberAvailable
59+
)
60+
61+
class Deployment(Check):
62+
apiVersion = 'apps/v1'
63+
64+
def ready(self, resource):
65+
replicas = resource.observed.spec.replicas or 1
66+
if replicas != resource.status.updatedReplicas or replicas != resource.status.availableReplicas:
67+
return False
68+
return bool(resource.conditions.Available.status)
69+
70+
class HorizontalPodAutoscaler(Check):
71+
apiVersion = 'autoscaling/v2'
72+
73+
def ready(self, resource):
74+
for type in ('FailedGetScale', 'FailedUpdateScale', 'FailedGetResourceMetric', 'InvalidSelector'):
75+
if resource.conditions[type].status:
76+
return False
77+
for type in ('ScalingActive', 'ScalingLimited'):
78+
if resource.conditions[type].status:
79+
return True
80+
return False
81+
82+
class Ingress(Check):
83+
apiVersion = 'networking.k8s.io/v1'
84+
85+
def ready(self, resource):
86+
return len(resource.status.loadBalancer.ingress) > 0
87+
88+
class Job(Check):
89+
apiVersion = 'batch/v1'
90+
91+
def ready(self, resource):
92+
return bool(resource.conditions.JobComplete.status)
93+
94+
class Namespace(Check):
95+
apiVersion = 'v1'
96+
97+
class PersistentVolumeClaim(Check):
98+
apiVersion = 'v1'
99+
100+
def ready(self, resource):
101+
return resource.status.phase == 'Bound'
102+
103+
class Pod(Check):
104+
apiVersion = 'v1'
105+
106+
def ready(self, resource):
107+
if resource.status.phase == 'Succeeded':
108+
return True
109+
if resource.status.phase == 'Running':
110+
if resource.observed.spec.restartPolicy == 'Always':
111+
if resource.conditions.Ready.status:
112+
return True
113+
return False
114+
115+
class ReplicaSet(Check):
116+
apiVersion = 'v1'
117+
118+
def ready(self, resource):
119+
if int(resource.status.observedGeneration) < int(resource.observed.spec.generation):
120+
return False
121+
if resource.conditions.ReplicaFailure.status:
122+
return False
123+
replicas = resource.observed.spec.replicas or 1
124+
return resource.status.AvailableReplicas >= replicas
125+
126+
class Secret(Check):
127+
apiVersion = 'v1'
128+
129+
class Service(Check):
130+
apiVersion = 'v1'
131+
132+
def ready(self, resource):
133+
if resource.observed.spec.type != 'LoadBalancer':
134+
return True
135+
return len(resource.status.loadBalancer.ingress) > 0
136+
137+
class ServiceAccount(Check):
138+
apiVersion = 'v1'
139+
140+
class StatefulSet(Check):
141+
apiVersion = 'apps/v1'
142+
143+
def ready(self, resource):
144+
replicas = resource.observed.spec.replicas or 1
145+
return (replicas == resource.status.readyReplicas and
146+
replicas == resource.status.currentReplicas and
147+
resource.status.currentRevision == resource.status.updateRevision
148+
)

crossplane/pythonic/command.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ def add_function_arguments(cls, parser):
5050
action='store_true',
5151
help='Allow oversized protobuf messages',
5252
)
53+
parser.add_argument(
54+
'--crossplane-v1',
55+
action='store_true',
56+
help='Enable Crossplane V1 compatibility mode',
57+
)
5358

5459
def __init__(self, args):
5560
self.args = args

0 commit comments

Comments
 (0)