Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Resource limits and reserves Modules #104

Merged
merged 6 commits into from
May 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,100 @@ Manage the registry configuration for a given dokku application
state: absent
```

### dokku_resource_limit

Manage resource limits for a given dokku application

#### Parameters

|Parameter|Choices/Defaults|Comments|
|---------|----------------|--------|
|app<br /><sup>*required*</sup>||The name of the app|
|clear_before|*Choices:* <ul><li>True</li><li>**False** (default)</li></ul>|Clear all resource limits before applying|
|process_type||The process type selector|
|resources||The Resource type and quantity (required when state=present)|
|state|*Choices:* <ul><li>**present** (default)</li><li>absent</li></ul>|The state of the resource limits|

#### Example

```yaml
- name: Limit CPU and memory of a dokku app
dokku_resource_limit:
app: hello-world
resources:
cpu: 100
memory: 100

- name: name: Limit resources per process type of a dokku app
dokku_resource_limit:
app: hello-world
process_type: web
resources:
cpu: 100
memory: 100

- name: Clear limits before applying new limits
dokku_resource_limit:
app: hello-world
state: present
clear_before: True
resources:
cpu: 100
memory: 100

- name: Remove all resource limits
dokku_resource_limit:
app: hello-world
state: absent
```

### dokku_resource_reserve

Manage resource reservations for a given dokku application

#### Parameters

|Parameter|Choices/Defaults|Comments|
|---------|----------------|--------|
|app<br /><sup>*required*</sup>||The name of the app|
|clear_before|*Choices:* <ul><li>True</li><li>**False** (default)</li></ul>|Clear all reserves before apply|
|process_type||The process type selector|
|resources||The Resource type and quantity (required when state=present)|
|state|*Choices:* <ul><li>**present** (default)</li><li>absent</li></ul>|The state of the resource reservations|

#### Example

```yaml
- name: Reserve CPU and memory for a dokku app
dokku_resource_reserve:
app: hello-world
resources:
cpu: 100
memory: 100

- name: Create a reservation per process type of a dokku app
dokku_resource_reserve:
app: hello-world
process_type: web
resources:
cpu: 100
memory: 100

- name: Clear all reservations before applying
dokku_resource_reserve:
app: hello-world
state: present
clear_before: True
resources:
cpu: 100
memory: 100

