Skip to content

Commit

Permalink
Merge pull request #139 from larsewi/get-set-input
Browse files Browse the repository at this point in the history
CFE-4070: Added commands `cfbs get-input` and `cfbs set-input`
  • Loading branch information
olehermanse authored Nov 3, 2022
2 parents 5dbcf89 + 4a69a9c commit 2e9eb80
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 0 deletions.
94 changes: 94 additions & 0 deletions cfbs/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import copy
import logging as log
import json
from collections import OrderedDict
from cfbs.args import get_args

from cfbs.utils import (
Expand Down Expand Up @@ -1000,3 +1001,96 @@ def input_command(args, input_from="cfbs input"):
files_to_commit.append(input_path)
config.save()
return Result(0, do_commit, None, files_to_commit)


def set_input_command(name, infile):
config = CFBSConfig.get_instance()
module = config.get_module_from_build(name)
if module is None:
log.error("Module '%s' not found" % name)
return 1

spec = module.get("input")
if spec is None:
log.error("Module '%s' does not accept input" % name)
return 1
log.debug("Input spec for module '%s': %s" % (name, pretty(spec)))

try:
data = json.load(infile, object_pairs_hook=OrderedDict)
except json.decoder.JSONDecodeError as e:
log.error("Error reading json from stdin: %s" % e)
return 1
log.debug("Input data for module '%s': %s" % (name, pretty(data)))

def _compare_dict(a, b, ignore={}):
assert isinstance(a, dict) and isinstance(b, dict)
if set(a.keys()) != set(b.keys()) - ignore:
return False
# Avoid code duplication by converting the values of the two dicts
# into two lists in the same order and compare the lists instead
keys = a.keys()
return _compare_list([a[key] for key in keys], [b[key] for key in keys])

def _compare_list(a, b):
assert isinstance(a, list) and isinstance(b, list)
if len(a) != len(b):
return False
for x, y in zip(a, b):
if type(x) != type(y):
return False
if isinstance(x, dict) and not _compare_dict(x, y):
return False
if isinstance(x, list) and not _compare_list(x, y):
return False
if x != y:
return False
return True

for a, b in zip(spec, data):
if (
not isinstance(a, dict)
or not isinstance(b, dict)
or not _compare_dict(a, b, ignore={"response"})
):
log.error(
"Input data for module '%s' does not conform with input definition"
% name
)
return 1

path = os.path.join(name, "input.json")
log.debug("Writing json to file '%s'" % path)
write_json(path, data)

return 0


def get_input_command(name, outfile):
config = CFBSConfig.get_instance()
module = config.get_module_from_build(name)
if module is None:
module = config.index.get_module_object(name)
if module is None:
log.error("Module '%s' not found" % name)
return 1

if "input" not in module:
log.error("Module '%s' does not accept input" % name)
return 1

path = os.path.join(name, "input.json")
data = read_json(path)
if data is None:
log.debug("Loaded input from module '%s' definition" % name)
data = module["input"]
else:
log.debug("Loaded input from '%s'" % path)

data = pretty(data) + "\n"
try:
outfile.write(data)
except OSError as e:
log.error("Failed to write json: %s" % e)
return 1
return 0
35 changes: 35 additions & 0 deletions cfbs/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
__copyright__ = ["Northern.tech AS"]

import logging as log
import sys

from cfbs.version import string as version
from cfbs.utils import user_error, is_cfbs_repo
Expand Down Expand Up @@ -119,6 +120,40 @@ def main() -> int:
return commands.update_command(args.args)
if args.command == "input":
return commands.input_command(args.args)
if args.command in ("set-input", "get-input"):
filename = "stdin" if args.command == "set-input" else "stdout"
if len(args.args) <= 0:
log.error(
"Missing required arguments <module> and <filename (or - for %s)>"
% filename
)
return 1
if len(args.args) == 1:
log.error("Missing required argument <filename (or - for %s)>" % filename)
return 1
if len(args.args) > 2:
log.error(
"Too many arguments: expected <module> and <filename (or - for %s)>"
% filename
)
return 1

module, filename = args.args[0], args.args[1]

if filename == "-":
file = sys.stdin if args.command == "set-input" else sys.stdout
else:
try:
file = open(filename, "r" if args.command == "set-input" else "w")
except OSError as e:
log.error("Can't open '%s': %s" % (filename, e))
return 1
try:
if args.command == "set-input":
return commands.set_input_command(module, file)
return commands.get_input_command(module, file)
finally:
file.close()

print_help()
user_error("Command '%s' not found" % args.command)
163 changes: 163 additions & 0 deletions tests/shell/030_get_set_input.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
set -e
set -x
cd tests/
mkdir -p ./tmp/
cd ./tmp/
touch cfbs.json && rm cfbs.json
rm -rf .git
rm -rf create-single-file

echo '{
"build": [
{
"name": "create-single-file",
"input": [
{
"type": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?"
}
]
}
]
}' > cfbs.json

