Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Simon János
/
orchestrator
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
f02141dd
authored
Nov 19, 2016
by
Simon János
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
initial rest api implementation
parent
b927b8e6
Pipeline
#291
passed with stage
in 52 seconds
Changes
6
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
299 additions
and
4 deletions
+299
-4
README.md
+23
-2
orchestrator/api/__init__.py
+0
-0
orchestrator/api/stacks.py
+100
-0
setup.py
+7
-2
tests/test_api.py
+166
-0
tox.ini
+3
-0
No files found.
README.md
View file @
f02141dd
Circle Orchestrator
\ No newline at end of file
## Circle Orchestrator
### How to build
#### Build requirements
*
python 2.7
*
python 3.4
*
tox
#### Build
```
$ cd orchestrator
$ tox
```
### How to run
```
$ .tox/py27/bin/orchestrator
```
or optionally
```
$ .tox/py27/bin/orchestrator --http-host=0.0.0.0 --http-port=8080
```
orchestrator/api/__init__.py
0 → 100644
View file @
f02141dd
orchestrator/api/stacks.py
0 → 100644
View file @
f02141dd
import
json
import
sys
from
wsgiref
import
simple_server
import
falcon
from
oslo_config
import
cfg
from
oslo_config.cfg
import
StrOpt
,
IntOpt
from
oslo_log
import
log
from
orchestrator.model.resources
import
ResourceGroup
,
InvalidResourceException
,
Resource
HTTP_SERVER_OPTS
=
[
StrOpt
(
'host'
,
default
=
'127.0.0.1'
),
IntOpt
(
'port'
,
default
=
8000
)
]
cfg
.
CONF
.
register_cli_opts
(
HTTP_SERVER_OPTS
,
'http'
)
log
.
register_options
(
cfg
.
CONF
)
log
.
setup
(
cfg
.
CONF
,
'orchestrator'
)
class
Stack
(
object
):
def
__init__
(
self
):
self
.
logger
=
log
.
getLogger
()
self
.
stacks
=
{}
def
on_get
(
self
,
_request
,
response
,
stack_id
=
None
):
if
stack_id
is
not
None
:
if
stack_id
in
self
.
stacks
:
response
.
body
=
json
.
dumps
(
self
.
stacks
[
stack_id
],
default
=
Resource
.
json_encoder
)
else
:
raise
falcon
.
HTTPNotFound
()
response
.
body
=
json
.
dumps
(
list
(
self
.
stacks
.
values
()),
default
=
Resource
.
json_encoder
)
def
on_post
(
self
,
request
,
response
):
if
request
.
content_length
:
req
=
request
.
stream
.
read
()
.
decode
(
'utf8'
)
stack
=
ResourceGroup
(
json
.
loads
(
req
))
self
.
stacks
[
stack
.
id
]
=
stack
response
.
body
=
str
(
stack
)
else
:
raise
falcon
.
HTTPBadRequest
(
'Empty request received'
)
def
on_put
(
self
,
request
,
response
,
stack_id
):
if
stack_id
in
self
.
stacks
.
keys
():
update_body
=
request
.
stream
.
read
()
.
decode
(
'utf8'
)
updated_stack
=
json
.
loads
(
update_body
)
diff
=
self
.
stacks
[
stack_id
]
.
diff
(
ResourceGroup
(
updated_stack
),
flat_leaves
=
False
)
response
.
body
=
json
.
dumps
(
diff
,
default
=
Resource
.
json_encoder
)
self
.
stacks
[
stack_id
]
=
updated_stack
else
:
raise
falcon
.
HTTPNotFound
()
def
on_delete
(
self
,
_request
,
_response
,
stack_id
):
if
stack_id
in
self
.
stacks
.
keys
():
del
self
.
stacks
[
stack_id
]
else
:
raise
falcon
.
HTTPNotFound
()
class
LoggingMiddleware
(
object
):
logger
=
log
.
getLogger
()
def
process_request
(
self
,
request
,
*
_
):
self
.
logger
.
info
(
'Request received:
%
s
%
s'
,
request
.
method
,
request
.
path
)
def
process_response
(
self
,
_
,
response
,
*
__
):
if
response
.
body
:
self
.
logger
.
info
(
'Sending response (
%
s):
%
s'
,
response
.
status
,
response
.
body
)
def
handle_method_not_allowed
(
*
args
):
params
=
args
[
3
]
raise
falcon
.
HTTPMethodNotAllowed
([
'GET'
,
'PUT'
,
'DELETE'
]
if
params
else
[
'GET'
,
'POST'
])
def
handle_bad_request
(
*
args
):
exception
=
args
[
0
]
raise
falcon
.
HTTPBadRequest
(
description
=
str
(
exception
))
def
create_app
():
stack_controller
=
Stack
()
app
=
falcon
.
API
(
middleware
=
[
LoggingMiddleware
()])
app
.
add_route
(
'/stacks'
,
stack_controller
)
app
.
add_route
(
'/stacks/{stack_id}'
,
stack_controller
)
app
.
add_error_handler
(
TypeError
,
handle_method_not_allowed
)
app
.
add_error_handler
(
InvalidResourceException
,
handle_bad_request
)
return
app
def
main
(
args
=
None
):
cfg
.
CONF
(
args
[
1
:]
if
args
else
None
)
app
=
create_app
()
httpd
=
simple_server
.
make_server
(
cfg
.
CONF
.
http
.
host
,
cfg
.
CONF
.
http
.
port
,
app
)
httpd
.
serve_forever
()
if
__name__
==
'__main__'
:
main
(
sys
.
argv
)
setup.py
View file @
f02141dd
from
setuptools
import
setup
from
setuptools
import
setup
,
find_packages
setup
(
name
=
"orchestrator"
,
...
...
@@ -8,5 +8,10 @@ setup(
description
=
"Orchestrator for Circle cloud"
,
license
=
"GPLv3"
,
url
=
"http://circlecloud.org"
,
packages
=
[
'orchestrator'
,
'tests'
]
packages
=
find_packages
(),
entry_points
=
{
'console_scripts'
:
[
'orchestrator = orchestrator.api.stacks:main'
]
}
)
tests/test_api.py
0 → 100644
View file @
f02141dd
import
json
from
falcon
import
HTTP_OK
,
HTTP_METHOD_NOT_ALLOWED
,
HTTP_BAD_REQUEST
,
HTTP_NOT_FOUND
from
falcon.testing
import
TestCase
from
orchestrator.api
import
stacks
class
StacksControllerTest
(
TestCase
):
def
setUp
(
self
):
super
(
StacksControllerTest
,
self
)
.
setUp
()
self
.
app
=
stacks
.
create_app
()
def
test_stacks_api_allowed_methods_on_endpoints
(
self
):
self
.
assertEqual
(
HTTP_OK
,
self
.
simulate_get
(
'/stacks'
)
.
status
)
self
.
assertEqual
(
HTTP_NOT_FOUND
,
self
.
simulate_put
(
'/stacks/42'
)
.
status
)
self
.
assertEqual
(
HTTP_NOT_FOUND
,
self
.
simulate_delete
(
'/stacks/42'
)
.
status
)
self
.
assertEqual
(
HTTP_NOT_FOUND
,
self
.
simulate_get
(
'/stacks/42'
)
.
status
)
self
.
assertEqual
(
HTTP_BAD_REQUEST
,
self
.
simulate_post
(
'/stacks'
)
.
status
)
self
.
assertEqual
(
HTTP_METHOD_NOT_ALLOWED
,
self
.
simulate_post
(
'/stacks/42'
)
.
status
)
self
.
assertEqual
(
HTTP_METHOD_NOT_ALLOWED
,
self
.
simulate_put
(
'/stacks'
)
.
status
)
self
.
assertEqual
(
HTTP_METHOD_NOT_ALLOWED
,
self
.
simulate_delete
(
'/stacks'
)
.
status
)
def
test_stacks_api_create
(
self
):
# given
stack_request_body
=
{
'id'
:
'root'
,
'type'
:
'group'
,
'resources'
:
[
{
'id'
:
'instance-1'
,
'type'
:
'instance'
},
{
'id'
:
'level-1'
,
'type'
:
'group'
,
'resources'
:
[
{
'id'
:
'instance-2'
,
'type'
:
'instance'
},
{
'id'
:
'instance-3'
,
'type'
:
'instance'
}
]
}
]
}
# when
result
=
self
.
simulate_post
(
'/stacks'
,
body
=
json
.
dumps
(
stack_request_body
))
# then
self
.
assertEqual
(
sort_resources
(
stack_request_body
),
sort_resources
(
result
.
json
))
def
test_stacks_api_create_with_empty_json
(
self
):
# given
empty_request_body
=
'{}'
# when
result
=
self
.
simulate_post
(
'/stacks'
,
body
=
empty_request_body
,
headers
=
{
'content-type'
:
'application/json'
})
# then
self
.
assertIn
(
'id'
,
result
.
json
.
keys
())
self
.
assertIn
(
'type'
,
result
.
json
.
keys
())
self
.
assertIn
(
'resources'
,
result
.
json
.
keys
())
self
.
assertEqual
(
'group'
,
result
.
json
[
'type'
])
self
.
assertEqual
([],
result
.
json
[
'resources'
])
def
test_stacks_api_create_with_empty_body
(
self
):
# when
result
=
self
.
simulate_post
(
'/stacks'
,
headers
=
{
'content-type'
:
'application/json'
})
# then
self
.
assertEqual
(
HTTP_BAD_REQUEST
,
result
.
status
)
def
test_stacks_api_create_invalid_input
(
self
):
# given
invalid_request_bodies
=
[
json
.
dumps
({
'invalid'
:
'value'
}),
json
.
dumps
({
'resources'
:
[{
'invalid'
:
'value'
}]})
]
for
body
in
invalid_request_bodies
:
# when
result
=
self
.
simulate_post
(
'/stacks'
,
body
=
body
,
headers
=
{
'content-type'
:
'application/json'
})
# then
self
.
assertEqual
(
HTTP_BAD_REQUEST
,
result
.
status
)
def
test_stacks_api_update
(
self
):
# given
stack_id
=
'some_resource_group'
resource_id
=
'new_resource'
group1
=
{
'id'
:
stack_id
,
'type'
:
'group'
,
'resources'
:
[]}
resource
=
{
'id'
:
resource_id
,
'type'
:
'instance'
}
group2
=
{
'id'
:
stack_id
,
'type'
:
'group'
,
'resources'
:
[
resource
]}
expected_diff
=
{
'added'
:
{
'
%
s.
%
s'
%
(
stack_id
,
resource_id
):
resource
},
'removed'
:
{}
}
# when
self
.
simulate_post
(
'/stacks'
,
body
=
json
.
dumps
(
group1
))
put_result
=
self
.
simulate_put
(
'/stacks/
%
s'
%
stack_id
,
body
=
json
.
dumps
(
group2
))
get_result
=
self
.
simulate_get
(
'/stacks'
)
# then
self
.
assertEqual
(
expected_diff
,
put_result
.
json
)
self
.
assertIn
(
group2
,
get_result
.
json
)
def
test_stacks_api_delete
(
self
):
# given
resource_id
=
'some_resource'
resource
=
{
'id'
:
resource_id
,
'type'
:
'instance'
}
# when
self
.
simulate_post
(
'/stacks'
,
body
=
json
.
dumps
(
resource
))
list_result_before_delete
=
self
.
simulate_get
(
'/stacks'
)
delete_result
=
self
.
simulate_delete
(
'/stacks/
%
s'
%
resource_id
)
list_result_after_delete
=
self
.
simulate_get
(
'/stacks'
)
# then
self
.
assertEqual
([
resource
],
list_result_before_delete
.
json
)
self
.
assertEqual
(
HTTP_OK
,
delete_result
.
status
)
self
.
assertEqual
([],
list_result_after_delete
.
json
)
def
test_stacks_api_get_list
(
self
):
# given
resource1
=
{
'id'
:
'some_resource'
,
'type'
:
'instance'
}
resource2
=
{
'id'
:
'some_other_resource'
,
'type'
:
'instance'
}
# when
self
.
simulate_post
(
'/stacks'
,
body
=
json
.
dumps
(
resource1
))
self
.
simulate_post
(
'/stacks'
,
body
=
json
.
dumps
(
resource2
))
result
=
self
.
simulate_get
(
'/stacks'
)
.
json
# then
self
.
assertEqual
(
2
,
len
(
result
))
self
.
assertIn
(
resource1
,
result
)
self
.
assertIn
(
resource2
,
result
)
def
test_stacks_api_get_one
(
self
):
# given
resource_id
=
'some_resource'
resource
=
{
'id'
:
resource_id
,
'type'
:
'instance'
}
# when
self
.
simulate_post
(
'/stacks'
,
body
=
json
.
dumps
(
resource
))
result
=
self
.
simulate_get
(
'/stacks/
%
s'
%
resource_id
)
.
json
# then
self
.
assertEqual
([
resource
],
result
)
def
sort_resources
(
group
):
try
:
for
resource
in
group
[
'resources'
]:
sort_resources
(
resource
)
group
[
'resources'
]
.
sort
(
key
=
lambda
r
:
r
[
'id'
])
except
(
KeyError
,
AttributeError
):
pass
tox.ini
View file @
f02141dd
...
...
@@ -5,6 +5,9 @@ envlist = py27, py34
production
=
enum34
voluptuous
oslo.config
oslo.log
falcon
; celery
test
=
pytest
...
...
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