- name: Remove all resource reservations
dokku_resource_reserve:
app: hello-world
state: absent
```

### dokku_service_create

Creates a given service
Expand Down
253 changes: 253 additions & 0 deletions library/dokku_resource_limit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# from ansible.module_utils.basic import *
from ansible.module_utils.basic import AnsibleModule
import subprocess
import re

DOCUMENTATION = """
---
module: dokku_resource_limit
short_description: Manage resource limits for a given dokku application
options:
app:
description:
- The name of the app
required: True
default: null
aliases: []
resources:
description:
- The Resource type and quantity (required when state=present)
required: False
default: null
aliases: []
process_type:
description:
- The process type selector
required: False
default: null
alias: []
clear_before:
description:
- Clear all resource limits before applying
required: False
default: "False"
choices: [ "True", "False" ]
aliases: []
state:
description:
- The state of the resource limits
required: False
default: present
choices: [ "present", "absent" ]
aliases: []
author: Alexandre Pavanello e Silva
requirements: [ ]

"""

EXAMPLES = """
- name: Limit CPU and memory of a dokku app
dokku_resource_limit:
app: hello-world
resources:
cpu: 100
memory: 100

- name: name: Limit resources per process type of a dokku app
dokku_resource_limit:
app: hello-world
process_type: web
resources:
cpu: 100
memory: 100

- name: Clear limits before applying new limits
dokku_resource_limit:
app: hello-world
state: present
clear_before: True
resources:
cpu: 100
memory: 100

- name: Remove all resource limits
dokku_resource_limit:
app: hello-world
state: absent
"""


def force_list(var):
if isinstance(var, list):
return var
return list(var)


def subprocess_check_output(command, split="\n"):
error = None
output = []
try:
output = subprocess.check_output(command, shell=True)
if isinstance(output, bytes):
output = output.decode("utf-8")
output = str(output).rstrip("\n")
if split is None:
return output, error

output = output.split(split)
output = force_list(filter(None, output))
output = [o.strip() for o in output]
except subprocess.CalledProcessError as e:
error = str(e)
return output, error


def dokku_resource_clear(data):
error = None
process_type = ""
if data["process_type"]:
process_type = "--process-type {0}".format(data["process_type"])
command = "dokku resource:limit-clear {0} {1}".format(process_type, data["app"])
try:
subprocess.check_call(command, shell=True)
except subprocess.CalledProcessError as e:
error = str(e)
return error


def dokku_resource_limit_report(data):

process_type = ""
if data["process_type"]:
process_type = "--process-type {0}".format(data["process_type"])
command = "dokku --quiet resource:limit {0} {1}".format(process_type, data["app"])

output, error = subprocess_check_output(command)
if error is not None:
return output, error
output = [re.sub(r"\s+", "", line) for line in output]

report = {}

for line in output:
if ":" not in line:
continue
key, value = line.split(":", 1)
report[key] = value

return report, error


def dokku_resource_limit_present(data):
is_error = True
has_changed = False
meta = {"present": False}

if "resources" not in data:
meta["error"] = "missing required arguments: resources"
return (is_error, has_changed, meta)

report, error = dokku_resource_limit_report(data)
meta["debug"] = report.keys()
if error:
meta["error"] = error
return (is_error, has_changed, meta)

for k, v in data["resources"].items():
if k not in report.keys():
is_error = True
has_changed = False
meta["error"] = "Unknown resource {0}, choose one of: {1}".format(
k, list(report.keys())
)
return (is_error, has_changed, meta)
if report[k] != str(v):
has_changed = True

if data["clear_before"] is True:

error = dokku_resource_clear(data)
if error:
meta["error"] = error
is_error = True
has_changed = False
return (is_error, has_changed, meta)
has_changed = True

if not has_changed:
meta["present"] = True
is_error = False
return (is_error, has_changed, meta)

values = []
for key, value in data["resources"].items():
values.append("--{0} {1}".format(key, value))

process_type = ""
if data["process_type"]:
process_type = "--process-type {0}".format(data["process_type"])

command = "dokku resource:limit {0} {1} {2}".format(
" ".join(values), process_type, data["app"]
)
try:
subprocess.check_call(command, shell=True)
is_error = False
has_changed = True
meta["present"] = True
except subprocess.CalledProcessError as e:
meta["error"] = str(e)
return (is_error, has_changed, meta)


def dokku_resource_limit_absent(data):
is_error = True
has_changed = False
meta = {"present": True}

error = dokku_resource_clear(data)
if error:
meta["error"] = error
is_error = True
has_changed = False
return (is_error, has_changed, meta)

is_error = False
has_changed = True
meta = {"present": False}

return (is_error, has_changed, meta)


def main():
fields = {
"app": {"required": True, "type": "str"},
"process_type": {"required": False, "type": "str"},
"resources": {"required": False, "type": "dict"},
"clear_before": {"required": False, "type": "bool"},
"state": {
"required": False,
"default": "present",
"choices": ["present", "absent"],
"type": "str",
},
}
choice_map = {
"present": dokku_resource_limit_present,
"absent": dokku_resource_limit_absent,
}

module = AnsibleModule(argument_spec=fields, supports_check_mode=False)
is_error, has_changed, result = choice_map.get(module.params["state"])(
module.params
)

if is_error:
module.fail_json(msg=result["error"], meta=result)
module.exit_json(changed=has_changed, meta=result)


if __name__ == "__main__":
main()
Loading