Skip to content

Commit dee2c45

Browse files
authored
Merge branch 'main' into feature/compliance-history
2 parents b6b9f4c + 9b150b5 commit dee2c45

10 files changed

Lines changed: 108 additions & 75 deletions

File tree

Standards/scs-0007-v2-certification-integrators.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ As to remain backward compatibility with version 1 of this standard, the suffix
3939

4040
### General expectations
4141

42-
The general expectation for certification is to provide proof of experience in setting up, operating and supporting SCS-compliant environments.
42+
The general expectation for certification is to provide proof of experience in setting up, operating and supporting SCS-compliant environments. This experience MUST remain in place for as long as the certification is valid.
4343

4444
SCS is an open source community project with the goal of enabling digital sovereignty. As such, the commitment and support of this mission SHOULD be recognized and promoted beyond technical competence.
4545

Tests/iaas/requirements.txt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,26 @@
55
# pip-compile requirements.in
66
#
77
boto3==1.42.86
8-
# via -r requirements.in
8+
# via -r iaas/requirements.in
99
botocore==1.42.86
1010
# via
1111
# boto3
1212
# s3transfer
1313
certifi==2026.2.25
1414
# via
15-
# -c ../requirements.txt
15+
# -c iaas/../requirements.txt
1616
# requests
1717
cffi==2.0.0
1818
# via
19-
# -c ../requirements.txt
19+
# -c iaas/../requirements.txt
2020
# cryptography
2121
charset-normalizer==3.4.7
2222
# via
23-
# -c ../requirements.txt
23+
# -c iaas/../requirements.txt
2424
# requests
2525
cryptography==46.0.7
2626
# via
27-
# -c ../requirements.txt
27+
# -c iaas/../requirements.txt
2828
# openstacksdk
2929
decorator==5.2.1
3030
# via
@@ -34,7 +34,7 @@ dogpile-cache==1.5.0
3434
# via openstacksdk
3535
idna==3.11
3636
# via
37-
# -c ../requirements.txt
37+
# -c iaas/../requirements.txt
3838
# requests
3939
iso8601==2.1.0
4040
# via
@@ -52,7 +52,7 @@ jsonpointer==3.1.1
5252
keystoneauth1==5.13.1
5353
# via openstacksdk
5454
openstacksdk==4.10.0
55-
# via -r requirements.in
55+
# via -r iaas/requirements.in
5656
os-service-types==1.8.2
5757
# via
5858
# keystoneauth1
@@ -68,41 +68,41 @@ psutil==7.2.2
6868
# via openstacksdk
6969
pycparser==3.0
7070
# via
71-
# -c ../requirements.txt
71+
# -c iaas/../requirements.txt
7272
# cffi
7373
python-dateutil==2.9.0.post0
7474
# via
75-
# -c ../requirements.txt
75+
# -c iaas/../requirements.txt
7676
# botocore
7777
pyyaml==6.0.3
7878
# via
79-
# -c ../requirements.txt
79+
# -c iaas/../requirements.txt
8080
# openstacksdk
8181
requests==2.33.1
8282
# via
83-
# -c ../requirements.txt
83+
# -c iaas/../requirements.txt
8484
# keystoneauth1
8585
requestsexceptions==1.4.0
8686
# via openstacksdk
8787
s3transfer==0.16.0
8888
# via boto3
8989
six==1.17.0
9090
# via
91-
# -c ../requirements.txt
91+
# -c iaas/../requirements.txt
9292
# python-dateutil
9393
stevedore==5.7.0
9494
# via
9595
# dogpile-cache
9696
# keystoneauth1
9797
typing-extensions==4.15.0
9898
# via
99-
# -c ../requirements.txt
99+
# -c iaas/../requirements.txt
100100
# keystoneauth1
101101
# openstacksdk
102102
# os-service-types
103-
urllib3==2.6.3
103+
urllib3==2.7.0
104104
# via
105-
# -c ../requirements.txt
105+
# -c iaas/../requirements.txt
106106
# botocore
107107
# requests
108108

Tests/kaas/plugin/cs_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ def get_secret_data(api_instance: CoreV1Api, namespace, secret):
4141

4242
def get_cluster_status(api_instance: CustomObjectsApi, namespace, name):
4343
return api_instance.get_namespaced_custom_object_status(
44-
'cluster.x-k8s.io', 'v1beta1', namespace, 'clusters', name
44+
'cluster.x-k8s.io', 'v1beta2', namespace, 'clusters', name
4545
)
4646

4747

