Skip to content
Open
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
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ You can also use dynamic values from the datastore. See the
is serialized as JSON.
* ``kv.grep_object`` - Find datastore items which name matches the provided query
amd deserialize their values from JSON serialized objects.

* ``kv.entry.get`` - Retrieve entry in a JSON serialized object from the
datastore. An entry is a standard JSON object with properties.
Fails if the datastore key does not exist.
* ``kv.entry.set_property`` - Set a property in the named entry
of a JSON serialized object. Then, serialize and store the updated object.
The property's value may be any json-serializable type.
Fails if the datastore key does not exist. A coordination backend is recommended.
* ``kv.entry.append_property`` - Add the value to the end of an array property
in the named entry of a JSON serialized object. Then, serialize and store the
updated object. The property must be an array. The value to append can be anything.
Fails if the datastore key does not exist. A coordination backend is recommended.
* ``kv.entry.delete_property`` - Delete a property from a named entry of a JSON
serialized object in the datastore. If the entry is empty, delete it as well.
Fails if the datastore key does not exist. A coordination backend is recommended.

Note: ``kv.set`` and ``kv.get`` actions support compressing value before
storing it in a datastore and decompressing it when retrieving it from
Expand Down
57 changes: 57 additions & 0 deletions actions/kv_entry_append_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import json

from st2common.services import coordination as coordination_service

from lib.action import St2BaseAction

__all__ = ["St2KVPEntryAppendPropertyAction"]


class St2KVPEntryAppendPropertyAction(St2BaseAction):
# noinspection PyShadowingBuiltins
def run(self, key, entry, property, value):
with coordination_service.get_coordinator().get_lock("st2.kv_entry." + key):
# get and deserialize object or fail.
_key = self.client.keys.get_by_name(key, decrypt=False)

if not _key:
raise Exception("Key does not exist in datastore")

# optimistically try to decode a json value
try:
value = json.loads(value)
except (TypeError, ValueError):
# assume it is either already decoded (TypeError)
# or it is a plain string (ValueError)
# (malformed JSON objects/arrays will be strings)
pass

deserialized = json.loads(_key.value)

# update or insert object.entry.property
_entry = deserialized.get(entry, {})
_property = _entry.get(property, [])
try:
_property.append(value)
except AttributeError:
raise Exception(
"Cannot append. Property {}.{}.{} is not an array!".format(
key, entry, property
)
)

_entry[property] = _property
deserialized[entry] = _entry

# re-serialize and save
serialized = json.dumps(deserialized)
kvp = self._kvp(name=key, value=serialized)
kvp.id = key

self.client.keys.update(kvp)
response = {
"key": key,
"entry_name": entry,
"entry": _entry,
}
return response
32 changes: 32 additions & 0 deletions actions/kv_entry_append_property.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: 'kv.entry.append_property'
enabled: true
description: |
Add a value to the end of an array property under a named entry (sub-object)
of a serialized object in kv datastore.
Fails if the key does not exist in the datastore, as a property inside it can't be appended to yet.
The kv object uses this structure: { entry: { property: [value, value], ...}, ...}
This action does not support encrypted datastore objects.

runner_type: python-script
entry_point: kv_entry_append_property.py
parameters:
key:
required: true
type: string
position: 0
entry:
description: name of the key's entry (or fallback entry)
required: true
type: string
position: 1
property:
description: name of the entry's property to append to (property should have an array value)
required: true
type: string
position: 2
value:
description: the value may be any json-serializable type (string, number, array, object)
required: true
# type: any
position: 3
48 changes: 48 additions & 0 deletions actions/kv_entry_delete_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import json

from st2common.services import coordination as coordination_service

from lib.action import St2BaseAction

__all__ = ["St2KVPEntryDeletePropertyAction"]


class St2KVPEntryDeletePropertyAction(St2BaseAction):
# noinspection PyShadowingBuiltins
def run(self, key, entry, property):
with coordination_service.get_coordinator().get_lock("st2.kv_entry." + key):
# get and deserialize object or fail.
_key = self.client.keys.get_by_name(key, decrypt=False)

if not _key:
raise Exception("Key does not exist in datastore")

deserialized = json.loads(_key.value)

# delete object.entry.property
_entry = deserialized.get(entry, {})
try:
del _entry[property]
except KeyError:
pass

# delete object.entry if entry is empty
if not _entry:
try:
del deserialized[entry]
except KeyError:
pass

# re-serialize and save
serialized = json.dumps(deserialized)
kvp = self._kvp(name=key, value=serialized)
kvp.id = key

