Skip to content

Commit

Permalink
Merge pull request #41 from vmware/topic/okurth/support-sha256
Browse files Browse the repository at this point in the history
add option to support sha256 checksum type
  • Loading branch information
oliverkurth committed Nov 14, 2023
2 parents 1a5004d + 523d592 commit ba658b0
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 9 deletions.
25 changes: 16 additions & 9 deletions ova-compose/ova-compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,13 +1076,14 @@ def write_xml(self, ovf_file=None):


@staticmethod
def _get_sha512(filename):
def _get_hash(filename, hash_type):
hash = hashlib.new(hash_type)
with open(filename, "rb") as f:
hash = hashlib.sha512(f.read()).hexdigest();
return hash
hash.update(f.read())
return hash.hexdigest()


def write_manifest(self, ovf_file=None, mf_file=None):
def write_manifest(self, ovf_file=None, mf_file=None, hash_type="sha512"):
if ovf_file == None:
ovf_file = f"{self.name}.ovf"
if mf_file == None:
Expand All @@ -1093,9 +1094,9 @@ def write_manifest(self, ovf_file=None, mf_file=None):
filenames.append(file.path)
with open(mf_file, "wt") as f:
for fname in filenames:
hash = OVF._get_sha512(fname)
hash = OVF._get_hash(fname, hash_type)
fname = os.path.basename(fname)
f.write(f"SHA512({fname})= {hash}\n")
f.write(f"{hash_type.upper()}({fname})= {hash}\n")


def usage():
Expand All @@ -1106,6 +1107,7 @@ def usage():
print(" -o, --output-file <file> output file or directory name")
print(" -f, --format ova|ovf|dir output format")
print(" -m, --manifest create manifest file along with ovf (default true for output formats ova and dir)")
print(" --checksum-type sha256|sha512 set the checksum type for the manifest. Must be sha256 or sha512.")
print(" -q quiet mode")
print(" -h print help")
print("")
Expand Down Expand Up @@ -1145,9 +1147,10 @@ def main():
do_quiet = False
do_manifest = False
params = {}
checksum_type = "sha512"

try:
opts, args = getopt.getopt(sys.argv[1:], 'f:hi:mo:q', longopts=['format=', 'input-file=', 'manifest', 'output-file=', 'param='])
opts, args = getopt.getopt(sys.argv[1:], 'f:hi:mo:q', longopts=['format=', 'input-file=', 'manifest', 'output-file=', 'param=', 'checksum-type='])
except:
print ("invalid option")
sys.exit(2)
Expand All @@ -1161,6 +1164,8 @@ def main():
output_format = a
elif o in ['-m', '--manifest']:
do_manifest = True
elif o in ['--checksum-type']:
checksum_type = a
elif o in ['--param']:
k,v = a.split('=', maxsplit=1)
params[k] = yaml.safe_load(v)
Expand All @@ -1175,6 +1180,8 @@ def main():
assert config_file != None, "no input file specified"
assert output_file != None, "no output file/directory specified"

assert checksum_type in ["sha512", "sha256"], f"checksum-type '{checksum_type}' is invalid"

if config_file != None:
f = open(config_file, 'r')

Expand Down Expand Up @@ -1212,7 +1219,7 @@ def main():
ovf_file = output_file
ovf.write_xml(ovf_file=ovf_file)
if do_manifest:
ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file)
ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file, hash_type=checksum_type)
elif output_format == "ova" or output_format == "dir":
pwd = os.getcwd()
tmpdir = tempfile.mkdtemp(prefix=f"{basename}-", dir=pwd)
Expand All @@ -1227,7 +1234,7 @@ def main():
os.symlink(os.path.join(pwd, file.path), dst)
all_files.append(dst)

ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file)
ovf.write_manifest(ovf_file=ovf_file, mf_file=mf_file, hash_type=checksum_type)

