Skip to content

Commit 1c3ac5e

Browse files
committed
Fixed json promise type
Signed-off-by: Victor Moene <[email protected]>
1 parent 1b37ef9 commit 1c3ac5e

File tree

2 files changed

+39
-35
lines changed

2 files changed

+39
-35
lines changed

promise-types/json/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Promise type for manipulating `json` files
55
| Name | Type | Description |
66
|---------------|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
77
| `object` | `data container` | json object type. It can also be json arrays |
8-
| `array` | `slist`, `rlist`, `ilist`, `data array` | json array type. `slist`, `rlist` and `ilist` will only create string arrays. To create array of other types, use `data array` |
8+
| `array` | `data array` | json array type |
99
| `string` | `string` | json string type |
1010
| `number` | `real`, `int` | json number type |
1111
| `primitive` | `string` | Primitives are values that are either `"true"`, `"false"` or `"null"` in json |
@@ -64,9 +64,9 @@ And the content of `/tmp/oldfile.json` will become:
6464

6565
If the field doesn't exist, it is appended. If it already exists, its data will be overwritten.
6666

67-
### Writing types
67+
### Writing arrays
6868

69-
In order to write compound type such as arrays containg booleans, numbers, etc... One has to use the `data container` type in the policy.
69+
In order to write compound type such as arrays containg booleans, numbers, etc... One has to use the `data` type in the policy.
7070

7171
To see what happens if we use
7272

@@ -84,12 +84,12 @@ bundle agent main
8484
8585
json:
8686
"/tmp/example_1.json:json_data"
87-
array => "$(json_data)";
87+
array => "@(json_data)";
8888
8989
"/tmp/example_2.json:real_list"
90-
array => "$(real_list)";
90+
array => "@(real_list)";
9191
"/tmp/example_2.json:bool_list"
92-
array => "$(bool_list)";
92+
array => "@(bool_list)";
9393
}
9494
```
9595

promise-types/json/json_promise_type.py

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import json
3+
import tempfile
34

45
from cfengine import PromiseModule, ValidationError, Result, AttributeObject
56

@@ -48,22 +49,23 @@ def validate_promise(self, promiser, attributes, metadata):
4849
if present_types == 0:
4950
raise ValidationError(
5051
"The promiser '{}' is missing a type attribute. The possible types are {}".format(
51-
promiser, str(self.types)
52+
promiser, ", ".join(["'{}'".format(t) for t in self.types])
5253
)
5354
)
5455
elif len(present_types) > 1:
5556
raise ValidationError(
56-
"The attributes {} cannot be together".format(str(self.types))
57+
"The attributes {} cannot be together".format(
58+
", ".join(["'{}'".format(t) for t in self.types])
59+
)
5760
)
5861

59-
filename, _, _ = promiser.partition(":")
60-
if os.path.exists(filename) and not os.path.isfile(filename):
61-
raise ValidationError(
62-
"'{}' already exists and is not a file".format(filename)
63-
)
62+
filename, colon, field = promiser.partition(":")
6463

65-
if not filename.endswith(".json"):
66-
raise ValidationError("'{}' is not a json file")
64+
if not filename:
65+
raise ValidationError("Invalid syntax: missing file name")
66+
67+
if colon and not field:
68+
raise ValidationError("Invalid syntax: field specified but empty")
6769

6870
model = self.create_attribute_object(attributes)
6971
if (
@@ -77,14 +79,16 @@ def validate_promise(self, promiser, attributes, metadata):
7779

7880
if model.array:
7981
if isinstance(model.array, str):
80-
if not is_json_serializable(model.array):
82+
try:
83+
array = json.loads(model.array)
84+
85+
except:
8186
raise ValidationError(
82-
"'{}' is not a valid list".format(model.array)
87+
"'{}' cannot be serialized to a json array".format(model.array)
8388
)
84-
85-
if not isinstance(json.loads(model.array), list):
89+
if not isinstance(array, list):
8690
raise ValidationError(
87-
"'{}' is not a valid data".format(model.array)
91+
"'{}' is not a valid data array".format(model.array)
8892
)
8993

9094
elif not isinstance(model.array, list):
@@ -106,23 +110,18 @@ def evaluate_promise(self, promiser, attributes, metadata):
106110
model = self.create_attribute_object(attributes)
107111
filename, _, field = promiser.partition(":")
108112

113+
if os.path.exists(filename) and not os.path.isfile(filename):
114+
self.log_error("'{}' already exists and is not a file".format(filename))
115+
return Result.NOT_KEPT
116+
109117
# type conversion
110118

111119
datatype = next(t for t in self.types if t in attributes)
112120

113-
match datatype:
114-
case "object" | "array":
115-
data = (
116-
json.loads(attributes[datatype])
117-
if isinstance(attributes[datatype], str)
118-
else attributes[datatype]
119-
)
120-
case "number":
121-
data = float(model.number) if "." in model.number else int(model.number)
122-
case "primitive":
123-
data = None if model.primitive == "null" else model.primitive == "true"
124-
case _: # strings
125-
data = attributes[datatype]
121+
if isinstance(attributes[datatype], str) and not model.string:
122+
data = json.loads(attributes[datatype])
123+
else:
124+
data = attributes[datatype]
126125

127126
# json manipulation
128127

@@ -141,11 +140,16 @@ def evaluate_promise(self, promiser, attributes, metadata):
141140
Result.KEPT
142141
content = data
143142

144-
with open(filename, "w") as f:
143+
fd, tmp = tempfile.mkstemp()
144+
145+
with open(tmp, "w") as f:
145146
json.dump(content, f, indent=4)
146147

148+
os.rename(tmp, filename)
149+
os.close(fd)
150+
147151
self.log_info("Updated '{}'".format(filename))
148-
Result.REPAIRED
152+
return Result.REPAIRED
149153

150154

151155
if __name__ == "__main__":

0 commit comments

Comments
 (0)