# Igor asks for input
cfbs get-input create-single-file - > actual.output
echo '[
{
"type": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?"
}
]' > expected.output
diff actual.output expected.output

# Igor adds response with some php magic
echo '[
{
"type": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?",
"response": "/tmp/test-1.txt"
}
]' | cfbs set-input create-single-file -

# Igor asks for input again
cfbs get-input create-single-file - > actual.output
echo '[
{
"type": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?",
"response": "/tmp/test-1.txt"
}
]' > expected.output
diff actual.output expected.output

# Igor changes the response to something else
echo '[
{
"type": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?",
"response": "/tmp/test-2.txt"
}
]' | cfbs set-input create-single-file -

# Igor asks for input once again
cfbs get-input create-single-file actual.output
echo '[
{
"type": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?",
"response": "/tmp/test-2.txt"
}
]' > expected.output
diff actual.output expected.output

# Igor changes the wrong value
echo '[
{
"type": "string",
"variable": "bogus",
"label": "Filename",
"question": "What file should this module create?",
"response": "/tmp/test-2.txt"
}
]' > igors-input.json
! cfbs set-input create-single-file igors-input.json

# Now Igor instead changes a key
echo '[
{
"doofus": "string",
"variable": "filename",
"label": "Filename",
"question": "What file should this module create?",
"response": "/tmp/test-2.txt"
}
]' > igors-input.json
! cfbs set-input create-single-file igors-input.json

# Igor changes the order but that's all right
echo '[
{
"variable": "filename",
"type": "string",
"label": "Filename",
"response": "/tmp/test-3.txt",
"question": "What file should this module create?"
}
]' | cfbs set-input create-single-file -

# Igor asks for input and now the order is different
cfbs get-input create-single-file actual.output
echo '[
{
"variable": "filename",
"type": "string",
"label": "Filename",
"response": "/tmp/test-3.txt",
"question": "What file should this module create?"
}
]' > expected.output
diff actual.output expected.output

# Igor asks for input of a module that is not in the project
cfbs get-input delete-files@0.0.1 actual.output
echo '[
{
"type": "list",
"variable": "files",
"namespace": "delete_files",
"bundle": "delete_files",
"label": "Files",
"subtype": [
{
"key": "path",
"type": "string",
"label": "Path",
"question": "Path to file"
},
{
"key": "why",
"type": "string",
"label": "Why",
"question": "Why should this file be deleted?",
"default": "Unknown"
}
],
"while": "Specify another file you want deleted on your hosts?"
}
]' > expected.output
diff actual.output expected.output

echo "Igor is happy!"
1 change: 1 addition & 0 deletions tests/shell/all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ bash tests/shell/026_init_no_masterfiles.sh
bash tests/shell/027_init_masterfiles_version_master.sh
bash tests/shell/028_init_masterfiles_version_3.18.2.sh
bash tests/shell/029_init_masterfiles_version_3.18.1-1.sh
bash tests/shell/030_get_set_input.sh

echo "All cfbs shell tests completed successfully!"

0 comments on commit 2e9eb80

Please sign in to comment.