mirror of
https://github.com/openSUSE/osc.git
synced 2025-02-15 23:07:18 +01:00
Migrate 'token' command to obs_api.Token
This commit is contained in:
parent
3c096b82c9
commit
011adb4689
@ -5,7 +5,6 @@ Scenario: Run `osc token` with no arguments
|
|||||||
When I execute osc with args "token"
|
When I execute osc with args "token"
|
||||||
Then stdout is
|
Then stdout is
|
||||||
"""
|
"""
|
||||||
<directory count="0"/>
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -24,11 +23,15 @@ Scenario: Run `osc token --operation rebuild`
|
|||||||
Given I execute osc with args "token"
|
Given I execute osc with args "token"
|
||||||
And stdout matches
|
And stdout matches
|
||||||
"""
|
"""
|
||||||
<directory count="1">
|
ID : 1
|
||||||
<entry id="1" string=".*" kind="rebuild" description="" triggered_at="" project="test:factory" package="test-pkgA"/>
|
String : .*
|
||||||
</directory>
|
Operation : rebuild
|
||||||
|
Description :
|
||||||
|
Project : test:factory
|
||||||
|
Package : test-pkgA
|
||||||
|
Triggered at :
|
||||||
"""
|
"""
|
||||||
And I search 'string="(?P<token>[^"]+)' in stdout and store named groups in 'tokens'
|
And I search 'String *: *(?P<token>.+)\n' in stdout and store named groups in 'tokens'
|
||||||
When I execute osc with args "token --trigger {context.tokens[0][token]}"
|
When I execute osc with args "token --trigger {context.tokens[0][token]}"
|
||||||
Then stdout is
|
Then stdout is
|
||||||
"""
|
"""
|
||||||
|
@ -1688,6 +1688,7 @@ class Osc(cmdln.Cmdln):
|
|||||||
osc token --delete <TOKENID>
|
osc token --delete <TOKENID>
|
||||||
osc token --trigger <TOKENSTRING> [--operation <OPERATION>] [<PROJECT> <PACKAGE>]
|
osc token --trigger <TOKENSTRING> [--operation <OPERATION>] [<PROJECT> <PACKAGE>]
|
||||||
"""
|
"""
|
||||||
|
from . import obs_api
|
||||||
|
|
||||||
args = slash_split(args)
|
args = slash_split(args)
|
||||||
|
|
||||||
@ -1696,60 +1697,51 @@ class Osc(cmdln.Cmdln):
|
|||||||
raise oscerr.WrongOptions(msg)
|
raise oscerr.WrongOptions(msg)
|
||||||
|
|
||||||
apiurl = self.get_api_url()
|
apiurl = self.get_api_url()
|
||||||
url_path = ['person', conf.get_apiurl_usr(apiurl), 'token']
|
user = conf.get_apiurl_usr(apiurl)
|
||||||
|
|
||||||
|
if len(args) > 1:
|
||||||
|
project = args[0]
|
||||||
|
package = args[1]
|
||||||
|
else:
|
||||||
|
project = None
|
||||||
|
package = None
|
||||||
|
|
||||||
if opts.create:
|
if opts.create:
|
||||||
if not opts.operation:
|
if not opts.operation:
|
||||||
self.argparser.error("Please specify --operation")
|
self.argparser.error("Please specify --operation")
|
||||||
|
|
||||||
if opts.operation == 'workflow' and not opts.scm_token:
|
if opts.operation == 'workflow' and not opts.scm_token:
|
||||||
msg = 'The --operation=workflow option requires a --scm-token=<token> option'
|
msg = 'The --operation=workflow option requires a --scm-token=<token> option'
|
||||||
raise oscerr.WrongOptions(msg)
|
raise oscerr.WrongOptions(msg)
|
||||||
print("Create a new token")
|
|
||||||
query = {'cmd': 'create'}
|
|
||||||
if opts.operation:
|
|
||||||
query['operation'] = opts.operation
|
|
||||||
if opts.scm_token:
|
|
||||||
query['scm_token'] = opts.scm_token
|
|
||||||
if len(args) > 1:
|
|
||||||
query['project'] = args[0]
|
|
||||||
query['package'] = args[1]
|
|
||||||
|
|
||||||
url = makeurl(apiurl, url_path, query)
|
print("Create a new token")
|
||||||
f = http_POST(url)
|
status = obs_api.Token.cmd_create(
|
||||||
while True:
|
apiurl,
|
||||||
data = f.read(16384)
|
user,
|
||||||
if not data:
|
operation=opts.operation,
|
||||||
break
|
project=project,
|
||||||
sys.stdout.buffer.write(data)
|
package=package,
|
||||||
|
scm_token=opts.scm_token,
|
||||||
|
)
|
||||||
|
print(status.to_string())
|
||||||
|
|
||||||
elif opts.delete:
|
elif opts.delete:
|
||||||
print("Delete token")
|
print("Delete token")
|
||||||
url_path.append(opts.delete)
|
status = obs_api.Token.do_delete(apiurl, user, token=opts.delete)
|
||||||
url = makeurl(apiurl, url_path)
|
print(status.to_string())
|
||||||
http_DELETE(url)
|
|
||||||
elif opts.trigger:
|
elif opts.trigger:
|
||||||
print("Trigger token")
|
print("Trigger token")
|
||||||
query = {}
|
status = obs_api.Token.do_trigger(apiurl, token=opts.trigger, project=project, package=package)
|
||||||
if len(args) > 1:
|
print(status.to_string())
|
||||||
query['project'] = args[0]
|
|
||||||
query['package'] = args[1]
|
|
||||||
if opts.operation:
|
|
||||||
url = makeurl(apiurl, ["trigger", opts.operation], query)
|
|
||||||
else:
|
|
||||||
url = makeurl(apiurl, ["trigger"], query)
|
|
||||||
headers = {
|
|
||||||
'Content-Type': 'application/octet-stream',
|
|
||||||
'Authorization': "Token " + opts.trigger,
|
|
||||||
}
|
|
||||||
fd = http_POST(url, headers=headers)
|
|
||||||
print(decode_it(fd.read()))
|
|
||||||
else:
|
else:
|
||||||
if args and args[0] in ['create', 'delete', 'trigger']:
|
if args and args[0] in ['create', 'delete', 'trigger']:
|
||||||
raise oscerr.WrongArgs("Did you mean --" + args[0] + "?")
|
raise oscerr.WrongArgs("Did you mean --" + args[0] + "?")
|
||||||
# just list token
|
|
||||||
url = makeurl(apiurl, url_path)
|
# just list tokens
|
||||||
for data in streamfile(url, http_GET):
|
token_list = obs_api.Token.do_list(apiurl, user)
|
||||||
sys.stdout.buffer.write(data)
|
for obj in token_list:
|
||||||
|
print(obj.to_human_readable_string())
|
||||||
|
print()
|
||||||
|
|
||||||
@cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
|
@cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
|
||||||
help='affect only a given attribute')
|
help='affect only a given attribute')
|
||||||
|
@ -4,3 +4,4 @@ from .package_sources import PackageSources
|
|||||||
from .person import Person
|
from .person import Person
|
||||||
from .project import Project
|
from .project import Project
|
||||||
from .request import Request
|
from .request import Request
|
||||||
|
from .token import Token
|
||||||
|
@ -11,6 +11,8 @@ class StatusData(XmlModel):
|
|||||||
SOURCEPACKAGE = "sourcepackage"
|
SOURCEPACKAGE = "sourcepackage"
|
||||||
TARGETPROJECT = "targetproject"
|
TARGETPROJECT = "targetproject"
|
||||||
TARGETPACKAGE = "targetpackage"
|
TARGETPACKAGE = "targetpackage"
|
||||||
|
TOKEN = "token"
|
||||||
|
ID = "id"
|
||||||
|
|
||||||
name: NameEnum = Field(
|
name: NameEnum = Field(
|
||||||
xml_attribute=True,
|
xml_attribute=True,
|
||||||
|
179
osc/obs_api/token.py
Normal file
179
osc/obs_api/token.py
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
from ..util.models import * # pylint: disable=wildcard-import,unused-wildcard-import
|
||||||
|
from .status import Status
|
||||||
|
|
||||||
|
|
||||||
|
class Token(XmlModel):
|
||||||
|
XML_TAG = "entry"
|
||||||
|
|
||||||
|
id: int = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
The unique id of this token.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
string: str = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
The token secret. This string can be used instead of the password to
|
||||||
|
authenticate the user or to trigger service runs via the
|
||||||
|
`POST /trigger/runservice` route.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
description: Optional[str] = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
This attribute can be used to identify a token from the list of tokens
|
||||||
|
of a user.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
project: Optional[str] = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
If this token is bound to a specific package, then the packages'
|
||||||
|
project is available in this attribute.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
package: Optional[str] = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
The package name to which this token is bound, if it has been created
|
||||||
|
for a specific package. Otherwise this attribute and the project
|
||||||
|
attribute are omitted.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Kind(str, Enum):
|
||||||
|
RSS = "rss"
|
||||||
|
REBUILD = "rebuild"
|
||||||
|
RELEASE = "release"
|
||||||
|
RUNSERVICE = "runservice"
|
||||||
|
WORKFLOW = "workflow"
|
||||||
|
|
||||||
|
kind: Kind = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
This attribute specifies which actions can be performed via this token.
|
||||||
|
- rss: used to retrieve the notification RSS feed
|
||||||
|
- rebuild: trigger rebuilds of packages
|
||||||
|
- release: trigger project releases
|
||||||
|
- runservice: run a service via the POST /trigger/runservice route
|
||||||
|
- workflow: trigger SCM/CI workflows, see https://openbuildservice.org/help/manuals/obs-user-guide/cha.obs.scm_ci_workflow_integration.html
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
triggered_at: str = Field(
|
||||||
|
xml_attribute=True,
|
||||||
|
description=textwrap.dedent(
|
||||||
|
"""
|
||||||
|
The date and time a token got triggered the last time.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_human_readable_string(self) -> str:
|
||||||
|
"""
|
||||||
|
Render the object as a human readable string.
|
||||||
|
"""
|
||||||
|
from ..output import KeyValueTable
|
||||||
|
|
||||||
|
table = KeyValueTable()
|
||||||
|
table.add("ID", str(self.id))
|
||||||
|
table.add("String", self.string, color="bold")
|
||||||
|
table.add("Operation", self.kind)
|
||||||
|
table.add("Description", self.description)
|
||||||
|
table.add("Project", self.project)
|
||||||
|
table.add("Package", self.package)
|
||||||
|
table.add("Triggered at", self.triggered_at)
|
||||||
|
return f"{table}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def do_list(cls, apiurl: str, user: str):
|
||||||
|
from ..util.xml import ET
|
||||||
|
|
||||||
|
url_path = ["person", user, "token"]
|
||||||
|
url_query = {}
|
||||||
|
response = cls.xml_request("GET", apiurl, url_path, url_query)
|
||||||
|
root = ET.parse(response).getroot()
|
||||||
|
assert root.tag == "directory"
|
||||||
|
result = []
|
||||||
|
for node in root:
|
||||||
|
result.append(cls.from_xml(node, apiurl=apiurl))
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cmd_create(
|
||||||
|
cls,
|
||||||
|
apiurl: str,
|
||||||
|
user: str,
|
||||||
|
*,
|
||||||
|
operation: Optional[str] = None,
|
||||||
|
project: Optional[str] = None,
|
||||||
|
package: Optional[str] = None,
|
||||||
|
scm_token: Optional[str] = None,
|
||||||
|
):
|
||||||
|
if operation == "workflow" and not scm_token:
|
||||||
|
raise ValueError('``operation`` = "workflow" requires ``scm_token``')
|
||||||
|
|
||||||
|
url_path = ["person", user, "token"]
|
||||||
|
url_query = {
|
||||||
|
"cmd": "create",
|
||||||
|
"operation": operation,
|
||||||
|
"project": project,
|
||||||
|
"package": package,
|
||||||
|
"scm_token": scm_token,
|
||||||
|
}
|
||||||
|
response = cls.xml_request("POST", apiurl, url_path, url_query)
|
||||||
|
return Status.from_file(response, apiurl=apiurl)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def do_delete(cls, apiurl: str, user: str, token: str):
|
||||||
|
url_path = ["person", user, "token", token]
|
||||||
|
url_query = {}
|
||||||
|
response = cls.xml_request("DELETE", apiurl, url_path, url_query)
|
||||||
|
return Status.from_file(response, apiurl=apiurl)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def do_trigger(
|
||||||
|
cls,
|
||||||
|
apiurl: str,
|
||||||
|
token: str,
|
||||||
|
*,
|
||||||
|
operation: Optional[str] = None,
|
||||||
|
project: Optional[str] = None,
|
||||||
|
package: Optional[str] = None,
|
||||||
|
):
|
||||||
|
if operation:
|
||||||
|
url_path = ["trigger", operation]
|
||||||
|
else:
|
||||||
|
url_path = ["trigger"]
|
||||||
|
|
||||||
|
url_query = {
|
||||||
|
"project": project,
|
||||||
|
"package": package,
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/octet-stream",
|
||||||
|
"Authorization": f"Token {token}",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = cls.xml_request("POST", apiurl, url_path, url_query, headers=headers)
|
||||||
|
return Status.from_file(response, apiurl=apiurl)
|
Loading…
x
Reference in New Issue
Block a user