diff options
-rw-r--r-- | test/end-to-end/just-mr/TARGETS | 14 | ||||
-rw-r--r-- | test/end-to-end/just-mr/create_test_archives.cpp | 32 | ||||
-rw-r--r-- | test/end-to-end/just-mr/install-roots-symlinks.sh | 230 | ||||
-rw-r--r-- | test/end-to-end/just-mr/install-roots.sh | 35 | ||||
-rw-r--r-- | test/end-to-end/just-mr/just-mr.test.sh | 67 |
5 files changed, 336 insertions, 42 deletions
diff --git a/test/end-to-end/just-mr/TARGETS b/test/end-to-end/just-mr/TARGETS index 2f44f962..7cdc9ba5 100644 --- a/test/end-to-end/just-mr/TARGETS +++ b/test/end-to-end/just-mr/TARGETS @@ -60,6 +60,13 @@ , "deps": [["end-to-end", "mr-tool-under-test"], ["end-to-end", "tool-under-test"]] } +, "install-roots-symlinks": + { "type": ["@", "rules", "shell/test", "script"] + , "name": ["install-roots-symlinks"] + , "test": ["install-roots-symlinks.sh"] + , "deps": + [["end-to-end", "mr-tool-under-test"], ["end-to-end", "tool-under-test"]] + } , "TESTS": { "type": "install" , "tainted": ["test"] @@ -71,7 +78,12 @@ , { "type": "if" , "cond": {"type": "var", "name": "TEST_BOOTSTRAP_JUST_MR"} , "then": [] - , "else": ["just_mr_mp", "git-tree-verbosity", "defaults"] + , "else": + [ "install-roots-symlinks" + , "just_mr_mp" + , "git-tree-verbosity" + , "defaults" + ] } ] } diff --git a/test/end-to-end/just-mr/create_test_archives.cpp b/test/end-to-end/just-mr/create_test_archives.cpp index 2f4327b7..3fa6759a 100644 --- a/test/end-to-end/just-mr/create_test_archives.cpp +++ b/test/end-to-end/just-mr/create_test_archives.cpp @@ -35,16 +35,27 @@ Structure of content tree: +--root +--bar +--foo + +--foo_l + +--baz_l +--baz +--bar +--foo + +--foo_l + +foo_l is symlink "baz/foo_l" [non-upwards, pointing to file] +baz_l is symlink "baz" [non-upwards, pointing to tree] +baz/foo_l is symlink "../foo_l" [upwards & confined, pointing to symlink] */ -auto const kExpected = filetree_t{{"root", {"", ObjectType::Tree}}, - {"root/foo", {"foo", ObjectType::File}}, - {"root/bar", {"bar", ObjectType::File}}, - {"root/baz", {"", ObjectType::Tree}}, - {"root/baz/foo", {"foo", ObjectType::File}}, - {"root/baz/bar", {"bar", ObjectType::File}}}; +auto const kExpected = + filetree_t{{"root", {"", ObjectType::Tree}}, + {"root/foo", {"foo", ObjectType::File}}, + {"root/bar", {"bar", ObjectType::File}}, + {"root/baz", {"", ObjectType::Tree}}, + {"root/baz_l", {"baz", ObjectType::Symlink}}, + {"root/foo_l", {"foo", ObjectType::Symlink}}, + {"root/baz/foo", {"foo", ObjectType::File}}, + {"root/baz/bar", {"bar", ObjectType::File}}, + {"root/baz/foo_l", {"../foo_l", ObjectType::Symlink}}}; void create_files(std::filesystem::path const& destDir = ".") { for (auto const& [path, file] : kExpected) { @@ -66,6 +77,15 @@ void create_files(std::filesystem::path const& destDir = ".") { std::exit(1); }; } break; + case ObjectType::Symlink: { + if (not FileSystemManager::CreateSymlink(content, + destDir / path)) { + Logger::Log(LogLevel::Error, + "Could not create test symlink at path {}", + (destDir / path).string()); + std::exit(1); + }; + } break; default: { Logger::Log(LogLevel::Error, "File system failure in creating test archive"); diff --git a/test/end-to-end/just-mr/install-roots-symlinks.sh b/test/end-to-end/just-mr/install-roots-symlinks.sh new file mode 100644 index 00000000..aa3ae836 --- /dev/null +++ b/test/end-to-end/just-mr/install-roots-symlinks.sh @@ -0,0 +1,230 @@ +#!/bin/sh +# Copyright 2023 Huawei Cloud Computing Technology Co., Ltd. +# +# 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. + + +### +# This test extends install-roots-basic with 'special' pragma use-cases +## + +set -eu + +readonly JUST="${PWD}/bin/tool-under-test" +readonly JUST_MR="${PWD}/bin/mr-tool-under-test" +readonly DISTDIR="${TEST_TMPDIR}/distfiles" +readonly LBR="${TEST_TMPDIR}/local-build-root" + +readonly INSTALL_DIR_RESOLVED="${TEST_TMPDIR}/installation-target-resolved" +readonly INSTALL_DIR_UNRESOLVED="${TEST_TMPDIR}/installation-target-unresolved" +readonly INSTALL_DIR_SPECIAL_IGNORE="${TEST_TMPDIR}/installation-target-special-ignore" +readonly INSTALL_DIR_SPECIAL_PARTIAL="${TEST_TMPDIR}/installation-target-special-partial" +readonly INSTALL_DIR_SPECIAL_COMPLETE="${TEST_TMPDIR}/installation-target-special-complete" + +readonly TEST_DATA="The content of the data file in foo" +readonly TEST_DIRS="bar/baz" +readonly DATA_PATH="bar/baz/data.txt" +readonly NON_UPWARDS_LINK_PATH="bar/baz/nonupwards" +readonly NON_UPWARDS_LINK_TARGET="data.txt" +readonly UPWARDS_LINK_PATH="bar/upwards" +readonly UPWARDS_LINK_TARGET="../bar/baz" +readonly INDIRECT_LINK_PATH="bar/indirect" +readonly INDIRECT_LINK_TARGET="upwards/data.txt" + +mkdir -p "${DISTDIR}" + +### +# Set up sample archives +## + +mkdir -p "foo/${TEST_DIRS}" +echo {} > foo/TARGETS +echo -n "${TEST_DATA}" > "foo/${DATA_PATH}" + +# add resolvable non-upwards symlink +ln -s "${NON_UPWARDS_LINK_TARGET}" "foo/${NON_UPWARDS_LINK_PATH}" +tar cf "${DISTDIR}/foo-1.2.3_v1.tar" foo \ + --sort=name --owner=root:0 --group=root:0 --mtime='UTC 1970-01-01' 2>&1 +foocontent_v1=$(git hash-object "${DISTDIR}/foo-1.2.3_v1.tar") +echo "Foo archive v1 has content ${foocontent_v1}" + +# get Git-tree of foo subdir with only non-upwards symlinks +cd foo +( + git init \ + && git config user.email "nobody@example.org" \ + && git config user.name "Nobody" \ + && git add . \ + && git commit -m "test" --date="1970-01-01T00:00Z" +) +UNRESOLVED_TREE=$(git log -n 1 --format='%T') +echo "Foo archive v1 has unresolved tree ${UNRESOLVED_TREE}" +rm -rf .git +cd .. + +# now add also a resolvable upwards symlink +ln -s "${UPWARDS_LINK_TARGET}" "foo/${UPWARDS_LINK_PATH}" +# ...and a resolvable non-upwards symlink pointing to it +ln -s "${INDIRECT_LINK_TARGET}" "foo/${INDIRECT_LINK_PATH}" +tar cf "${DISTDIR}/foo-1.2.3_v2.tar" foo \ + --sort=name --owner=root:0 --group=root:0 --mtime='UTC 1970-01-01' 2>&1 +foocontent_v2=$(git hash-object "${DISTDIR}/foo-1.2.3_v2.tar") +echo "Foo archive v2 has content ${foocontent_v2}" +rm -rf foo + +### +# Setup sample repository config +## + +touch ROOT +cat > repos.json <<EOF +{ "repositories": + { "foo_v1_resolved": + { "repository": + { "type": "archive" + , "content": "${foocontent_v1}" + , "fetch": "http://non-existent.example.org/foo-1.2.3_v1.tar" + , "subdir": "foo" + , "pragma": {"special": "resolve-completely"} + } + } + , "foo_v2_ignore_special": + { "repository": + { "type": "archive" + , "content": "${foocontent_v2}" + , "fetch": "http://non-existent.example.org/foo-1.2.3_v2.tar" + , "subdir": "foo" + , "pragma": {"special": "ignore"} + } + } + , "foo_v2_resolve_partially": + { "repository": + { "type": "archive" + , "content": "${foocontent_v2}" + , "fetch": "http://non-existent.example.org/foo-1.2.3_v2.tar" + , "subdir": "foo" + , "pragma": {"special": "resolve-partially"} + } + } + , "foo_v2_resolve_completely": + { "repository": + { "type": "archive" + , "content": "${foocontent_v2}" + , "fetch": "http://non-existent.example.org/foo-1.2.3_v2.tar" + , "subdir": "foo" + , "pragma": {"special": "resolve-completely"} + } + } + , "": + { "repository": {"type": "file", "path": "."} + , "bindings": + { "foo_v1_resolved": "foo_v1_resolved" + , "foo_v2_ignore_special": "foo_v2_ignore_special" + , "foo_v2_resolve_partially": "foo_v2_resolve_partially" + , "foo_v2_resolve_completely": "foo_v2_resolve_completely" + } + } + } +} +EOF + +# Compute the repository configuration +CONF=$("${JUST_MR}" --norc --local-build-root "${LBR}" --distdir "${DISTDIR}" setup) +cat "${CONF}" +echo + + +### +# Test archives with non-upwards symlinks +## + +echo === root with non-upwards symlinks === + +# Read tree from repository configuration +TREE=$(jq -r '.repositories.foo_v1_resolved.workspace_root[1]' "${CONF}") +echo Tree is "${TREE}" + +# As the tree is known to just (in the git CAS), we should be able to install +# it with install-cas +"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_RESOLVED}" \ + "${TREE}::t" 2>&1 +test "$(cat "${INSTALL_DIR_RESOLVED}/${DATA_PATH}")" = "${TEST_DATA}" +# non-upwards symlink is resolved +test "$(cat "${INSTALL_DIR_RESOLVED}/${NON_UPWARDS_LINK_PATH}")" = "${TEST_DATA}" + +# A resolved tree should add to CAS also its unresolved version; in our case, +# the unresolved tree has only non-upwards symlinks, so it can be installed +"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_UNRESOLVED}" \ + "${UNRESOLVED_TREE}::t" 2>&1 +test "$(cat "${INSTALL_DIR_UNRESOLVED}/${DATA_PATH}")" = "${TEST_DATA}" +test "$(readlink "${INSTALL_DIR_UNRESOLVED}/${NON_UPWARDS_LINK_PATH}")" = "${NON_UPWARDS_LINK_TARGET}" + + +### +# Test archives with confined symlinks (upwards and non-upwards) +## + +echo === root with ignored special entries === + +# Read tree from repository configuration +TREE=$(jq -r '.repositories.foo_v2_ignore_special.workspace_root[1]' "${CONF}") +echo Tree is "${TREE}" + +# As the tree is known to just (in the git CAS), we should be able to install +# it with install-cas +"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_SPECIAL_IGNORE}" \ + "${TREE}::t" 2>&1 +test "$(cat "${INSTALL_DIR_SPECIAL_IGNORE}/${DATA_PATH}")" = "${TEST_DATA}" +[ ! -e "${INSTALL_DIR_SPECIAL_IGNORE}/${NON_UPWARDS_LINK_PATH}" ] # symlink should be missing +[ ! -e "${INSTALL_DIR_SPECIAL_IGNORE}/${UPWARDS_LINK_PATH}" ] # symlink should be missing +[ ! -e "${INSTALL_DIR_SPECIAL_IGNORE}/${INDIRECT_LINK_PATH}" ] # symlink should be missing + + +echo === root with partially resolved symlinks === + +# Read tree from repository configuration +TREE=$(jq -r '.repositories.foo_v2_resolve_partially.workspace_root[1]' "${CONF}") +echo Tree is "${TREE}" + +# As the tree is known to just (in the git CAS), we should be able to install +# it with install-cas +"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_SPECIAL_PARTIAL}" \ + "${TREE}::t" 2>&1 +test "$(cat "${INSTALL_DIR_SPECIAL_PARTIAL}/${DATA_PATH}")" = "${TEST_DATA}" +# non-upwards link remains as-is +test "$(readlink "${INSTALL_DIR_SPECIAL_PARTIAL}/${NON_UPWARDS_LINK_PATH}")" = "${NON_UPWARDS_LINK_TARGET}" +# upwards link is resolved, in this case to a tree +[ -d "${INSTALL_DIR_SPECIAL_PARTIAL}/${UPWARDS_LINK_PATH}" ] +test "$(cat "${INSTALL_DIR_SPECIAL_PARTIAL}/${UPWARDS_LINK_PATH}/data.txt")" = "${TEST_DATA}" +# indirect link is non-upwards, so it should remain as-is +test "$(readlink "${INSTALL_DIR_SPECIAL_PARTIAL}/${INDIRECT_LINK_PATH}")" = "${INDIRECT_LINK_TARGET}" + + +echo === root with fully resolved symlinks === + +# Read tree from repository configuration +TREE=$(jq -r '.repositories.foo_v2_resolve_completely.workspace_root[1]' "${CONF}") +echo Tree is "${TREE}" + +# As the tree is known to just (in the git CAS), we should be able to install +# it with install-cas +"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_SPECIAL_COMPLETE}" \ + "${TREE}::t" 2>&1 +test "$(cat "${INSTALL_DIR_SPECIAL_COMPLETE}/${DATA_PATH}")" = "${TEST_DATA}" +# all links get resolved +test "$(cat "${INSTALL_DIR_SPECIAL_COMPLETE}/${NON_UPWARDS_LINK_PATH}")" = "${TEST_DATA}" +[ -d "${INSTALL_DIR_SPECIAL_COMPLETE}/${UPWARDS_LINK_PATH}" ] +test "$(cat "${INSTALL_DIR_SPECIAL_COMPLETE}/${UPWARDS_LINK_PATH}/data.txt")" = "${TEST_DATA}" +test "$(cat "${INSTALL_DIR_SPECIAL_COMPLETE}/${INDIRECT_LINK_PATH}")" = "${TEST_DATA}" + +echo OK diff --git a/test/end-to-end/just-mr/install-roots.sh b/test/end-to-end/just-mr/install-roots.sh index aab0c90b..6ff66401 100644 --- a/test/end-to-end/just-mr/install-roots.sh +++ b/test/end-to-end/just-mr/install-roots.sh @@ -20,8 +20,7 @@ readonly JUST="${PWD}/bin/tool-under-test" readonly JUST_MR="${PWD}/bin/mr-tool-under-test" readonly DISTDIR="${TEST_TMPDIR}/distfiles" readonly LBR="${TEST_TMPDIR}/local-build-root" -readonly INSTALL_DIR_1="${TEST_TMPDIR}/installation-target-1" -readonly INSTALL_DIR_2="${TEST_TMPDIR}/installation-target-2" +readonly INSTALL_DIR="${TEST_TMPDIR}/installation-target" readonly TEST_DATA="The content of the data file in foo" readonly TEST_PATH="bar/baz" @@ -51,18 +50,9 @@ cat > repos.json <<EOF , "subdir": "foo" } } - , "foo_ignore_special": - { "repository": - { "type": "archive" - , "content": "${foocontent}" - , "fetch": "http://non-existent.example.org/foo-1.2.3.tar" - , "subdir": "foo" - , "pragma": {"special": "ignore"} - } - } , "": { "repository": {"type": "file", "path": "."} - , "bindings": {"foo": "foo", "foo_ignore_special": "foo_ignore_special"} + , "bindings": {"foo": "foo"} } } } @@ -73,30 +63,15 @@ CONF=$("${JUST_MR}" --norc --local-build-root "${LBR}" --distdir "${DISTDIR}" se cat "${CONF}" echo -echo === regular root === - # Read tree from repository configuration TREE=$(jq -r '.repositories.foo.workspace_root[1]' "${CONF}") echo Tree is "${TREE}" # As the tree is known to just (in the git CAS), we should be able to install # it with install-cas -"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_1}" \ - "${TREE}::t" 2>&1 -test "$(cat "${INSTALL_DIR_1}/${TEST_PATH}/data.txt")" = "${TEST_DATA}" -test "$(readlink "${INSTALL_DIR_1}/${TEST_PATH}/link")" = "${LINK_TARGET}" - -echo === ignore_special root === - -# Read tree from repository configuration -TREE=$(jq -r '.repositories.foo_ignore_special.workspace_root[1]' "${CONF}") -echo Tree is "${TREE}" - -# As the tree is known to just (in the git CAS), we should be able to install -# it with install-cas -"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR_2}" \ +"${JUST}" install-cas --local-build-root "${LBR}" -o "${INSTALL_DIR}" \ "${TREE}::t" 2>&1 -test "$(cat "${INSTALL_DIR_2}/${TEST_PATH}/data.txt")" = "${TEST_DATA}" -[ ! -e "${INSTALL_DIR_2}/${TEST_PATH}/link" ] # symlink should be missing +test "$(cat "${INSTALL_DIR}/${TEST_PATH}/data.txt")" = "${TEST_DATA}" +test "$(readlink "${INSTALL_DIR}/${TEST_PATH}/link")" = "${LINK_TARGET}" echo OK diff --git a/test/end-to-end/just-mr/just-mr.test.sh b/test/end-to-end/just-mr/just-mr.test.sh index cdd0ace4..d82e5309 100644 --- a/test/end-to-end/just-mr/just-mr.test.sh +++ b/test/end-to-end/just-mr/just-mr.test.sh @@ -53,10 +53,15 @@ readonly TGZ_REPO_SHA256=$(sha256sum tgz_repo.tar.gz | awk '{print $1}') readonly TGZ_REPO_SHA512=$(sha512sum tgz_repo.tar.gz | awk '{print $1}') echo "Set up file repos" +# add files mkdir -p "${FILE_ROOT}/test_dir1" echo test > "${FILE_ROOT}/test_dir1/test_file" mkdir -p "${FILE_ROOT}/test_dir2/test_dir3" echo test > "${FILE_ROOT}/test_dir2/test_dir3/test_file" +# add resolvable non-upwards symlink +ln -s test_file "${FILE_ROOT}/test_dir1/nonupwards" +# add resolvable upwards symlink +ln -s ../test_dir3/test_file "${FILE_ROOT}/test_dir2/test_dir3/upwards" echo "Set up local git repo" # NOTE: Libgit2 has no support for Git bundles yet @@ -67,6 +72,7 @@ mkdir -p foo/bar echo foo > foo.txt echo bar > foo/bar.txt echo baz > foo/bar/baz.txt +ln -s dummy foo/link EOF # set up git repo ( @@ -122,6 +128,20 @@ cat > test-repos.json <<EOF , "sha256": "${ZIP_REPO_SHA256}" , "sha512": "${ZIP_REPO_SHA512}" , "subdir": "root" + , "pragma": {"special": "resolve-partially"} + } + , "bindings": {"tgz_repo": "tgz_repo", "distdir_repo": "distdir_repo"} + } + , "zip_repo_resolved": + { "repository": + { "type": "zip" + , "content": "${ZIP_REPO_CONTENT}" + , "distfile": "zip_repo.zip" + , "fetch": "http://127.0.0.1:${port_num}/zip_repo.zip" + , "sha256": "${ZIP_REPO_SHA256}" + , "sha512": "${ZIP_REPO_SHA512}" + , "subdir": "root" + , "pragma": {"special": "resolve-completely"} } , "bindings": {"tgz_repo": "tgz_repo", "distdir_repo": "distdir_repo"} } @@ -134,6 +154,7 @@ cat > test-repos.json <<EOF , "sha256": "${TGZ_REPO_SHA256}" , "sha512": "${TGZ_REPO_SHA512}" , "subdir": "root/baz" + , "pragma": {"special": "ignore"} } , "bindings": {"git_repo": "git_repo"} } @@ -146,6 +167,16 @@ cat > test-repos.json <<EOF , "subdir": "foo" } } + , "git_repo_ignore_special": + { "repository": + { "type": "git" + , "repository": "${GIT_ROOT}" + , "branch": "test" + , "commit": "${GIT_REPO_COMMIT}" + , "subdir": "foo" + , "pragma": {"special": "ignore"} + } + } , "git_tree_repo": { "repository": { "type": "git tree" @@ -153,26 +184,52 @@ cat > test-repos.json <<EOF , "cmd": ["sh", "${SERVER_ROOT}/bin/git_dir_setup.sh"] } } + , "git_tree_repo_ignore_special": + { "repository": + { "type": "git tree" + , "id": "${GIT_TREE_ID}" + , "cmd": ["sh", "${SERVER_ROOT}/bin/git_dir_setup.sh"] + , "pragma": {"special": "ignore"} + } + } , "file_repo1": { "repository": { "type": "file" , "path": "${FILE_ROOT}/test_dir1" + , "pragma": {"to_git": true} } } - , "file_repo2": + , "file_repo2_ignore_special": { "repository": { "type": "file" , "path": "${FILE_ROOT}/test_dir2" - , "pragma": {"to_git": true} + , "pragma": {"special": "ignore"} + } + } + , "file_repo2_resolve_partially": + { "repository": + { "type": "file" + , "path": "${FILE_ROOT}/test_dir2" + , "pragma": {"special": "resolve_partially"} + } + } + , "file_repo2_resolve_completely": + { "repository": + { "type": "file" + , "path": "${FILE_ROOT}/test_dir2" + , "pragma": {"special": "resolve_completely"} } - , "bindings": {"file_repo1": "file_repo1"} } , "distdir_repo": { "repository": { "type": "distdir" - , "repositories": ["git_repo", "zip_repo", "file_repo2"] + , "repositories": ["git_repo", "zip_repo", "file_repo1"] + } + , "bindings": + { "file_repo2_ignore_special": "file_repo2_ignore_special" + , "file_repo2_resolve_partially": "file_repo2_resolve_partially" + , "file_repo2_resolve_completely": "file_repo2_resolve_completely" } - , "bindings": {"file_repo1": "file_repo1"} } } } |