def test_nested_lists_with_value_file(tmp_path): p = tmp_path / "test.yaml" p.write_text(yaml.dump({"a": ["b", "c"]})) # We replace resolved = resolve(["a.0=new"], [str(p)]) assert resolved == {"a": ["new", "c"]} # We append resolved = resolve(["a.2=d"], [str(p)]) assert resolved == {"a": ["b", "c", "d"]} # The index '3' is out of bound (neither a replacement nor an append) with pytest.raises(ValueError): resolve(["a.3=new"], [str(p)])
def test_cli_values_value_files(): values = resolve([], [VALUES_STAGING_FILE]) with open(VALUES_STAGING_FILE) as fd: assert values == yaml.load(fd) # putting the same file twice should not change the resulting values assert resolve([], [VALUES_STAGING_FILE, VALUES_STAGING_FILE]) == values # cli values override values from files assert resolve(["env=production"], [VALUES_STAGING_FILE])["env"] == "production" # multiple values override (last wins) assert resolve(["env=1", "env=2"], [VALUES_STAGING_FILE])["env"] == "2" # nested override assert (resolve( ["nodeSelector.node-label=pool-2"], [VALUES_STAGING_FILE])["nodeSelector"]["node-label"] == "pool-2")
def test_resolve_invalid_values(values): with pytest.raises(ValueError): resolve(values, [])
def test_nested_lists(values, expected): assert resolve(values, []) == Context(*expected)
def test_valid_values(values, expected): assert resolve(values, []) == Context(*expected)
def test_value_files_override(): values = resolve([], [VALUES_STAGING_FILE]) assert values["nodeSelector"]["node-label"] == "pool-1" values = resolve([], [VALUES_STAGING_FILE, VALUES_PRODUCTION_FILE]) assert values["nodeSelector"]["node-label"] == "pool-2"
def test_nested_lists_invalid_index(): with pytest.raises(ValueError, match=".* list 'a' has not been given a value."): resolve(["a.1=v1"], [])
def test_nested_dicts(values, expected): assert resolve(values, []) == expected
def test_valid_values(values, expected): assert resolve(values, []) == expected
def cli(): """kuku: Kubernetes templating tool. Usage: kuku apply [-v] [-f <FILE>]... [-s <key=value>]... <TEMPLATES_DIR> kuku delete [-v] [-f <FILE>]... [-s <key=value>]... <TEMPLATES_DIR> kuku render [-v] [-f <FILE>]... [-s <key=value>]... <TEMPLATES_DIR> kuku (-h | --help) kuku --version Options: -h --help Show this screen. -v --verbose Dump debug info to stderr. --version Show version. -s KEY=VALUE --set KEY=VALUE Set values on the command line (accepts multiple options or separate values with commas: Ex: -s key1=val1,key2=val2). -f FILE --file FILE Specify values in a YAML file (accepts multiple options). Notes: Resolution of values: --set overrides values in --file by merging. The last value wins. """ # Parse cli arguments from docstring arguments = docopt(str(cli.__doc__), version=__version__) # Load k8s config -- needed to the correct API versions of k8s config.load_kube_config() # Find all templates try: templates = find(arguments["<TEMPLATES_DIR>"]) except ValueError as e: print(e) exit(1) raise try: # Resolve values context = resolve(arguments["--set"], arguments["--file"]) except ValueError as e: print(e) exit(1) raise # Render templates with resolved context rendering = render(context, templates) if not context.is_complete: print(f"Missing values: {context.missing_keys}") exit(1) output = dump(rendering) if arguments["render"]: # print yaml print(output) if arguments["apply"]: print("Not implemented yet. Use kuku render ... | kubectl apply -f-") exit(1) if arguments["delete"]: print("Not implemented yet. Use kuku render ... | kubectl delete -f-") exit(1) if arguments["--verbose"]: print(output, file=sys.stderr)