4848
def delete_cluster(api_instance: CustomObjectsApi, namespace, name):
4949
"""mimic `kubectl delete cluster`"""
5050
# beware: do not fiddle with propagation policy here, as this may lead to severe problems
5151
return api_instance.delete_namespaced_custom_object(
52-
'cluster.x-k8s.io', 'v1beta1', namespace, 'clusters', name,
52+
'cluster.x-k8s.io', 'v1beta2', namespace, 'clusters', name,
5353
)

Tests/kaas/plugin/plugin_clusterstacks.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616

1717
TEMPLATE_KEYS = ('cluster', 'clusterstack', 'kubeconfig')
18+
TIMEOUTS = [30] * 40 + [0] # wait at most 1200 seconds (20 minutes); sentinel value at the end
1819

1920

2021
class _ClusterOps:
@@ -44,6 +45,7 @@ def _get_phase(self, co_api: _csh.CustomObjectsApi):
4445

4546
def create(self, co_api: _csh.CustomObjectsApi, cluster_dict):
4647
# repeat this because it's possible that a cluster object exists that is in Deleting phase
48+
timeouts = iter(TIMEOUTS)
4749
while True:
4850
logger.debug(f'creating cluster object for {self.name}')
4951
try:
@@ -60,8 +62,11 @@ def create(self, co_api: _csh.CustomObjectsApi, cluster_dict):
6062
logger.debug(f'cluster object for {self.name} already present in phase {phase}')
6163
if phase.lower() in ('provisioned', 'provisioning'):
6264
break
63-
logger.debug(f'waiting 30 s for cluster {self.name} to vanish or become provisioned')
64-
time.sleep(30)
65+
timeout = next(timeouts)
66+
if not timeout:
67+
raise RuntimeError(f"Timeout error while waiting on Cluster {self.name}")
68+
logger.debug(f'waiting {timeout} s for cluster {self.name} to vanish or become provisioned')
69+
time.sleep(timeout)
6570
else:
6671
break
6772

@@ -78,23 +83,34 @@ def delete(self, co_api: _csh.CustomObjectsApi):
7883
# it will take at least 5 s (and way longer if the cluster is already running)
7984
logger.debug(f'cluster {self.name} deletion requested; waiting 8 s for it to vanish')
8085
time.sleep(8)
86+
timeouts = iter(TIMEOUTS)
8187
while True:
8288
phase = self._get_phase(co_api)
8389
if phase is None:
90+
logger.debug(f"Cluster {self.name} deleted.")
8491
break
8592
if phase.lower() != 'deleting':
8693
raise RuntimeError(f'cluster {self.name} in phase {phase}; expected: Deleting')
87-
logger.debug(f'cluster {self.name} still deleting; waiting 30 s for it to vanish')
88-
time.sleep(30)
94+
timeout = next(timeouts)
95+
if not timeout:
96+
raise RuntimeError(f"Cluster {self.name} has not gone away in time")
97+
logger.debug(f'cluster {self.name} still deleting; waiting {timeout} s for it to vanish')
98+
time.sleep(timeout)
8999

90100
def get_kubeconfig(self, core_api: _csh.CoreV1Api):
91101
return _csh.get_secret_data(core_api, self.namespace, self.secret_name)
92102

93103
def wait_for_cluster_ready(self, co_api: _csh.CustomObjectsApi):
94-
while not self._get_condition_ready(co_api):
95-
logger.debug(f'waiting 30 s for cluster {self.name} to become ready')
96-
time.sleep(30)
97-
logger.debug(f'cluster {self.name} appears to be ready')
104+
timeouts = iter(TIMEOUTS)
105+
while True:
106+
if self._get_condition_ready(co_api):
107+
logger.debug(f'cluster {self.name} appears to be ready')
108+
break
109+
timeout = next(timeouts)
110+
if not timeout:
111+
raise RuntimeError(f"Cluster {self.name} has not become ready in time")
112+
logger.debug(f'waiting {timeout} s for cluster {self.name} to become ready')
113+
time.sleep(timeout)
98114

99115

100116
def load_templates(env, basepath, fn_map, keys=TEMPLATE_KEYS):

Tests/kaas/plugin/plugin_gardener.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616

1717
TEMPLATE_KEYS = ('shoot', 'kubeconfig')
18+
TIMEOUTS = [30] * 40 + [0] # wait at most 1200 seconds (20 minutes); sentinel value at the end
1819

1920

2021
class _ShootOps:
@@ -35,6 +36,7 @@ def create(self, co_api: _gh.CustomObjectsApi, shoot_dict):
3536
# Gardener shoot reconciliation is idempotent, so we can just try to create.
3637
# If it exists, we check its state.
3738
# repeat this because it's possible that a cluster object exists that is in state Delete
39+
timeouts = iter(TIMEOUTS)
3840
while True:
3941
logger.debug(f"creating shoot object for {self.name}")
4042
try:
@@ -55,8 +57,11 @@ def create(self, co_api: _gh.CustomObjectsApi, shoot_dict):
5557
if state == 'Failed':
5658
raise RuntimeError(f"Shoot {self.name} is in Failed state. Please check the Gardener dashboard.")
5759