if output_format == "ova":
ret = subprocess.check_call(["tar", "--format=ustar", "-h",
Expand Down
115 changes: 115 additions & 0 deletions pytest/test_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Copyright (c) 2023 VMware, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the “License”); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an “AS IS” BASIS, without warranties or
# conditions of any kind, EITHER EXPRESS OR IMPLIED. See the License for the
# specific language governing permissions and limitations under the License.

import glob
import hashlib
import os
import pytest
import shutil
import subprocess
import yaml
import xmltodict


THIS_DIR = os.path.dirname(os.path.abspath(__file__))
OVA_COMPOSE = os.path.join(THIS_DIR, "..", "ova-compose", "ova-compose.py")

VMDK_CONVERT=os.path.join(THIS_DIR, "..", "build", "vmdk", "vmdk-convert")

CONFIG_DIR=os.path.join(THIS_DIR, "configs")

WORK_DIR=os.path.join(os.getcwd(), "pytest-tmp")


@pytest.fixture(scope='module', autouse=True)
def setup_test():
os.makedirs(WORK_DIR, exist_ok=True)

process = subprocess.run(["dd", "if=/dev/zero", "of=dummy.img", "bs=1024", "count=1024"], cwd=WORK_DIR)
assert process.returncode == 0

process = subprocess.run([VMDK_CONVERT, "dummy.img", "dummy.vmdk"], cwd=WORK_DIR)
assert process.returncode == 0

yield
shutil.rmtree(WORK_DIR)


def check_mf(mf_path, hash_type, work_dir=WORK_DIR):
with open(mf_path, "rt") as f:
for line in f:
left, hash_mf = line.split("=")
hash_mf = hash_mf.strip()

assert left.startswith(hash_type.upper())

filename = left[len(hash_type):].strip("()")
hash = hashlib.new(hash_type)
with open(os.path.join(work_dir, filename), "rb") as f:
hash.update(f.read())

assert hash.hexdigest() == hash_mf


@pytest.mark.parametrize("hash_type", [None, "sha256", "sha512"])
def test_ovf_manifest(hash_type):
in_yaml = os.path.join(CONFIG_DIR, "basic.yaml")
basename = os.path.basename(in_yaml.rsplit(".", 1)[0])
out_ovf = os.path.join(WORK_DIR, f"{basename}.ovf")
out_mf = os.path.join(WORK_DIR, f"{basename}.mf")

args = [OVA_COMPOSE, "-i", in_yaml, "-o", out_ovf, "-m"]
if hash_type is not None:
args += ["--checksum-type", hash_type]
else:
hash_type = "sha512"

process = subprocess.run(args, cwd=WORK_DIR)
assert process.returncode == 0

assert os.path.isfile(out_mf)

check_mf(out_mf, hash_type)


@pytest.mark.parametrize("hash_type", [None, "sha256", "sha512"])
def test_ova_manifest(hash_type):
in_yaml = os.path.join(CONFIG_DIR, "basic.yaml")
basename = os.path.basename(in_yaml.rsplit(".", 1)[0])
out_ova = os.path.join(WORK_DIR, f"{basename}.ova")
out_mf = os.path.join(WORK_DIR, f"{basename}.mf")

args = [OVA_COMPOSE, "-i", in_yaml, "-o", out_ova]
if hash_type is not None:
args += ["--checksum-type", hash_type]
else:
hash_type = "sha512"

process = subprocess.run(args, cwd=WORK_DIR)
assert process.returncode == 0

subprocess.run(["tar", "xf", out_ova], cwd=WORK_DIR)

check_mf(out_mf, hash_type)


def test_manifest_invalid_checksum_type():
in_yaml = os.path.join(CONFIG_DIR, "basic.yaml")
basename = os.path.basename(in_yaml.rsplit(".", 1)[0])
out_ovf = os.path.join(WORK_DIR, f"{basename}.ovf")
out_mf = os.path.join(WORK_DIR, f"{basename}.mf")

args = [OVA_COMPOSE, "-i", in_yaml, "-o", out_ovf, "-m", "--checksum-type", "foobar"]
process = subprocess.run(args, cwd=WORK_DIR)
assert process.returncode != 0

0 comments on commit ba658b0

Please sign in to comment.