-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: support agent tokens * use params instead * rename to get and define default value for agent address * add tokens to documentation * sort the endpoints alphabetically * reorder tokens methods * fix: add missing default value * fix: return empty dict and not list * validate that response is an empty list
- Loading branch information
Showing
4 changed files
with
278 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from typing import List, Optional | ||
|
||
import xmltodict | ||
|
||
|
||
class Tokens: | ||
"""With tokens, one can authorize/unauthorize agents, or access agent | ||
details including token value and latest usage information. | ||
""" | ||
|
||
def __init__(self, quickbuild): | ||
self.quickbuild = quickbuild | ||
|
||
def get(self, agent_address: Optional[str] = None) -> List[dict]: | ||
""" | ||
Get token value and latest used information of agents. | ||
Args: | ||
agent_address (Optional[str]): | ||
Build agent address, eg. my-agent:8811. | ||
If param address is set to None, details of all agents will be returned. | ||
Returns: | ||
List[dict]: List of token and agent details | ||
""" | ||
def callback(response: str) -> List[dict]: | ||
root = xmltodict.parse(response) | ||
|
||
tokens = [] | ||
if root['list'] is not None: | ||
tokens = root['list']['com.pmease.quickbuild.model.Token'] | ||
if isinstance(tokens, list) is False: | ||
tokens = [tokens] | ||
return tokens | ||
|
||
params_agent_address = dict(address=agent_address) if agent_address else {} | ||
|
||
return self.quickbuild._request( | ||
'GET', | ||
'tokens', | ||
callback, | ||
params=params_agent_address, | ||
) | ||
|
||
def authorize(self, agent_ip: str, agent_port: int = 8811) -> str: | ||
""" | ||
Authorize a build agent to join the build grid. | ||
Args: | ||
agent_ip (str): The build agent IP address. | ||
agent_port (int): The build agent port (default: 8811). | ||
Returns: | ||
str: identifier of the newly created token for the build agent | ||
""" | ||
response = self.quickbuild._request( | ||
'GET', | ||
'tokens/authorize', | ||
params=dict(ip=agent_ip, port=agent_port), | ||
) | ||
|
||
return response | ||
|
||
def unauthorize(self, agent_ip: str, agent_port: int = 8811) -> str: | ||
""" | ||
Unauthorize an already authorized build agent. | ||
Args: | ||
agent_ip (str): The build agent IP address. | ||
agent_port (int): The build agent port (default: 8811). | ||
Returns: | ||
str: identifier of the removed token representing the build agent. | ||
""" | ||
response = self.quickbuild._request( | ||
'GET', | ||
'tokens/unauthorize', | ||
params=dict(ip=agent_ip, port=agent_port), | ||
) | ||
|
||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
|
||
import re | ||
|
||
import pytest | ||
import responses | ||
|
||
from quickbuild import AsyncQBClient, QBClient | ||
|
||
|
||
REGEX_IP = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' | ||
REGEX_PORT = '\d+' | ||
|
||
TOKEN_XML = r"""<?xml version="1.0" encoding="UTF-8"?> | ||
<list> | ||
<com.pmease.quickbuild.model.Token> | ||
<id>120204</id> | ||
<value>84858611-a1fe-4f88-a49c-f600cf0ecf11</value> | ||
<ip>192.168.1.100</ip> | ||
<port>8811</port> | ||
<test>false</test> | ||
<lastUsedDate>2021-02-08T20:01:09.426Z</lastUsedDate> | ||
<lastUsedReason>Run step (configuration: root/pipelineC, build: B.50906, step: master>build)</lastUsedReason> | ||
<hostName>quickbuild-agent-192-168-1-100</hostName> | ||
<offlineAlert>true</offlineAlert> | ||
</com.pmease.quickbuild.model.Token> | ||
</list> | ||
""" | ||
|
||
TOKENS_XML = r"""<?xml version="1.0" encoding="UTF-8"?> | ||
<list> | ||
<com.pmease.quickbuild.model.Token> | ||
<id>117554</id> | ||
<value>bee7e8ff-fd9a-475a-8cf5-42a5353b8875</value> | ||
<ip>192.168.1.100</ip> | ||
<port>8811</port> | ||
<test>false</test> | ||
<lastUsedDate>2021-02-08T20:08:29.360Z</lastUsedDate> | ||
<lastUsedReason>Check build condition (configuration: root/pipelineA)</lastUsedReason> | ||
<hostName>quickbuild-agent-192-168-1-100</hostName> | ||
<offlineAlert>true</offlineAlert> | ||
</com.pmease.quickbuild.model.Token> | ||
<com.pmease.quickbuild.model.Token> | ||
<id>115672</id> | ||
<value>27350640-d9f9-4a10-96ae-b6ec8fee998b</value> | ||
<ip>192.168.1.101</ip> | ||
<port>8811</port> | ||
<test>false</test> | ||
<lastUsedDate>2021-02-08T20:01:10.175Z</lastUsedDate> | ||
<lastUsedReason>Run step (configuration: root/pipelineA, build: B.1234, step: master>finalize)</lastUsedReason> | ||
<hostName>quickbuild-agent-192-168-1-101</hostName> | ||
<offlineAlert>true</offlineAlert> | ||
</com.pmease.quickbuild.model.Token> | ||
<com.pmease.quickbuild.model.Token> | ||
<id>116545</id> | ||
<value>8f604c48-b9f4-4bbe-847c-c073b2aebc81</value> | ||
<ip>192.168.1.102</ip> | ||
<port>8811</port> | ||
<test>false</test> | ||
<lastUsedDate>2021-02-08T20:01:10.013Z</lastUsedDate> | ||
<lastUsedReason>Run step (configuration: root/pipelineB, build: B.123, step: master>publish)</lastUsedReason> | ||
<hostName>quickbuild-agent-192-168-1-102</hostName> | ||
<offlineAlert>true</offlineAlert> | ||
</com.pmease.quickbuild.model.Token> | ||
</list> | ||
""" | ||
|
||
EMPTY_TOKEN_XML = r"""<?xml version="1.0" encoding="UTF-8"?> | ||
<list/> | ||
""" | ||
|
||
|
||
@responses.activate | ||
def test_authorize(): | ||
RESPONSE_DATA = '120123' | ||
|
||
responses.add( | ||
responses.GET, | ||
re.compile(r'.*/rest/tokens/authorize\?ip={}&port={}'.format(REGEX_IP, REGEX_PORT)), | ||
content_type='text/plain', | ||
body=RESPONSE_DATA, | ||
match_querystring=True, | ||
) | ||
|
||
response = QBClient('http://server').tokens.authorize('192.168.1.100', 8811) | ||
assert response == '120123' | ||
|
||
response = QBClient('http://server').tokens.authorize('192.168.1.100') | ||
assert response == '120123' | ||
|
||
|
||
@responses.activate | ||
def test_unauthorize(): | ||
RESPONSE_DATA = '120123' | ||
|
||
responses.add( | ||
responses.GET, | ||
re.compile(r'.*/rest/tokens/unauthorize\?ip={}&port={}'.format(REGEX_IP, REGEX_PORT)), | ||
content_type='text/plain', | ||
body=RESPONSE_DATA, | ||
match_querystring=True, | ||
) | ||
|
||
response = QBClient('http://server').tokens.unauthorize('192.168.1.100', 8811) | ||
assert response == '120123' | ||
|
||
response = QBClient('http://server').tokens.unauthorize('192.168.1.100') | ||
assert response == '120123' | ||
|
||
|
||
@responses.activate | ||
def test_token_and_agent_details(): | ||
responses.add( | ||
responses.GET, | ||
re.compile(r'.*/rest/tokens\?address=quickbuild-agent-192-168-1-100%3A8811'), | ||
content_type='application/xml', | ||
body=TOKEN_XML | ||
) | ||
|
||
response = QBClient('http://server').tokens.get('quickbuild-agent-192-168-1-100:8811') | ||
assert len(response) == 1 | ||
assert response[0]['id'] == '120204' | ||
|
||
|
||
@responses.activate | ||
def test_tokens_and_agent_details(): | ||
responses.add( | ||
responses.GET, | ||
re.compile(r'.*/rest/tokens'), | ||
content_type='application/xml', | ||
body=TOKENS_XML, | ||
) | ||
|
||
response = QBClient('http://server').tokens.get() | ||
assert len(response) == 3 | ||
assert response[0]['id'] == '117554' | ||
assert response[1]['id'] == '115672' | ||
assert response[2]['id'] == '116545' | ||
|
||
|
||
@responses.activate | ||
def test_tokens_and_agent_details_with_unknown_address(): | ||
responses.add( | ||
responses.GET, | ||
re.compile(r'.*/rest/tokens\?address=unknown'), | ||
content_type='application/xml', | ||
body=EMPTY_TOKEN_XML, | ||
match_querystring=True, | ||
) | ||
|
||
response = QBClient('http://server').tokens.get('unknown') | ||
assert len(response) == 0 | ||
assert response == [] | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_authorize_async(aiohttp_mock): | ||
RESPONSE_DATA = '120123' | ||
|
||
client = AsyncQBClient('http://server') | ||
try: | ||
aiohttp_mock.get( | ||
re.compile(r'.*/rest/tokens/authorize\?ip={}&port={}'.format(REGEX_IP, REGEX_PORT)), | ||
body=RESPONSE_DATA, | ||
) | ||
|
||
response = await client.tokens.authorize('192.168.1.100') | ||
assert response == '120123' | ||
finally: | ||
await client.close() | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_unauthorize_async(aiohttp_mock): | ||
RESPONSE_DATA = '120123' | ||
|
||
client = AsyncQBClient('http://server') | ||
try: | ||
aiohttp_mock.get( | ||
re.compile(r'.*/rest/tokens/unauthorize\?ip={}&port={}'.format(REGEX_IP, REGEX_PORT)), | ||
body=RESPONSE_DATA, | ||
) | ||
|
||
response = await client.tokens.unauthorize('192.168.1.100') | ||
assert response == '120123' | ||
finally: | ||
await client.close() |