58-
logger.debug(f'waiting 30 s for shoot {self.name} to proceed')
59-
time.sleep(30)
60+
timeout = next(timeouts)
61+
if not timeout:
62+
raise RuntimeError(f"Timeout error while waiting on shoot {self.name}")
63+
logger.debug(f'waiting {timeout} s for shoot {self.name} to proceed')
64+
time.sleep(timeout)
6065
else:
6166
break
6267

@@ -74,41 +79,52 @@ def delete(self, co_api: _gh.CustomObjectsApi):
7479

7580
logger.debug(f"Shoot {self.name} deletion requested; waiting 8 s for it to start deleting")
7681
time.sleep(8)
82+
timeouts = iter(TIMEOUTS)
7783
while True:
7884
last_op = self._get_last_operation(co_api)
7985
if last_op is None:
8086
logger.info(f"Shoot {self.name} has been deleted.")
8187
break
8288

83-
if last_op.get('type') != 'Delete':
84-
raise RuntimeError(f"Shoot {self.name} in unexpected state during deletion: {last_op!r}")
85-
state = last_op.get('state', 'Unknown')
86-
if state not in ('Processing', 'Succeeded'):
87-
raise RuntimeError(f"Shoot {self.name} in unexpected state during deletion: state={state}")
89+
timeout = next(timeouts)
90+
if not timeout:
91+
raise RuntimeError(f"Shoot {self.name} has not gone away in time")
8892

89-
if state == 'Processing':
93+
state = last_op.get('state', 'Unknown')
94+
if last_op.get('type') != 'Delete':
95+
logger.debug(f"Shoot {self.name} in unexpected state during deletion: {last_op!r}; waiting {timeout} s")
96+
elif state == 'Processing':
9097
progress = last_op.get('progress', 0)
91-
logger.debug(f"Shoot {self.name} is being deleted (progress: {progress} %); waiting 30 s")
98+
logger.debug(f"Shoot {self.name} is being deleted (progress: {progress} %); waiting {timeout} s")
9299
elif state == 'Succeeded':
93-
logger.info(f"Shoot {self.name} deletion succeeded, but object still exists. Waiting 30 s for it to vanish.")
94-
time.sleep(30)
100+
logger.info(f"Shoot {self.name} deletion succeeded, but object still exists. Waiting {timeout} s for it to vanish.")
101+
else:
102+
raise RuntimeError(f"Shoot {self.name} in unexpected state during deletion: state={state}")
103+
time.sleep(timeout)
95104

96105
def get_kubeconfig(self, api_client: ApiClient):
97106
return _gh.request_kubeconfig(api_client, self.namespace, self.name)
98107

99108
def wait_for_shoot_ready(self, co_api: _gh.CustomObjectsApi):
100-
last_op = self._get_last_operation(co_api)
101-
while last_op is not None and last_op.get('state') not in ('Succeeded', 'Failed'):
109+
timeouts = iter(TIMEOUTS)
110+
while True:
111+
last_op = self._get_last_operation(co_api)
112+
if last_op is None:
113+
raise RuntimeError(f"Shoot {self.name} object disappeared")
114+
102115
state = last_op.get('state', 'Unknown')
116+
if state == 'Succeeded':
117+
logger.debug(f'shoot {self.name} appears to be ready')
118+
return
119+
if state not in ('Unknown', 'Processing'):
120+
raise RuntimeError(f"Shoot {self.name} object in unexpected state {last_op.get('state')}")
121+
122+
timeout = next(timeouts)
123+
if not timeout:
124+
raise RuntimeError(f"Shoot {self.name} has not become ready in time")
103125
progress = last_op.get('progress', 0)
104-
logger.debug(f'waiting 30 s for shoot {self.name} to become ready (current state: {state}, progress: {progress}%)')
105-
time.sleep(30)
106-
last_op = self._get_last_operation(co_api)
107-
if last_op is None:
108-
raise RuntimeError(f"Shoot {self.name} object disappeared or in state Failed")
109-
if last_op.get('state') != 'Succeeded':
110-
raise RuntimeError(f"Shoot {self.name} object in unexpected state {last_op.get('state')}")
111-
logger.debug(f'shoot {self.name} appears to be ready')
126+
logger.debug(f'waiting {timeout} s for shoot {self.name} to become ready (current state: {state}, progress: {progress}%)')
127+
time.sleep(timeout)
112128

113129

114130
def load_templates(env, basepath, fn_map, keys=TEMPLATE_KEYS):

0 commit comments

Comments
 (0)