Skip to content

Commit 1c7b10d

Browse files
committed
Fix token refresh for AAP 2.6
Signed-off-by: Justin Cinkelj <[email protected]>
1 parent 38ff7d1 commit 1c7b10d

File tree

5 files changed

+86
-43
lines changed

5 files changed

+86
-43
lines changed

src/backend/apps/clusters/connector.py

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import requests
88
from django.db import transaction
99
import urllib3
10+
import json
1011

1112
from backend.apps.clusters.encryption import decrypt_value, encrypt_value
1213
from backend.apps.clusters.models import (
@@ -85,7 +86,7 @@ def headers(self):
8586
}
8687

8788
def _reauth(self, timeout=None):
88-
url = f'{self.cluster.base_url}/api/o/token/' # TODO AAP version
89+
url = f'{self.cluster.base_url}{self.cluster.oauth_token_url}'
8990
refresh_token = decrypt_value(self.cluster.refresh_token)
9091
client_id = self.cluster.client_id
9192
client_secret = decrypt_value(self.cluster.client_secret)
@@ -270,39 +271,48 @@ def sync_common(self, sync_type):
270271
def ping(self, ping_url):
271272
logger.info(f'Pinging api {self.cluster.base_url}{ping_url}')
272273
url = f'{self.cluster.base_url}{ping_url}'
273-
return self.execute_get_one(url=url, timeout=5)
274+
# Do same as execute_get_one(), but without authentication.
275+
# ping is used to check version before token refresh, so we might not have a valid access token yet.
276+
# ping endpoint does not required authentication, but (AAP 2.6 at least):
277+
# - request is rejected if invalid HTTP basic auth is provided.
278+
# - request is accepted if invalid 'Authorization: Bearer ...' is provided.
279+
timeout = 5
280+
response = requests.get(
281+
url=url,
282+
verify=self.cluster.verify_ssl,
283+
timeout=timeout,
284+
headers=self.headers)
285+
if response is None or not response.ok:
286+
return None
287+
response = response.json()
288+
return response
274289

275-
@property
276-
def is_aap25_instance(self):
277-
logger.info(f'Checking if is AAP 2.5 at {self.cluster.base_url}')
278-
response = self.ping("/api/gateway/v1/ping/")
279-
return True if response is not None else False
290+
def detect_aap_version(self):
291+
logger.info(f'Checking if is AAP 2.5 ... 2.6 at {self.cluster.base_url}')
292+
response25 = self.ping("/api/gateway/v1/ping/")
293+
if response25:
294+
if response25["version"] == "2.6":
295+
return ClusterVersionChoices.AAP26
296+
elif response25["version"] == "2.5":
297+
return ClusterVersionChoices.AAP25
298+
else:
299+
raise Exception(f'Not valid version {response25["version"]} for cluster {self.cluster.base_url}.')
280300

281-
@property
282-
def is_aap24_instance(self):
283301
logger.info(f'Checking if is AAP 2.4 at {self.cluster.base_url}')
284-
response = self.ping("/api/v2/ping/")
285-
return True if response is not None else False
302+
response24 = self.ping("/api/v2/ping/")
303+
if response24:
304+
return ClusterVersionChoices.AAP24
286305

287-
def check_aap_version(self):
288-
logger.info(f'Checking AAP version at {self.cluster.base_url}')
289-
is_aap25_instance = self.is_aap25_instance
290-
if is_aap25_instance:
291-
if self.cluster.aap_version != ClusterVersionChoices.AAP25:
292-
self.cluster.aap_version = ClusterVersionChoices.AAP25
293-
self.cluster.save()
294-
logger.info(f'Detected AAP version 2.5 at {self.cluster.base_url}')
295-
return True
296-
297-
is_aap24_instance = self.is_aap24_instance
298-
if is_aap24_instance:
299-
if self.cluster.aap_version != ClusterVersionChoices.AAP24:
300-
self.cluster.aap_version = ClusterVersionChoices.AAP24
301-
self.cluster.save()
302-
logger.info(f'Detected AAP version 2.4 at {self.cluster.base_url}')
303-
return True
304306
raise Exception(f'Not valid version for cluster {self.cluster.base_url}.')
305307

308+
def check_aap_version(self):
309+
aap_version = self.detect_aap_version()
310+
if self.cluster.aap_version != aap_version:
311+
self.cluster.aap_version = aap_version
312+
self.cluster.save()
313+
logger.info(f'Detected AAP version {aap_version} at {self.cluster.base_url}')
314+
return True
315+
306316
def sync_jobs(self):
307317
for job in self.jobs:
308318
logger.info("Checking status of job %s", job)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 4.2.17 on 2025-10-23 12:13
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("clusters", "0015_cluster_client_id_cluster_client_secret_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="cluster",
15+
name="aap_version",
16+
field=models.CharField(
17+
choices=[
18+
("AAP 2.6", "AAP 2.6"),
19+
("AAP 2.5", "AAP 2.5"),
20+
("AAP 2.4", "AAP 2.4"),
21+
],
22+
default="AAP 2.4",
23+
max_length=15,
24+
),
25+
),
26+
]

src/backend/apps/clusters/models.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Meta:
3636

3737

3838
class ClusterVersionChoices(models.TextChoices):
39+
AAP26 = "AAP 2.6", "AAP 2.6"
3940
AAP25 = "AAP 2.5", "AAP 2.5"
4041
AAP24 = "AAP 2.4", "AAP 2.4"
4142

@@ -60,7 +61,7 @@ def base_url(self):
6061