self.client.keys.update(kvp)
response = {
"key": key,
"entry_name": entry,
"entry": _entry,
"entry_deleted": not _entry,
}
return response
28 changes: 28 additions & 0 deletions actions/kv_entry_delete_property.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: 'kv.entry.delete_property'
enabled: true
description: |
Delete a property (if it exists) under a named entry (sub-object) of a
serialized object in kv datastore. If the entry is empty after deleting
the property, this deletes the entry as well.
Fails if the key does not exist in the datastore.
The kv object uses this structure: { entry: { property: value, ... }, entry: {}, ...}
This action does not support encrypted datastore objects.

runner_type: python-script
entry_point: kv_entry_delete_property.py
parameters:
key:
required: true
type: string
position: 0
entry:
description: name of the key's entry (or fallback entry)
required: true
type: string
position: 1
property:
description: name of the entry's property to delete
required: true
type: string
position: 2
23 changes: 23 additions & 0 deletions actions/kv_entry_get.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import json

from lib.action import St2BaseAction

__all__ = ["St2KVPEntryGetAction"]


class St2KVPEntryGetAction(St2BaseAction):
# noinspection PyShadowingBuiltins
def run(self, key, entry, fallback):
# get and deserialize object or fail.
_key = self.client.keys.get_by_name(key, decrypt=False)

if not _key:
raise Exception("Key does not exist in datastore")

deserialized = json.loads(_key.value)

# try get object.entry.property
_entry = deserialized.get(fallback, {})
_entry.update(deserialized.get(entry, {}))

return _entry
29 changes: 29 additions & 0 deletions actions/kv_entry_get.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: 'kv.entry.get'
enabled: true
description: |
Get a named entry (sub-object) of a serialized object in kv datastore
merged on top of the fallback entry (ie the fallback provides defaults).
Fails if the key does not exist in the datastore.
Does not fail if the entry or fallback is missing.
The kv object uses this structure: { fallback: { property: value, ... }, entry: {}, ...}
This action does not support encrypted datastore objects.

runner_type: python-script
entry_point: kv_entry_get.py
parameters:
key:
required: true
type: string
position: 0
entry:
description: name of the key's entry
required: true
type: string
position: 1
fallback:
description: name of the key's fallback entry
required: false
type: string
default: default
position: 2
47 changes: 47 additions & 0 deletions actions/kv_entry_set_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import json

from st2common.services import coordination as coordination_service

from lib.action import St2BaseAction

__all__ = ["St2KVPEntrySetPropertyAction"]


class St2KVPEntrySetPropertyAction(St2BaseAction):
# noinspection PyShadowingBuiltins
def run(self, key, entry, property, value):
with coordination_service.get_coordinator().get_lock("st2.kv_entry." + key):
# get and deserialize object or fail.
_key = self.client.keys.get_by_name(key, decrypt=False)

if not _key:
raise Exception("Key does not exist in datastore")

# optimistically try to decode a json value
try:
value = json.loads(value)
except (TypeError, ValueError):
# assume it is either already decoded (TypeError)
# or it is a plain string (ValueError)
# (malformed JSON objects/arrays will be strings)
pass

deserialized = json.loads(_key.value)

# update or insert object.entry.property
_entry = deserialized.get(entry, {})
_entry[property] = value
deserialized[entry] = _entry

# re-serialize and save
serialized = json.dumps(deserialized)
kvp = self._kvp(name=key, value=serialized)
kvp.id = key

self.client.keys.update(kvp)
response = {
"key": key,
"entry_name": entry,
"entry": _entry,
}
return response
31 changes: 31 additions & 0 deletions actions/kv_entry_set_property.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
name: 'kv.entry.set_property'
enabled: true
description: |
Insert or update a property under a named entry (sub-object) of a serialized object in kv datastore.
Fails if the key does not exist in the datastore, as a property inside it can't be inserted/updated yet.
The kv object uses this structure: { entry: { property: value, ... }, entry: {}, ...}
This action does not support encrypted datastore objects.

runner_type: python-script
entry_point: kv_entry_set_property.py
parameters:
key:
required: true
type: string
position: 0
entry:
description: name of the key's entry (or fallback entry)
required: true
type: string
position: 1
property:
description: name of the entry's property to set
required: true
type: string
position: 2
value:
description: the property value may be any json-serializable type (string, number, array, object)
required: true
# type: any
position: 3
2 changes: 1 addition & 1 deletion pack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
ref: st2
name: st2
description: StackStorm utility actions and aliases
version: 1.3.1
version: 1.4.0
python_versions:
- "2"
- "3"
Expand Down