def load_paths(self): """Add all paths to search for buildspecs. We must read configuration file and check property ``buildspec_roots`` for list of directories to search. We check all directories exist, if any fail we don't add them to path. In addition, we add the default buildspec path where we find tutorials and general tests. """ buildspec_paths = self.configuration.target_config.get( "buildspec_roots") or [] if buildspec_paths: self.roots += buildspec_paths # only load default buildspecs if 'load_default_buildspecs' set to True if self.configuration.target_config.get("load_default_buildspecs"): self.paths += BUILDSPEC_DEFAULT_PATH # for every root buildspec defined in configuration or via --root option, # we resolve path and if path exist add to self.paths. The path must be a # directory. If its file, we ignore it if self.roots: for root in self.roots: path = resolve_path(root, exist=False) if not os.path.exists(path): print(f"Path: {path} does not exist!") if is_file(path): print(f"Path: {path} must be a directory not a file") if is_dir(path): self.paths.append(path)
def discover_buildspecs_by_executor_name(executor_name): """This method discovers buildspecs by executor name, using ``--executor-name`` option from ``buildtest build`` command. This method will read BUILDSPEC_CACHE_FILE and search for ``executor`` key in buildspec recipe and match with input executor name. The return is a list of matching buildspec with executor name to process. :param executor_name: Input executor name from command line argument ``buildtest build --executor-name <name>`` :type executor_name: string :return: a list of buildspec files that match tag name :rtype: list """ if not is_file(BUILDSPEC_CACHE_FILE): raise BuildTestError( f"Cannot for buildspec cache: {BUILDSPEC_CACHE_FILE}, please run 'buildtest buildspec find' " ) with open(BUILDSPEC_CACHE_FILE, "r") as fd: cache = json.loads(fd.read()) buildspecs = [] # query all buildspecs from BUILDSPEC_CACHE_FILE for tags keyword and # if it matches input_tag we add buildspec to list for buildspecfile in cache["buildspecs"].keys(): for test in cache["buildspecs"][buildspecfile].keys(): # check if executor in buildspec matches one in argument (buildtest build --executor <EXECUTOR>) if executor_name == cache["buildspecs"][buildspecfile][test].get( "executor"): buildspecs.append(buildspecfile) return buildspecs
def discover_buildspecs_by_tags(input_tag): """This method discovers buildspecs by tags, using ``--tags`` option from ``buildtest build`` command. This method will read BUILDSPEC_CACHE_FILE and search for ``tags`` key in buildspec recipe and match with input tag. Since ``tags`` field is a list, we check if input tag is in ``list`` and if so we add the entire buildspec into a list. The return is a list of buildspec files to process. :param input_tag: Input tags from command line argument ``buildtest build --tags <tags>`` :type input_tag: str :return: a list of buildspec files that match tag name :rtype: list """ if not is_file(BUILDSPEC_CACHE_FILE): raise BuildTestError( f"Cannot for buildspec cache: {BUILDSPEC_CACHE_FILE}, please run 'buildtest buildspec find' " ) with open(BUILDSPEC_CACHE_FILE, "r") as fd: cache = json.loads(fd.read()) buildspecs = [] # query all buildspecs from BUILDSPEC_CACHE_FILE for tags keyword and # if it matches input_tag we add buildspec to list for buildspecfile in cache["buildspecs"].keys(): for test in cache["buildspecs"][buildspecfile].keys(): # if tags is not declared we set to empty list tag = cache["buildspecs"][buildspecfile][test].get("tags") or [] if input_tag in tag: buildspecs.append(buildspecfile) return buildspecs
def test_read_file(tmp_path): # testing invalid type for read_file, expects of type string. Expected return is 'None' print("Reading file with invalid type, passing 'None'") with pytest.raises(BuildTestError): read_file(None) file = os.path.join(tmp_path, "hello.txt") print(f"Checking {file} is not a file.") # ensure file is not valid assert not is_file(file) print(f"Now reading an invalid file: {file}, expecting read_file to return 'None'") # checking invalid file should report an error with pytest.raises(BuildTestError): read_file(file) with tempfile.TemporaryDirectory() as tmpdir: fname = os.path.join(tmpdir, "permission-denied.txt") msg = "hello world" write_file(fname, msg) # make permission 000 so its unreadable os.chmod(fname, 000) with pytest.raises(BuildTestError): read_file(fname) print("Reading '/etc/shadow' will raise an exception BuildTestError") # reading /etc/shadow will raise a Permission error so we catch this exception BuildTestError with pytest.raises(BuildTestError): read_file("/etc/shadow")
def test_resolve_path(): assert resolve_path("$HOME") assert resolve_path("~") random_name = "".join(random.choice(string.ascii_letters) for i in range(10)) # test a directory path that doesn't exist in $HOME with random key, but setting exist=False will return # path but doesn't mean file exists path = resolve_path(os.path.join("$HOME", random_name), exist=False) # checking if path is not file, or directory and not None. This is only valid when exist=False is set assert not is_file(path) assert not is_dir(path) assert path is not None
def build(self): """This method will build buildspec cache file. If user requests to rebuild cache we remove the file and recreate cache. If cache file exists, we simply load from cache """ # implements buildtest buildspec find --rebuild which removes cache file # before finding all buildspecs. We only remove file if file exists if self.rebuild and is_file(BUILDSPEC_CACHE_FILE): try: os.remove(BUILDSPEC_CACHE_FILE) print(f"Clearing cache file: {BUILDSPEC_CACHE_FILE}") except OSError as msg: raise BuildTestError(msg) # if cache file is not found, then we will build cache by searching # all buildspecs paths and traverse directory to find all .yml files if not is_file(BUILDSPEC_CACHE_FILE): self.build_cache() with open(BUILDSPEC_CACHE_FILE, "r") as fd: self.cache = json.loads(fd.read())
def test_read_file(tmp_path): # testing invalid type for read_file, expects of type string. Expected return is 'None' print("Reading file with invalid type, passing 'None'") with pytest.raises(BuildTestError): read_file(None) file = os.path.join(tmp_path, "hello.txt") print(f"Checking {file} is not a file.") # ensure file is not valid assert not is_file(file) print( f"Now reading an invalid file: {file}, expecting read_file to return 'None'" ) # checking invalid file should report an error with pytest.raises(BuildTestError): read_file(file) print("Reading '/etc/shadow' will raise an exception BuildTestError") # reading /etc/shadow will raise a Permission error so we catch this exception BuildTestError with pytest.raises(BuildTestError): read_file("/etc/shadow")
def load(self): """This method is responsible for loading report file. If file not found or report is empty dictionary we raise an error. The report file is loaded using ``json.loads`` and return value is a dictionary containing entire report of all tests. """ # raise error if BUILD_REPORT not found if not is_file(BUILD_REPORT): sys.exit( f"Unable to fetch report no such file found: {BUILD_REPORT}") report = None with open(BUILD_REPORT, "r") as fd: report = json.loads(fd.read()) # if report is None or issue with how json.load returns content of file we # raise error if not report: sys.exit( f"Fail to process {BUILD_REPORT} please check if file is valid json" f"or remove file") return report
def update_report(valid_builders): """This method will update BUILD_REPORT after every test run performed by ``buildtest build``. If BUILD_REPORT is not created, we will create file and update json file by extracting contents from builder.metadata :param valid_builders: builder object that were successful during build and able to execute test :type valid_builders: instance of BuilderBase (subclass) """ if not is_file(os.path.dirname(BUILD_REPORT)): create_dir(os.path.dirname(BUILD_REPORT)) # if file exists, read json file otherwise set report to empty dict try: with open(BUILD_REPORT, "r") as fd: report = json.loads(fd.read()) except OSError: report = {} for builder in valid_builders: buildspec = builder.metadata["buildspec"] name = builder.metadata["name"] entry = {} report[buildspec] = report.get(buildspec) or {} report[buildspec][name] = report.get(buildspec, {}).get(name) or [] # query over attributes found in builder.metadata, we only assign # keys that we care obout for reporting for item in [ "id", "full_id", "testroot", "testpath", "command", "outfile", "errfile", "schemafile", "executor", ]: entry[item] = builder.metadata[item] entry["tags"] = "" # convert tags to string if defined in buildspec if builder.metadata["tags"]: if isinstance(builder.metadata["tags"], list): entry["tags"] = " ".join(builder.metadata["tags"]) else: entry["tags"] = builder.metadata["tags"] # query over result attributes, we only assign some keys of interest for item in ["starttime", "endtime", "runtime", "state", "returncode"]: entry[item] = builder.metadata["result"][item] entry["output"] = builder.metadata["output"] entry["error"] = builder.metadata["error"] entry["job"] = builder.metadata.get("job") or None report[buildspec][name].append(entry) with open(BUILD_REPORT, "w") as fd: json.dump(report, fd, indent=2)
def test_checking_file(): file_name = str(uuid.uuid4()) assert not is_file(file_name) assert is_file("/bin/bash")