6162
@property
6263
def api_url(self):
63-
if self.aap_version == ClusterVersionChoices.AAP25:
64+
if self.aap_version in [ClusterVersionChoices.AAP25, ClusterVersionChoices.AAP26]:
6465
return f'/api/controller/v2'
6566
elif self.aap_version == ClusterVersionChoices.AAP24:
6667
return f'/api/v2'
@@ -69,13 +70,22 @@ def api_url(self):
6970

7071
@property
7172
def gui_base_url(self):
72-
if self.aap_version == ClusterVersionChoices.AAP25:
73+
if self.aap_version in [ClusterVersionChoices.AAP25, ClusterVersionChoices.AAP26]:
7374
return f'{self.base_url}/execution/'
7475
elif self.aap_version == ClusterVersionChoices.AAP24:
7576
return f'{self.base_url}/#/'
7677
else:
7778
raise NotImplementedError
7879

80+
@property
81+
def oauth_token_url(self):
82+
if self.aap_version == ClusterVersionChoices.AAP26:
83+
return '/o/token/'
84+
elif self.aap_version in [ClusterVersionChoices.AAP24, ClusterVersionChoices.AAP25]:
85+
return '/api/o/token/'
86+
else:
87+
raise NotImplementedError
88+
7989
@property
8090
def cache_timeout_blocked(self):
8191
from backend.apps.scheduler.models import SyncJob, JobTypeChoices as SyncJobTypeChoices

src/backend/apps/clusters/schemas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ClusterSettings(FrozenModel):
2828

2929

3030
class ClusterSchema(ClusterSettings):
31-
aap_version: Literal["AAP 2.5", "AAP 2.4"] | None = None
31+
aap_version: Literal["AAP 2.6", "AAP 2.5", "AAP 2.4"] | None = None
3232
api_url: str
3333
base_url: str
3434

src/backend/tests/unit/test_connector.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,19 @@ def mocked_requests_get(*args, **kwargs):
137137
response = connector.ping(url)
138138
assert response == {'version': '4.4.2'}
139139

140-
def test_is_aap25_instance(self, mocker, cluster):
140+
def test_detect_aap_version_25(self, mocker, cluster):
141141
def mocked_requests_get(*args, **kwargs):
142142
headers = {
143143
'Content-Type': 'application/json',
144144
'X-Api-Product-Name': 'Red Hat Ansible Automation Platform',
145145
}
146-
return get_response(**{'headers': headers})
146+
return get_response(**{'headers': headers, 'data': dict(version="2.5")})
147147

148148
connector = ApiConnector(cluster)
149149
mocker.patch('requests.get', new=mocked_requests_get)
150-
assert connector.is_aap25_instance is True
150+
assert connector.detect_aap_version() == ClusterVersionChoices.AAP25
151151

152-
def test_is_aap24_instance(self, mocker, cluster):
152+
def test_detect_aap_version_24(self, mocker, cluster):
153153
def mocked_requests_get(*args, **kwargs):
154154
headers = {
155155
'Content-Type': 'application/json',
@@ -158,26 +158,23 @@ def mocked_requests_get(*args, **kwargs):
158158
return get_response(**{'headers': headers})
159159

160160
connector = ApiConnector(cluster)
161-
mocker.patch('requests.get', new=mocked_requests_get)
162-
assert connector.is_aap24_instance is True
161+
mocker.patch('requests.get', side_effect=[None, mocked_requests_get()])
162+
assert connector.detect_aap_version() == ClusterVersionChoices.AAP24
163163

164164
def test_check_aap_version_24(self, mocker, cluster):
165-
mocker.patch('backend.apps.clusters.connector.ApiConnector.is_aap25_instance', return_value=False, new_callable=mocker.PropertyMock)
166-
mocker.patch('backend.apps.clusters.connector.ApiConnector.is_aap24_instance', return_value=True, new_callable=mocker.PropertyMock)
165+
mocker.patch('backend.apps.clusters.connector.ApiConnector.detect_aap_version', return_value="AAP 2.4")
167166
connector = ApiConnector(cluster)
168167
connector.check_aap_version()
169168
assert Cluster.objects.first().aap_version == ClusterVersionChoices.AAP24
170169

171170
def test_check_aap_version_25(self, mocker, cluster):
172-
mocker.patch('backend.apps.clusters.connector.ApiConnector.is_aap25_instance', return_value=True, new_callable=mocker.PropertyMock)
173-
mocker.patch('backend.apps.clusters.connector.ApiConnector.is_aap24_instance', return_value=False, new_callable=mocker.PropertyMock)
171+
mocker.patch('backend.apps.clusters.connector.ApiConnector.detect_aap_version', return_value="AAP 2.5")
174172
connector = ApiConnector(cluster)
175173
connector.check_aap_version()
176174
assert Cluster.objects.first().aap_version == ClusterVersionChoices.AAP25
177175

178176
def test_check_aap_version_fail(self, mocker, cluster):
179-
mocker.patch('backend.apps.clusters.connector.ApiConnector.is_aap25_instance', return_value=False, new_callable=mocker.PropertyMock)
180-
mocker.patch('backend.apps.clusters.connector.ApiConnector.is_aap24_instance', return_value=False, new_callable=mocker.PropertyMock)
177+
mocker.patch('backend.apps.clusters.connector.ApiConnector.detect_aap_version', side_effect=Exception('Not valid version for cluster https://localhost:8000.'), new_callable=mocker.PropertyMock)
181178
connector = ApiConnector(cluster)
182179
with pytest.raises(Exception) as e:
183180
connector.check_aap_version()

0 commit comments

Comments
 (0)