Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
slapos.core
Commits
5028473c
Commit
5028473c
authored
Jul 28, 2021
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
slap/request: emit a warning when requesting with parameters not matching schema
parent
80cf8cf1
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
208 additions
and
1 deletion
+208
-1
slapos/slap/slap.py
slapos/slap/slap.py
+25
-1
slapos/tests/test_slap.py
slapos/tests/test_slap.py
+183
-0
No files found.
slapos/slap/slap.py
View file @
5028473c
...
...
@@ -40,14 +40,17 @@ import os
import
logging
import
re
from
functools
import
wraps
import
warnings
import
json
import
jsonschema
import
six
from
.exception
import
ResourceNotReady
,
ServerError
,
NotFoundError
,
\
ConnectionError
from
.hateoas
import
SlapHateoasNavigator
,
ConnectionHelper
from
slapos.util
import
loads
,
dumps
,
bytes2str
,
unicode2str
,
xml2dict
,
dict2xml
,
calculate_dict_hash
from
slapos.util
import
(
SoftwareReleaseSchema
,
bytes2str
,
calculate_dict_hash
,
dict2xml
,
dumps
,
loads
,
unicode2str
,
xml2dict
)
from
xml.sax
import
saxutils
from
zope.interface
import
implementer
...
...
@@ -87,6 +90,27 @@ class SlapRequester(SlapDocument):
"""
def
_requestComputerPartition
(
self
,
request_dict
):
try
:
SoftwareReleaseSchema
(
request_dict
[
'software_release'
],
request_dict
[
'software_type'
]
).
validateInstanceParameterDict
(
loads
(
request_dict
[
'partition_parameter_xml'
]))
except
jsonschema
.
ValidationError
as
e
:
warnings
.
warn
(
"Request parameters do not validate against schema definition:
\
n
{e}"
.
format
(
e
=
e
),
UserWarning
,
)
except
Exception
as
e
:
# note that we intentionally catch wide exceptions, so that if anything
# is wrong with fetching the schema or the schema itself this does not
# prevent users from requesting instances.
warnings
.
warn
(
"Error validating request parameters against schema definition:
\
n
{e.__class__.__name__} {e}"
.
format
(
e
=
e
),
UserWarning
,
)
try
:
xml
=
self
.
_connection_helper
.
POST
(
'requestComputerPartition'
,
data
=
request_dict
)
except
ResourceNotReady
:
...
...
slapos/tests/test_slap.py
View file @
5028473c
...
...
@@ -32,6 +32,7 @@ from six.moves.urllib import parse
from
six
import
PY3
import
tempfile
import
logging
import
warnings
from
collections
import
OrderedDict
import
httmock
...
...
@@ -872,6 +873,188 @@ class TestComputerPartition(SlapMixin):
content_list
=
f
.
read
().
splitlines
()
self
.
assertEqual
(
sorted
(
content_list
),
[
'myref'
,
'mysecondref'
])
def
test_request_validate_request_parameter
(
self
):
def
handler
(
url
,
req
):
if
url
.
path
.
endswith
(
'/software.cfg.json'
):
return
json
.
dumps
(
{
"name"
:
"Test Software"
,
"description"
:
"Dummy software for Test"
,
"serialisation"
:
"json-in-xml"
,
"software-type"
:
{
'default'
:
{
"title"
:
"Default"
,
"description"
:
"Default type"
,
"request"
:
"instance-default-input-schema.json"
,
"response"
:
"instance-default-output-schema.json"
,
"index"
:
0
},
}
})
if
url
.
path
.
endswith
(
'/instance-default-input-schema.json'
):
return
json
.
dumps
(
{
"$schema"
:
"http://json-schema.org/draft-07/schema"
,
"description"
:
"Simple instance parameters schema for tests"
,
"required"
:
[
"foo"
],
"properties"
:
{
"foo"
:
{
"$ref"
:
"./schemas-definitions.json#/foo"
}
},
"type"
:
"object"
})
if
url
.
path
.
endswith
(
'/schemas-definitions.json'
):
return
json
.
dumps
({
"foo"
:
{
"type"
:
"string"
,
"const"
:
"bar"
}})
raise
ValueError
(
404
)
with
httmock
.
HTTMock
(
handler
):
with
mock
.
patch
.
object
(
warnings
,
'warn'
)
as
warn
:
cp
=
slapos
.
slap
.
ComputerPartition
(
'computer_id'
,
'partition_id'
)
cp
.
_connection_helper
=
mock
.
Mock
()
cp
.
_connection_helper
.
POST
.
side_effect
=
slapos
.
slap
.
ResourceNotReady
cp
.
request
(
'https://example.com/software.cfg'
,
'default'
,
'reference'
,
partition_parameter_kw
=
{
'foo'
:
'bar'
})
warn
.
assert_not_called
()
with
httmock
.
HTTMock
(
handler
):
with
mock
.
patch
.
object
(
warnings
,
'warn'
)
as
warn
:
cp
=
slapos
.
slap
.
ComputerPartition
(
'computer_id'
,
'partition_id'
)
cp
.
_connection_helper
=
mock
.
Mock
()
cp
.
_connection_helper
.
POST
.
side_effect
=
slapos
.
slap
.
ResourceNotReady
cp
.
request
(
'https://example.com/software.cfg'
,
'default'
,
'reference'
,
partition_parameter_kw
=
{
'foo'
:
'baz'
})
if
PY3
:
warn
.
assert_called_with
(
"Request parameters do not validate against schema definition:
\
n
"
"'bar' was expected
\
n
\
n
"
"Failed validating 'const' in schema['properties']['foo']:
\
n
"
" {'const': 'bar', 'type': 'string'}
\
n
\
n
"
"On instance['foo']:
\
n
'baz'"
,
UserWarning
)
else
:
# BBB
warn
.
assert_called_with
(
"Request parameters do not validate against schema definition:
\
n
"
"u'bar' was expected
\
n
\
n
"
"Failed validating u'const' in schema[u'properties'][u'foo']:
\
n
"
" {u'const': u'bar', u'type': u'string'}
\
n
\
n
"
"On instance[u'foo']:
\
n
'baz'"
,
UserWarning
)
def
test_request_validate_request_parameter_broken_software_release_schema
(
self
):
"""Corner case tests for incorrect software release schema, these should
not prevent the request (mostly for backward compatibility)
"""
def
wrong_software_cfg_schema
(
url
,
req
):
if
url
.
path
.
endswith
(
'/software.cfg.json'
):
return
"wrong"
raise
ValueError
(
404
)
def
wrong_instance_parameter_schema
(
url
,
req
):
if
url
.
path
.
endswith
(
'/software.cfg.json'
):
return
json
.
dumps
(
{
"name"
:
"Test Software"
,
"description"
:
"Dummy software for Test"
,
"serialisation"
:
"json-in-xml"
,
"software-type"
:
{
'default'
:
{
"title"
:
"Default"
,
"description"
:
"Default type"
,
"request"
:
"instance-default-input-schema.json"
,
"response"
:
"instance-default-output-schema.json"
,
"index"
:
0
},
}
})
if
url
.
path
.
endswith
(
'/instance-default-input-schema.json'
):
return
"wrong"
raise
ValueError
(
404
)
def
invalid_instance_parameter_schema
(
url
,
req
):
if
url
.
path
.
endswith
(
'/software.cfg.json'
):
return
json
.
dumps
(
{
"name"
:
"Test Software"
,
"description"
:
"Dummy software for Test"
,
"serialisation"
:
"json-in-xml"
,
"software-type"
:
{
'default'
:
{
"title"
:
"Default"
,
"description"
:
"Default type"
,
"request"
:
"instance-default-input-schema.json"
,
"response"
:
"instance-default-output-schema.json"
,
"index"
:
0
},
}
})
if
url
.
path
.
endswith
(
'/instance-default-input-schema.json'
):
return
json
.
dumps
(
{
"$schema"
:
"http://json-schema.org/draft-07/schema"
,
"description"
:
"Invalid json schema"
,
"required"
:
{
"wrong"
:
True
},
"properties"
:
{
[
"wrong schema"
]
},
"type"
:
"object"
})
raise
ValueError
(
404
)
def
broken_reference
(
url
,
req
):
if
url
.
path
.
endswith
(
'/software.cfg.json'
):
return
json
.
dumps
(
{
"name"
:
"Test Software"
,
"description"
:
"Dummy software for Test"
,
"serialisation"
:
"json-in-xml"
,
"software-type"
:
{
'default'
:
{
"title"
:
"Default"
,
"description"
:
"Default type"
,
"request"
:
"instance-default-input-schema.json"
,
"response"
:
"instance-default-output-schema.json"
,
"index"
:
0
},
}
})
if
url
.
path
.
endswith
(
'/instance-default-input-schema.json'
):
return
json
.
dumps
(
{
"$schema"
:
"http://json-schema.org/draft-07/schema"
,
"description"
:
"Simple instance parameters schema for tests"
,
"required"
:
[
"foo"
],
"properties"
:
{
"foo"
:
{
"$ref"
:
"broken"
}
},
"type"
:
"object"
})
raise
ValueError
(
404
)
for
handler
,
warning_expected
in
(
(
broken_reference
,
True
),
(
wrong_software_cfg_schema
,
False
),
(
wrong_instance_parameter_schema
,
True
),
(
invalid_instance_parameter_schema
,
True
),
):
with
httmock
.
HTTMock
(
handler
):
with
mock
.
patch
.
object
(
warnings
,
'warn'
)
as
warn
:
cp
=
slapos
.
slap
.
ComputerPartition
(
'computer_id'
,
'partition_id'
)
cp
.
_connection_helper
=
mock
.
Mock
()
cp
.
_connection_helper
.
POST
.
side_effect
=
slapos
.
slap
.
ResourceNotReady
cp
.
request
(
'https://example.com/software.cfg'
,
'default'
,
'reference'
,
partition_parameter_kw
=
{
'foo'
:
'bar'
})
if
warning_expected
:
warn
.
assert_called
()
else
:
warn
.
assert_not_called
()
def
_test_new_computer_partition_state
(
self
,
state
):
"""
Helper method to automate assertions of failing states on new Computer
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment