diff --git a/docs/Makefile b/docs/Makefile index 709c35b80..6e7d4f607 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -13,13 +13,27 @@ BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @source venv/bin/activate && \ + $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + livehtml: - @$(SPHINXAUTOBUILD) --watch ../leapp "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(0) + @source venv/bin/activate && \ + $(SPHINXAUTOBUILD) --watch ../leapp "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(0) + +# Prepare virtualenv similar to the environment used during build on Read The Docs and install required packages +env: + # ad. pip install -r doesn't really work with relative paths :), so keeping it in the project root + @PY_VER=$$(sed -n -e 's/^\s*python:\s"\(.*\)"/\1/p' ../.readthedocs.yaml); \ + echo $$PY_VER; \ + rm -fr ./venv; \ + virtualenv venv --python=python$$PY_VER && \ + source venv/bin/activate && \ + pushd .. && pip install -r requirements-docs.txt; popd # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @source venv/bin/activate && \ + $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: all help Makefile +.PHONY: all help Makefile env diff --git a/docs/source/_static/images/inplace-upgrade-workflow.svg b/docs/source/_static/images/inplace-upgrade-workflow.svg deleted file mode 100644 index d4704497f..000000000 --- a/docs/source/_static/images/inplace-upgrade-workflow.svg +++ /dev/null @@ -1,2578 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - Main Phases - - Inplace Upgrade Flow - - - Pre-FactsCollection - - - - Facts Collection - - - - Post-FactsCollection - - - - Pre-Checks - - - - Checks - - - - Post-Checks - - - - - - - - - - - - - - - - - - - - - - - - - - - Start - - - - Pre-Report - - - - Report - - - - Post-Report - - - - Pre-Download - - - - Download - - - - Post-Download - - - - Pre-InterimPreparation - - - - InterimPreparation - - - - Post-InterimPreparation - - - - Pre-InitramReboot - - - - Initram Start - - - - Post-InitramStart - - - - Pre-LateTests - - - - Late Tests - - - - Post-LateTests - - - - PrePreparation - - - - Preparation - - - - PostPreparation - - - - Pre-RPMUpgrade - - - - RPM Upgrade - - - - Post-RPMUpgrade - - - - PreApplications - - - - Applications - - - - PostApplications - - - - Pre-Third PartyApplications - - - - Third PartyApplications - - - - Post-Third PartyApplications - - - - PreFinalization - - - - Finalization - - - - PostFinalization - - - - PreReboot - - - - Reboot - - - - PostReboot - - - - Pre-FirstBoot - - - - First Boot - - - - Post-FirstBoot - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Report - - User Interaction - - - - - - - - - - - - - - - - - - - New System - Interim System (Initrd) - Old System - - - - - - - - - Pre-Target TransactionFactsCollection - - - - Target TransactionFacts Collection - - - - Post-Target TransactionFacts Collection - - - - - - - - - - - - - - - - - Pre-Target TransactionCheck - - - Target TransactionCheck - - - Post-Target TransactionCheck - - - - - - - - - - - - - - - - diff --git a/docs/source/architecture.md b/docs/source/architecture.md deleted file mode 100644 index c4df42d15..000000000 --- a/docs/source/architecture.md +++ /dev/null @@ -1,16 +0,0 @@ -# Architecture overview - -![Architecture overview](/_static/images/framework-arch-overview.png) - -There are two tools for working with the framework, the end user application `leapp` and the development utility `snactor`. The `leapp` tool is designed to run specific workflows, while the `snactor` tool can run arbitrary workflows, but also individual actors. - -A *workflow* describes what work is going to be done and when. Each workflow is made of a sequence of *phases*, which contain *actors* split into three stages - before, main, and after. Workflows, actors, and all the parts necessary for the execution are loaded from repositories. - -Each actor is executed in a forked child process to prevent the modification of the application state. All messages and logs produced by the actors are stored in the *audit database*. - -For more information about each part of the architecture, check the [terminology](terminology). - -### How is this different from Ansible? - -Leapp is message-driven. The execution of actors is dependent on the data produced by other actors running before them. This data is passed around in the form of *messages*. -This is in a contrast with Ansible where everything has to be specified up front. diff --git a/docs/source/autosectionlabelext.py b/docs/source/autosectionlabelext.py deleted file mode 100644 index a28ab7c8c..000000000 --- a/docs/source/autosectionlabelext.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.ext.autosectionlabel - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Allow reference sections by :ref: role using its title. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from docutils import nodes - -# from sphinx.locale import __ -import logging -from sphinx.util.nodes import clean_astext - - -def __(x): - return x - - -if False: - # For type annotation - from typing import Any, Dict # NOQA - from sphinx.application import Sphinx # NOQA - - -logger = logging.getLogger(__name__) - -if False: - # For type annotation - from typing import Any, Dict # NOQA - from sphinx.application import Sphinx # NOQA - - -def register_sections_as_label(app, document): - # type: (Sphinx, nodes.Node) -> None - labels = app.env.domaindata['std']['labels'] - anonlabels = app.env.domaindata['std']['anonlabels'] - for node in document.traverse(nodes.section): - labelid = node['ids'][0] - docname = app.env.docname - ref_name = getattr(node[0], 'rawsource', node[0].astext()) - if app.config.autosectionlabel_prefix_document: - name = nodes.fully_normalize_name(docname + ':' + ref_name) - print('{} + {} => {}'.format(docname, ref_name, name)) - else: - name = nodes.fully_normalize_name(ref_name) - sectname = clean_astext(node[0]) - - if name in labels: - logger.warning(__('duplicate label %s, other instance in %s'), - name, app.env.doc2path(labels[name][0])) - - anonlabels[name] = docname, labelid - labels[name] = docname, labelid, sectname - - -def setup(app): - # type: (Sphinx) -> Dict[unicode, Any] - app.add_config_value('autosectionlabel_prefix_document', True, 'env') - app.connect('doctree-read', register_sections_as_label) - - return { - 'version': 'builtin', - 'parallel_read_safe': True, - 'parallel_write_safe': True, - } diff --git a/docs/source/best-practices.md b/docs/source/best-practices.md index 7951290f7..a46691ae3 100644 --- a/docs/source/best-practices.md +++ b/docs/source/best-practices.md @@ -23,7 +23,7 @@ The snactor tool helps you with creating the base layout of a new Leapp reposito the repository artifacts like actors, models, tags, topics, and workflows. It also helps you with debugging as it is able to execute individual actors. -See the [tutorial on basic usage of snactor](first-actor). +See the [tutorial on basic usage of snactor](tutorials/first-actor). ## Move generic functionality to libraries @@ -118,7 +118,7 @@ def do_the_actor_thingy(actor): actor.log.debug("All the actor’s logic shall be outside actor.py") ``` -For more about unit testing, see the [tutorial](unit-testing). +For more about unit testing, see the [tutorial](tutorials/unit-testing). ## Do not introduce new dependencies diff --git a/docs/source/build-schema.md b/docs/source/build-schema.md index 79199646f..7f6869a56 100644 --- a/docs/source/build-schema.md +++ b/docs/source/build-schema.md @@ -1,8 +1,8 @@ # Naming schema of Leapp-related packages -All projects under the OAMG should use the same schema for names of RPM builds, -to be able to simply and fast find specific builds. The schema itself looks -like that (NVR without %{dist}): +All projects under OAMG should use the same schema for names of RPM builds, +to be able to simply and quickly find specific builds. The schema itself looks +like this (NVR without %{dist}): - for builds made from the main branch: ``` @@ -17,12 +17,12 @@ Where: - **name** is name of the project - **version** is current version of the application - **timestamp** in format: `+%Y%m%d%H%MZ`, e.g. "201812211407Z" -- **short-hash** is shortened hash of the commit from which the build has been +- **short-hash** is a shortened hash of the commit from which the build has been created -- **branch** is name of the branch from which the builds has been created; +- **branch** is name of the branch from which the builds have been created; in case the name of the branch contains dashes (`-`), these are automatically replaced by underscore (`_`) because dash is used as a separator in the name - of a (S)RPM file; so it is suggested (not required) to to avoid dashes + of a (S)RPM file; it is suggested (not required) to avoid dashes in names of branches - **number** is number of a pull-request (alternatively merge-request) to which builds are related diff --git a/docs/source/building-blocks-and-architecture.md b/docs/source/building-blocks-and-architecture.md new file mode 100644 index 000000000..5de56ee3e --- /dev/null +++ b/docs/source/building-blocks-and-architecture.md @@ -0,0 +1,99 @@ +# Building blocks and architecture TODO + +## Overview + +### Architecture overview TODO +![Architecture overview](/_static/images/framework-arch-overview.png) + +There are two tools for working with the framework, the end user application +`leapp` and the development utility `snactor`. The `leapp` tool is designed to +run specific workflows, while the `snactor` tool can run arbitrary workflows, +but also individual actors. + +A *workflow* describes what work is going to be done and when. Each workflow is +made of a sequence of *phases*, which contain *actors* split into three stages +- before, main, and after. Workflows, actors, and all the parts necessary for +the execution are loaded from repositories. + +Each actor is executed in a forked child process to prevent the modification of +the application state. All messages and logs produced by the actors are stored +in the *audit database*. + +For more information about each of the building blocks, read +[](#entities-in-leapp). + +### How is this different from Ansible? + +Leapp is message-driven. The execution of actors is dependent on the data produced by other actors running before them. This data is passed around in the form of *messages*. +This is in a contrast with Ansible where everything has to be specified up front. + +## Entities in Leapp + +### Actor + +An actor in terms of the Leapp project is a step that is executed within a workflow. +Actors define what kind of data they expect, and what they produce. Actors also +provide a list of tags, with which actors mark their use cases. + +Actors scan the system and produce the information found as messages. Other +actors consume those messages to make decisions, or process the data to produce +new messages. Some actors might apply changes to the system based on the +information gathered earlier. + +### Message + +A message is produced by an actor, and the payload follows the definition of +the [Model](#model) it is named after. Messaging is a term used to describe the +data exchange between [actors](#actor). + +### Model + +Models are the definition of the data model used in [message](#message) payloads. + +### Phase + +Phases are sections in a workflow dedicated to some specific area of execution. +A phase consists of three [stages](#stage): Before, Main, and After. +Phases are defined by assigning one or more tags to them, which will be used +to find actors in the [repositories](#repository) loaded. + +### Repository + +A repository is the place where all actors, models, tags, topics, and workflows +are defined. Additionally to that shared files, libraries and tools can be put +into the repository. + +### Stage + +Stage is a part of a [phase](#phase). There are three defined stages: +- Before +- Main +- After + +Before and After stages can be used to allow an actor to be run before or after any +other actors in the phase. This should be useful in some hooking scenarios, where +an action is supposed to be happening before or after another action. This way, other +actors could be influenced. + +### Tag + +A tag allows the framework to find [actors](#actor) in the [repository](#repository) +and group their execution based on that tag. + +### Topic + +Topics are assigned to models and are used for grouping the data into areas of interest. + +### Workflow + +Workflows describe what work is going to be done and when. A workflow is +describing a sequence of phases, where one phase has assigned filters with +which the framework selects actors that should be executed from the +repositories on the system. + +### Workflow APIs + +Workflow APIs are custom API classes that actors can use and automatically +inherit their consumed and produced messages. This way one can write a stable +API for third party actor writers, without being affected by changes of message +model layout, name changes etc. diff --git a/docs/source/compatibility-with-leapp-repository.md b/docs/source/compatibility-with-leapp-repository.md index 13c61233e..f95f27209 100644 --- a/docs/source/compatibility-with-leapp-repository.md +++ b/docs/source/compatibility-with-leapp-repository.md @@ -2,7 +2,7 @@ There is a hidden dragon in the split of leapp framework and leapp repositories projects. The development of features and incompatible changes has different -speed in both projects. We want to release our projects using semantic +pace in both projects. We want to release our projects using semantic versioning and, at the same time, we do not want to release a new version of the project after each merge of a new feature or incompatible change. We rather prefer releasing new versions with changelogs and everything @@ -13,13 +13,13 @@ provide new functionality in the upstream (main) branch without the need of immediate release of the new version of leapp. For these purposes the `leapp-framework` capability is provided in the framework (python[23]-leapp) rpms. -## When and how change the capability +## When and how to change the capability The `leapp-framework` capability has to be changed in case of any change of provided API (e.g. change in the leapp standard library) or functionality that could affect actors in leapp repositories. That includes new features as well -as an actor may depend on that in future, and the leapp-repository package -should be able to specify correct framework version that is needed. +as an actor may depend on that in future, and packages providing leapp repositories +should be able to specify correct framework version that is required. The `leapp-framework` capability uses just `X.Y` versioning: diff --git a/docs/source/conf.py b/docs/source/conf.py index d8551f42c..722a402de 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,7 +20,6 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))) import sphinx_rtd_theme # noqa @@ -38,9 +37,10 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.githubpages', - 'autosectionlabelext', + 'sphinx.ext.autosectionlabel', 'myst_parser', ] +autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/source/contributing.md b/docs/source/contributing.md index 65f94a550..2cfde5d75 100644 --- a/docs/source/contributing.md +++ b/docs/source/contributing.md @@ -1,17 +1,197 @@ # Contributing to the Leapp project - First, thank you for taking your time to contribute to the project. -The following is a set of guidelines for contributing effectively to the Leapp-related repositories -hosted under the [OS and Application Modernization Group organization](https://github.com/oamg/) -on GitHub. +The following is a set of guidelines for contributing effectively to the +Leapp-related repositories hosted under the [OS and Application Modernization +Group organization](https://github.com/oamg/) on GitHub. + +## Follow Python coding guidelines +While code stylistics (naming, whitespace, structure) is mostly handled by the famous PEP8 - https://www.python.org/dev/peps/pep-0008/ +there's still much to be defined and codified in terms of code semantics, which is equally important as the overall style. +Purpose of these guidelines is to reduce the *cognitive load* necessary to read through & understand the code base. + +### 1. Avoid Inheriting from Multiple Classes + +Python uses something called C3 Linearization (https://en.wikipedia.org/wiki/C3_linearization) +to figure out the method resolution order and, as you can see, the logic behind the algorithm is non-trivial - dragging +that context in your head all the time is, costly and unnecessary, given the particularly *little benefit* of using +multiple inheritance in the first place. + +### 2. Avoid Operator Overloading + +With the exception of mathematics there's usually very little reason for succumbing to the mysterious and +arcane arts of operator overloading. Why is math OK? Because operator overloading makes it consistent +with how *everybody* assumes the given operator works given how it works in math itself. +There's little benefit in operator overloading otherwise, because different people have different assumptions +and while you think that it's pretty clear that invalid state should make the object equal to `None`, well, pretty +much everyone else is going to find this behavior confusing (http://stackoverflow.com/questions/371266/checking-c-sharp-new-initialisation-for-null/371286), +because it's just not how the rest of the ecosystem works. Going back to math, for the same reason you wouldn't like `3 + 3 = 7` because +someone decided that the symbols for addition now should return `a + b + 1`, don't put such constructs in code. +The situation is much worse because Python isn't statically typed, so even if you knew that `SomeCrazyClass` overloads +some particular operator, you might have no idea that `some_var` even *is* an instance of `SomeCrazyClass`. + +### 3. Avoid 'In-Your-Face' error/exception messages + +Exceptions or terminal error messages should *always* give you enough context to either fix the problem on the OS level, if applicable, or +know the exact location and reason why the error was raised, given you need to fix it in code. +Even some shady corners of Python base library will sometimes simply tell you `Permission denied` when you're trying to access +a resource for which you don't have sufficient permission, which makes the message exceptionally unhelpful in case your application +manages multiple resources in a loop. All you end up doing is catch and re-raise the exception with more helpful message. + +#### 3.1. Type Your Exceptions + +Raise `ValueError` or `TypeError` when validating input parameters. Use custom exception hierarchy with `Exception` at its root for +raising errors related to application logic itself. Callers of your code then have fine grained control over various +failure modes, which will always help at making the code more robust. + +### 4. Avoid `**kwargs` like a plague + +Probably apart from function decorators, there's no legitimate reason for including `**kwargs` in your function signatures, ever. +Think of function signatures in terms of binding contracts between *caller* and *callee*, the caller knows how to invoke +the particular function (callee) simply by looking at the signature. In the context of contracts, `**kwargs` essentially +feels like "whatever, dude", because in the best case scenario the valid kwargs are documented in the function's doc string +(which is still going to stop code completion tools to a grinding halt), in the worst case you are going on a chase and will need +to inspect the code of at least one function body - granted the `**kwargs` are not passed around like a hot potato, in which case +your best bet is to resurrect the long forgotten art of "printf debugging" and sprinkle the code with a bunch of `print` calls +here and there. + +### 5. Import things the right way + +Avoid wildcard * imports. So called "Star" imports import every module member that's name doesn't start with `_` into the current scope, +which can quickly lead to name clashes and funny errors. + +Do not make relative imports. + +Imported modules should be divided into 3 groups stdlib/third-party/project in the following order separated +by a single newline; each group should be sorted alphabetically by full module/object name. + +If you need to use plenty of members from some module, use the `import module` syntax rather than `from module import foo`. +Always use `import module` syntax for python standard library, specifically instead of `from os.path import join` use `import os`. + +Sort imported modules as well as all imported functions by name, alphabetically. Our brains are very good at bi-secting names +in alphabetical order. + +Unless you have a really good reason, don't make local imports in function body. + +### 6. Avoid Using Assertions For Control Flow & Mutating Operations + +Assertions are, idiomatically, used to verify very basic assumptions about the code. It is assumed that once the code has been +stressed enough without triggering any assertion errors the code should be just fine. In C, for example, assertions are not +evaluated in non-debug builds. In Python, assertions are ignored if you execute your code with `-O` flag. +This also means that if the right-hand side of the assertion mutates certain state, the action *is not* carried out. + +### 7. Avoid Map, Reduce and Too Complex Comprehensions + +While one might say that writing out for loops makes the code look dumb and uninteresting, it also makes it +obvious and easy to understand, which are qualities, not defects. Code that uses map/reduce or several levels +of nested dictionary and list comprehensions almost always looks scary and it's very hard to deduce the +data flow of that particular piece of code. + +### 8. Avoid Deeply Nested Block Scopes + +In other words, high cyclomatic complexity is bad (https://en.wikipedia.org/wiki/Cyclomatic_complexity). Reasoning about code +with so much noise around is difficult, however, there are several ways how to avoid this situation. Instead of `if someCond:` +followed a by a huge chunk of code, consider the pattern of *eager returns* and simply return as soon as possible +`if not someCond: return` so that the following block doesn't need to be nested. This is of course not applicable for all +dark corners of cyclomatic complexity, for nested loops you might want to consider refactoring each loop into it's own +function or method. While the code will get a little more verbose, and you might face other difficulties like having to pass +around variables or even promoting some to instance variables, the resulting code is still much simpler to read then +condensed web of loops mutating maybe tens of local variables. + +### 9. Write Docstrings That Work Well With Tools + +The preferable way of writing docstrings for functions and methods is to use the first style mentioned at +(https://pythonhosted.org/an_example_pypi_project/sphinx.html#function-definitions). Plenty of editors or plugins are able +to parse these docstrings and provide targeted code completion and rudimentary validation. For consistency all docstrings +should start and end with `"""`. + +### 10. Avoid Shadowing Python Builtins + +type, file, id, map, filter, etc. have already been taken, be creative and invent your own object names. + +### 11. String Formatting + +#### 11.1 Prefer [new style](https://docs.python.org/3/library/string.html#string-formatting) String Formatting To [old style](https://docs.python.org/2/library/stdtypes.html#string-formatting) + +Though there is still a mixture of formatting styles remaining in leapp code base, this rule stands for +all new contributions. +Please use + +``` +new_style = "{}! {} is coming".format("Brace yourself", "Winter") +``` -## Code style guidelines +instead of -Your code should follow our -[Python Coding Guidelines](python-coding-guidelines) +``` +old_style_refactor_me = "%s! %s is coming!" % ("Brace yourself", "Winter") +old_style_refactor_me = "%(msg)s! %(obj)s is coming!" % {"msg": "Brace yourself", "obj": "Winter"} +``` -## Best practices in actor development +#### 11.2 Use Keyword Arguments To Increase Readability + +If you pass more than 2 arguments to format function, please invoke format with keyword arguments. +``` +msg_many_args = "{who} {when} {what}".format(who="A Lanister", when="always", what="pays his debts") +``` + +### 12. Docstrings + +Follow [PEP 257 - Docstring Conventions](https://www.python.org/dev/peps/pep-0257) + - with the exception, that the summary line of a multi-line docstring shall be on a new line, not on the same line as the opening quotes. + +There are some minimal requirements for the information that actor docstrings should provide, to learn more about the specifics please +consult the [contribution guidelines](contributing). + +This is example how to write docstrings: + +```python +class MyActor(Actor): + """ + Start with a single-line brief summary of the actor (under the triple quotes). + + Leave a blank line below the summary and then describe the actor's behaviour + here in detail. + """ + name = 'my_actor' + + def process(self): + """This is a simple method.""" + complicated_method(True) + + def complicated_method(switch): + """ + This is a summary line of a more complicated method. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc porta sed + urna venenatis faucibus. Phasellus at bibendum ligula, a posuere metus. + + :param switch: Description of the parameter. + :type switch: Expected type of the parameter. + :return: Description of what the method returns + """ + mutliline_string = ( + 'I am a multiline string.\n' + 'This is my second line.' + ) + return mutliline_string +``` + +### 13. Underscore usage + +For leapp and leapp-repository the `_` and `P_` is reserved for localization. Please don't use it for anything else like +variable-to-be-discarded. Instead, use a variable name prefixed with `dummy_`. What comes after +the prefix should describe the data that is being discarded, like so: + +``` python +dummy_scheme, netloc, path, dummy_params, query, fragment = urlparse("scheme://netloc/path;parameters?query#fragment") +``` + +Using an informative variable name for discarded values is helpful when a future developer modifies +the code and needs the discarded information. They can quickly see that the information is already +available; they just need to rename the variable and start using it. + +## Follow best practices * Actor development: [Best practices for actor development](best-practices) * Actor directory layout is to be found at [Repository directory layout](repository-dir-layout) (under `actorname/`) @@ -22,6 +202,14 @@ Your code should follow our 1. New folders and/or Python files shall use lowercase without underscores. 1. The actor's main file shall be named `actor.py`. +## Git Commit Messages + +* Write a descriptive commit message +* Use the present tense ("Add feature" not "Added feature") +* Use the imperative mood ("Move cursor to..." not "Moves cursor to...") +* If you are fixing a GitHub issue, include something like 'Closes issue #xyz' +* For more best practices, read [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) + ## Submitting a Pull Request Before you submit your pull request, consider the following guidelines: @@ -59,14 +247,6 @@ Before you submit your pull request, consider the following guidelines: * Every PR should have at least one code review before merging * All CI tests should pass -## Git Commit Messages - -* Write a descriptive commit message -* Use the present tense ("Add feature" not "Added feature") -* Use the imperative mood ("Move cursor to..." not "Moves cursor to...") -* If you are fixing a GitHub issue, include something like 'Closes issue #xyz' -* For more best practices, read [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) - ## Contact In case of any question, contact us at `#leapp` on Libera.Chat IRC network, or write the question as an issue on diff --git a/docs/source/dependencies-leapp-repository.md b/docs/source/dependencies-leapp-repository.md deleted file mode 100644 index cbefca016..000000000 --- a/docs/source/dependencies-leapp-repository.md +++ /dev/null @@ -1,56 +0,0 @@ -# How to deal with dependencies in leapp-repository - -First, read this -[document](dependencies) -to better understand the difficulties related to package dependencies in the -Leapp project. - -When talking about RHEL 7 to RHEL 8 upgrade, the goal is to cover dependencies -of all Leapp project-related packages, including the leapp-repository packages, -for both RHEL 7 and RHEL 8. Since the situation with dependencies of the leapp -packages is similar to the situation with the leapp-repository dependencies, -this document focuses on the leapp-repository specifics only. - -Currently there are two SPEC files for leapp-repository: - -- The -[leapp-repository.spec](https://github.com/oamg/leapp-repository/blob/main/packaging/leapp-repository.spec) -file is used to build leapp-repository packages and their dependency -metapackage _leapp-repository-deps_ **for RHEL 7**. -- The -[leapp-el7toel8-deps.spec](https://github.com/oamg/leapp-repository/blob/main/packaging/leapp-el7toel8-deps.spec) -file is used to build dependency metapackages _leapp-deps-el8_ and -_leapp-repository-deps-el8_ **for RHEL 8** whose purpose is to replace the -RHEL 7 dependency metapackages _leapp-deps_ and _leapp-repository-deps_ during -the upgrade. - -## What to do in leapp-repository when dependencies of leapp change? - -Go to the section below the line `%package -n %{ldname}` in the -[leapp-el7toel8-deps.spec](https://github.com/oamg/leapp-repository/blob/main/packaging/leapp-el7toel8-deps.spec). -This section creates the RHEL 8 _leapp-deps-el8_ metapackage that replaces the -RHEL7 _leapp-deps_ metapackage. So when the leapp package dependencies change -in the [leapp.spec](https://github.com/oamg/leapp/blob/main/packaging/leapp.spec) -together with incrementing version of the **leapp-framework-dependencies** -capability, it's necessary to: - -- provide the same **leapp-framework-dependencies** capability version - by _leapp-deps-el8_ - -- decide if this dependency change also applies to RHEL 8 and if so, update - the dependencies of the _leapp-deps-el8_ metapackage accordingly. - -There can be another case when we need to modify dependencies of leapp on -RHEL 8, e.g. when a RHEL 7 package is renamed or split on RHEL 8. In such case -we don't need to modify the capability value, just update dependencies of the -_leapp-deps-el8_ metapackage, commenting it properly. Nothing else. - -## What to do when leapp-repository dependencies need to change? - -When you want to modify *outer dependencies* of leapp-repository packages, do -that similarly to instructions related to Leapp packages, following the same -rules. Just take care of the **leapp-repository-dependencies** capability -instead of the *leapp-framework-dependencies* capability. Everything else is -the same. Interesting parts of the SPEC files are highlighted in the same way -as described in the -[leapp dependencies document](dependencies). diff --git a/docs/source/dependencies.md b/docs/source/dependencies.md index 9d0fdcace..27f7c0d95 100644 --- a/docs/source/dependencies.md +++ b/docs/source/dependencies.md @@ -18,22 +18,22 @@ changed, the value of the capability has to be incremented by one - so the information about the change is provided to other leapp-related packages. This metapackage can be simply replaced by a metapackage that would handle -outer dependencies of the target system. That means, that the metapackage -with dependencies for the target system is not part of this git repository -and the actors of the leapp-repository are responsible for providing -such package in case it is needed. +outer dependencies of the target system. That means, that the metapackage with +dependencies for the target system is not part of the [Leapp git repository +](https://github.com/oamg/leapp) and packages containing Leapp repositories are +responsible for providing such package in case it is needed. ## Why do I need to have a special way to define a dependency -One possible use of the Leapp framework is to do the in-place upgrade (IPU). -In that case, it is necessary to execute Leapp on two different -versions of the system - it starts with the source system and then continues -with the target (upgraded) system. It is quite common that some packages are -renamed between those two systems (or in case of leapp-repository, you would -like to use technology on the new system that is not provided on the original -one). -In such cases, it is hard to satisfy dependencies on both systems - when you -want to proceed with the upgrade process with the same Leapp packages. +One of the usages of the Leapp framework is to perform an in-place upgrade +(IPU) of a Linux operating system, RHEL in particular. In that case, it is +necessary to execute Leapp on two different versions of the system - it starts +with the source system and then continues with the target (upgraded) system. It +is quite common that some packages are renamed between those two systems. Or +maybe you would like to use technology on the new system that is not provided +on the original one). In such cases, it is hard to satisfy dependencies on both +systems - when you want to proceed with the upgrade process with the same Leapp +packages. As a solution, we are using the metapackage that contains those dependencies. @@ -67,7 +67,8 @@ is consistent across the SPEC file. ### Some more unusual steps There is an official -[Leapp repository](https://github.com/oamg/leapp-repository) that we manage +[Leapp repository](https://github.com/oamg/leapp-repository) providing Leapp +repositories for RHEL in-place upgrades that we manage and it is already affected by such change. If you modify the *outer dependencies*, inform the repository developers about the change (e.g. [issue](https://github.com/oamg/leapp-repository/issues/new)) diff --git a/docs/source/deprecation.md b/docs/source/deprecation.md index 142d884fb..0b038ce3f 100644 --- a/docs/source/deprecation.md +++ b/docs/source/deprecation.md @@ -10,14 +10,16 @@ impact on your code, we introduce the deprecation process described below. The following lists cover deprecated functionality in the leapp utility, snactor, the leapp standard library, etc. But don't cover deprecated functionalities -from particular leapp repositories (e.g. the [elt7toel8](https://github.com/oamg/leapp-repository/tree/main/repos/system_upgrade/el7toel8) leapp repository). For -such information, see [Deprecated functionality in the el7toel8 repository](el7toel8/deprecation.md#deprecated-functionality-in-the-el7toel8-repository). +from particular leapp repositories (e.g. the [RHEL in-place +upgrade](https://github.com/oamg/leapp-repository/tree/master/repos/system_upgrade/) +leapp repositories), these should be covered by the documentation of the +repositories. -## current upstream development (till the next release + 6months) +### current upstream development (till the next release + 6months) - nothing yet... -## v0.15.0 (till Mar 2023) +### v0.15.0 (till Mar 2023) - Reporting primitives - **`leapp.reporting.Flags`** - The `Flags` report primitive has been deprecated in favor of the more general `Groups` one. @@ -84,551 +86,3 @@ detail. Please note, that the Deprecation warning is emitted only if: - the deprecated class is instantiated - the deprecated function is called - - -## How to deprecate entities in leapp? - -When you want to deprecate an entity in leapp projects, use the `deprecated` -decorator from `leapp.utils.deprecation` above the definition of the entity. -The decorator has three input parameters: - -- `since` (mandatory) - specifying the start of the deprecation protection period -- `message` (mandatory) - explaining that particular deprecation - (e.g. in case the deprecated functionality has a replacement, it is expected - it will be mentioned in the msg.) -- `stack_level_offset` (optional) - useful to adjust the position of the reported - usage in the deprecation message; e.g. in case of a base class or derived classes - -**Warning: possible change:** *It's possible the `stack_level_offset` parameter -will be removed (or ignored) in future, if we discover a way to improve -the deprecation of derived classes.* - -In case of a class deprecation, all derived classes are considered to be deprecated -as well. However, the current reporting could be a little bit confusing. To -improve that, the `stack_level_offset` option can be specified. -See [examples of the use of the @deprecated decorator for classes](deprecation.md#classes). - -When you mark any entity as deprecated and this entity is then used -in the code, users will be notified about that via a terminal and report -messages (see the previous section). However, as the author of the deprecation, -you know that the entity is deprecated and you do not want -to notify people about the code that still uses the deprecated entity -just for the reason to retain the original functionality. To suppress -the deprecation messages in such cases, use the `suppress_deprecation` -decorator taking as arguments objects that should not be reported -by the deprecation. E.g. in case you use it above the definition -of an actor, any use of the deprecated entity inside the actor -will not be reported. - -WARNING: It is strictly forbidden to use the `suppress_deprecation` decorator -for any purposes but one - retaining the original official functionality over -the deprecation protection period. If you see the message and you are not -the *provider* of the functionality, you have to update your code -to be independent on it. - -### Examples of a model deprecation - -Imagine we want to deprecate a *Foo* model that is produced in an actor called -*FooProducer* and consumed in an actor called *FooConsumer*. Let's keep this -example simple and say that we do not want to set any replacement of this -model. The first thing we have to do is to set the model definition as deprecated: - -```python -from leapp.models import Model, fields -from leapp.topics import SomeTopic -from leapp.utils.deprecation import deprecated - - -@deprecated(since='2020-06-20', message='This model has been deprecated.') -class Foo(Model): - topic = SomeTopic - value = fields.String() -``` - -If we do only this and execute actors that produce/consume messages of this model, -we will obtain messages like these (just example from the actor -producing the message after execution by snactor): - -```bash -# snactor run fooproducer -============================================================ - USE OF DEPRECATED ENTITIES -============================================================ - -Usage of deprecated Model "Foo" @ /path/to/repo/actors/fooproducer/actor.py:17 -Near: self.produce(Foo(value='Answer is: 42')) - -Reason: This model has been deprecated. ------------------------------------------------------------- - -============================================================ - END OF USE OF DEPRECATED ENTITIES -============================================================ -``` - -Apparently, the `Reason` is not so good. It's just example. In real world -example, you would like to provide usually a little bit better explanation. -Anyway, much more interesting is the point, that the message is now printed -every time the actor is executed. - -Obviously we do not want to remove the actor yet, because in such a case, the -model could be hardly called as deprecated - we need to keep the same -functionality during the protection period. But at the same time, we do not -want the deprecation message to be produced in this case, as it would be kind -of a spam for users who don't care about that model at all. - -The warning messages are focused on a "custom" use of the deprecated -model - i.e. when a developer creates their own actor producing/consuming a message -of the model. To fix this, suppress the deprecation message in this actor. - -To do it, the only thing that has to be done is to set the -`suppress_deprecation` decorator with the `Foo` as an argument (in this case) -before the actor, e.g.: - -```python -from leapp.actors import Actor -from leapp.models import Foo # deprecated model -from leapp.tags import IPUWorkflowTag, FactsPhaseTag -from leapp.utils.deprecation import suppress_deprecation - - -@suppress_deprecation(Foo) -class FooProducer(Actor): - """ - Just produce the right answer to the world. - """ - - name = 'foo_producer' - consumes = () - produces = (Foo,) - tags = (IPUWorkflowTag, FactsPhaseTag) - - def process(self): - self.produce(Foo(value='Answer is: 42')) -``` - -This is the most simple case. Let's do a small change and produce the message -inside the private actor's library instead. The library looks like this: - -```python -from leapp.models import Foo # deprecated model -from leapp.libraries.stdlib import api - -def produce_answer(): - api.produce(Foo(value='Answer is: 42')) -``` - -And the updated actor looks like this: - -```python -from leapp.actors import Actor -from leapp.libraries.actor import fooproducer_lib -from leapp.models import Foo # deprecated model -from leapp.tags import IPUWorkflowTag, FactsPhaseTag -from leapp.utils.deprecation import suppress_deprecation - - -@suppress_deprecation(Foo) -class FooProducer(Actor): - """ - Just produce the right answer to the world. - """ - - name = 'foo_producer' - consumes = () - produces = (Foo,) - tags = (IPUWorkflowTag, FactsPhaseTag) - - def process(self): - fooproducer_lib.produce_answer() -``` - -Now, if you execute the actor again you still won't get any -deprecation message. So the `suppress_deprecation` decorator works transitively -as expected. However, even when the actor is treated well, the current -implementation could affect the result of unit tests. To explain the idea of what -could be wrong, imagine a unit test like this one: - -```python -from leapp.libraries.actor import fooproducer_lib -from leapp.libraries.common.testutils import produce_mocked -from leapp.libraries.stdlib import api -from leapp.models import Foo # deprecated model - -def test_process(monkeypatch): - produced_msgs = produce_mocked() - monkeypatch.setattr(api, 'produce', produced_msgs) - fooproducer_lib.produce_answer() - assert Foo(value='Answer is: 42') in produced_msgs.model_instance -``` - -If you run the test, you will get output like this (shortened): - -```python -| 21:48:01 | conftest | INFO | conftest.py | Actor 'foo_producer' context teardown complete - -repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process PASSED - -================================================== warnings summary ================================================== -repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process - /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/libraries/fooproducer_lib.py:5: _DeprecationWarningContext: Usage of deprecated Model "Foo" - api.produce(Foo(value='Answer is: 42')) - /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py:10: _DeprecationWarningContext: Usage of deprecated Model "Foo" - assert Foo(value='Answer is: 42') in produced_msgs.model_instances - --- Docs: http://doc.pytest.org/en/latest/warnings.html -======================================== 1 passed, 2 warnings in 0.13 seconds ======================================== -``` - -As you can see the warning have been generated again. This time on two places, -in `test_process` and `produce_answer` functions. Unless warning messages affect -results of tests, we do not require strictly to handle them. However, it's a good -practice. But if the warning log could affect the test (e.g. if a test function -checks logs) it should be treated by the `suppress_deprecation` decorator too. -In case of the library function, just add the `@suppress_deprecation(Foo)` line -before the definition of the `produce_answer` function. But if we do the same -for the test function, we will get an error (see that we have now just one -deprecation warning now): - -``` -| 21:59:57 | conftest | INFO | conftest.py | Actor 'foo_producer' context teardown complete -repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process FAILED - -====================================================== FAILURES ====================================================== -____________________________________________________ test_process ____________________________________________________ - -args = (), kwargs = {'monkeypatch': <_pytest.monkeypatch.MonkeyPatch object at 0x7f21924b24d0>} -suppressed = - - @functools.wraps(target_item) - def process_wrapper(*args, **kwargs): - for suppressed in suppressed_items: - _suppressed_deprecations.add(suppressed) - try: - return target_item(*args, **kwargs) - finally: - for suppressed in suppressed_items: -> _suppressed_deprecations.remove(suppressed) -E KeyError: - -tut/lib/python3.7/site-packages/leapp/utils/deprecation.py:35: KeyError -================================================== warnings summary ================================================== -repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process - /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py:13: _DeprecationWarningContext: Usage of deprecated Model "Foo" - assert Foo(value='Answer is: 42') in produced_msgs.model_instances - --- Docs: http://doc.pytest.org/en/latest/warnings.html -======================================== 1 failed, 1 warnings in 0.21 seconds ======================================== -``` - -It's because the mechanism of decorators in python and how pytest works. -In this case, we need to do a small workaround, like this: - -```python -from leapp.libraries.actor import fooproducer_lib -from leapp.libraries.common.testutils import produce_mocked -from leapp.utils.deprecation import suppress_deprecation -from leapp.libraries.stdlib import api -from leapp.models import Foo # deprecated model - - -@suppress_deprecation(Foo) -def _foo(value): - """Small workaround to suppress deprecation messages in tests.""" - return Foo(value=value) - -def test_process(monkeypatch): - produced_msgs = produce_mocked() - monkeypatch.setattr(api, 'produce', produced_msgs) - fooproducer_lib.produce_answer() - assert _foo(value='Answer is: 42') in produced_msgs.model_instances -``` - -That's the whole solution for the *FooProducer* actor. Analogically to this, -we need to treat the `FooConsumer` actor. You could notice that all imports -of the `Foo` model are commented. It's a good practice as it is more visible -to all developers that a deprecated entity is present. - -### Example of a model replacement - -This is analogous to the previous case. Take the same scenario, but extend it with -the case in which we want to replace the `Foo` model by the `Bar` model. What -will be changed in case of deprecation in the model definition? Just -the deprecation message and the new model definition: - -```python -from leapp.models import Model, fields -from leapp.topics import SomeTopic -from leapp.utils.deprecation import deprecated - - -@deprecated(since='2020-06-20', message='The model has been replaced by Bar.') -class Foo(Model): - topic = SomeTopic - value = fields.String() - - -class Bar(Model): - topic = SomeTopic - value = fields.String() -``` - -You can see that in this case, the model has been just renamed, to keep -it simple. But it's sure that the new model can be different from the original -one (e.g. a different name of fields, a different purpose and set of fields,...). -The change in the `FooProducer` will be just extended by handling the new -model (it should include update of tests as well; I am skippin the example -as the change is trivial): - -```python -from leapp.actors import Actor -from leapp.models import Bar -from leapp.models import Foo # deprecated model -from leapp.tags import IPUWorkflowTag, FactsPhaseTag -from leapp.utils.deprecation import suppress_deprecation - - -@suppress_deprecation(Foo) -class FooProducer(Actor): - """ - Just produce the right answer to the world. - """ - - name = 'foo_producer' - consumes = () - produces = (Bar, Foo) - tags = (IPUWorkflowTag, FactsPhaseTag) - - def process(self): - self.produce(Foo(value='Answer is: 42')) - self.produce(Bar(value='Answer is: 42')) -``` - -As you can see, the only thing that have been changed is the added production of the -`Bar` message. The original functionality is still present. - -### Example of a derived model deprecation - -**Warning: Known issue**: *The deprecation of a derived model is currently buggy and the documented -solution will end probably with traceback right now. The fix will be delivered -in the next release of leapp (current one is 0.11.0). As well, we will try to -simplify the final solution, so maybe this section will be more simple with -the next release.* - -It's a common situation, that some models are derived from others. Typical -example is a base model which is actually not produced or consumed -by actors, but it is used as a base model for other models. For the sake of simplicity, -we'll use one of our previous solutions, but update the definition -of the `Foo` model (skipping imports): - -```python -@deprecated(since='2020-01-1', message='This model has been deprecated.') -class BaseFoo(Model): - topic = SystemInfoTopic - value = fields.String() - - -class Foo(BaseFoo): - pass -``` - -Previously, the content was handled completely, but with the new change, we -will see the warnings again: - -``` -================================================== warnings summary ================================================== -repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process - /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/libraries/fooproducer_lib.py:7: _DeprecationWarningContext: Usage of deprecated Model "BaseFoo" - api.produce(Foo(value='Answer is: 42')) - /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py:10: _DeprecationWarningContext: Usage of deprecated Model "BaseFoo" - return Foo(value=value) -``` - -See that the reported deprecated model is `BaseFoo`, however only -the `Foo` model is produced. This could be very confusing to users and developers. The -deprecation of the `BaseFoo` model could resolve our troubles, in the meaning that -the base class and all other classes are covered already, that the deprecated -entity has been used. But it is confusing, that with this solution, you have -to update the code in actor, to suppress the `BaseFoo` model instead of `Foo`, -even when `BaseFoo` is not used anywhere directly in the code. I mean -something like this: - -```python -from leapp.models import Foo, BaseFoo -from leapp.libraries.stdlib import api -from leapp.utils.deprecation import suppress_deprecation - -@suppress_deprecation(BaseFoo) -def produce_answer(): - api.produce(Foo(value='Answer is: 42')) -``` - -Now, I will put here just several ideas what could user do and why these are -wrong (if you are interested just about the working correct solution, skip -after the list): - -1. *Deprecate all models (`BaseFoo`, `Foo`)* - The result will be two - deprecation message per one use of `Foo`. One with the `Foo` msg, one with - `BaseFoo`. That's not good, as we would like to get rid of `BaseFoo` - completely in the messages ideally. -1. *Deprecate just the derived models (`Foo`)* - That could resolve the problem, - but what if someone else derive a new model from the base one? They will not - be notified about the deprecation and removal will break their code instantly. - -If you want to ensure that all models (base and derived) are deprecated, the -best solution we are able to come up with it's little weird and it's going -slightly against our requirement (we are going to do an exception here), that -inside models cannot be defined any method or logic as they are supposed to be -used just as the *data container*. But this one is currently only possible way, -to deprecate such models correctly without confusing messages: - -```python -from leapp.models import Model, fields -from leapp.topics import SystemInfoTopic -from leapp.utils.deprecation import deprecated -from leapp.utils.deprecation import suppress_deprecation - - -@deprecated(since='2020-01-01', message='This model has been deprecated.') -class BaseFoo(Model): - topic = SystemInfoTopic - value = fields.String() - - -@deprecated(since='2020-01-01', message='This model has been deprecated.') -class Foo(BaseFoo): - @suppress_deprecation(BaseFoo) - def __init__(self, *args, **kwargs): - super(Foo, self).__init__(*args, **kwargs) -``` - -As you see, both models are deprecated. But in the derived one, there is the -`__init__` method defined, just for the purpose to be able to suppress the -deprecation message from the base model. Implementing this, the solution for -suppressing the deprecation warning in previous section will be working, without -any confusing messages. As well, this is the only possible usecase for a method -inside the models in official repositories managed by the OAMG team. - -## Additional various outputs of snactor and leapp - -### snactor warning message example - -``` -============================================================ - USE OF DEPRECATED ENTITIES -============================================================ - -Usage of deprecated function "deprecated_function" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:19 -Near: deprecated_function() - -Reason: This function is no longer supported. ------------------------------------------------------------- -Usage of deprecated Model "DeprecatedModel" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:20 -Near: self.produce(DeprecatedModel()) - -Reason: This model is deprecated - Please do not use it anymore ------------------------------------------------------------- -Usage of deprecated class "DeprecatedNoInit" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:21 -Near: DeprecatedNoInit() - -Reason: Deprecated class without __init__ ------------------------------------------------------------- -Usage of deprecated class "DeprecatedBaseNoInit" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:22 -Near: DeprecatedNoInitDerived() - -Reason: Deprecated base class without __init__ ------------------------------------------------------------- -Usage of deprecated class "DeprecatedWithInit" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:23 -Near: DeprecatedWithInit() - -Reason: Deprecated class with __init__ ------------------------------------------------------------- - -============================================================ - END OF USE OF DEPRECATED ENTITIES -============================================================ -``` - -### leapp report example entries - -``` ----------------------------------------- -Risk Factor: medium -Title: Usage of deprecated class "IsolatedActions" at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/libraries/repofileutils.py:38 -Summary: IsolatedActions are deprecated -Since: 2020-01-02 -Location: /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/libraries/repofileutils.py:38 -Near: def get_parsed_repofiles(context=mounting.NotIsolatedActions(base_dir='/')): - ----------------------------------------- -Risk Factor: medium -Title: Usage of deprecated class "IsolatedActions" at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/scansubscriptionmanagerinfo/libraries/scanrhsm.py:8 -Summary: IsolatedActions are deprecated -Since: 2020-01-02 -Location: /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/scansubscriptionmanagerinfo/libraries/scanrhsm.py:8 -Near: context = NotIsolatedActions(base_dir='/') - ----------------------------------------- -Risk Factor: medium -Title: Usage of deprecated function "deprecated_method" at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/deprecationdemo/actor.py:21 -Summary: Deprecated for Demo! -Since: 2020-06-17 -Location: /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/deprecationdemo/actor.py:21 -Near: self.deprecated_method() - ----------------------------------------- -``` - -## Additional simple usage examples of @deprecated - -### Functions - -```python -... -from leapp.utils.deprecation import deprecated - - -@deprecated(since='2020-06-20', message='This function has been deprecated!') -def some_deprecated_function(a, b, c): - pass -``` - -### Models - -```python -... -from leapp.utils.deprecation import deprecated - - -@deprecated(since='2020-06-20', message='This model has been deprecated!') -class MyModel(Model): - topic = SomeTopic - some_field = fields.String() -``` - -### Classes - -```python -... -from leapp.utils.deprecation import deprecated - - -@deprecated(since='2020-06-20', message='This class has been deprecated!') -class MyClass(object): - pass - - -# NOTE: Here we need to offset the stacklevel to get the report of the usage not in the derived class constructor -# but where the derived class has been created. -# How many levels you need, you will have to test, it depends on if you use the builtin __init__ methods or not, -# however it gives you the ability to go up the stack until you reach the position you need to. -@deprecated(since='2020-06-20', message='This class has been deprecated!', stack_level_offset=1) -class ABaseClass(object): - def __init__(self): - pass - - -class ADerivedClass(ABaseClass) - def __init__(self): - super(ADerivedClass, self).__init__() -``` diff --git a/docs/source/devenv-install.md b/docs/source/devenv-install.md index 210e48f4e..07bf946c5 100644 --- a/docs/source/devenv-install.md +++ b/docs/source/devenv-install.md @@ -1,4 +1,4 @@ -# Installing the development environment +# Installation ## RPM packages installation diff --git a/docs/source/el7toel8/actor-rhel7-to-rhel8.md b/docs/source/el7toel8/actor-rhel7-to-rhel8.md deleted file mode 100644 index 32b371f74..000000000 --- a/docs/source/el7toel8/actor-rhel7-to-rhel8.md +++ /dev/null @@ -1,469 +0,0 @@ -# How to create a Leapp actor for RHEL 7 to 8 upgrade - -## Introduction - -This document is intended for all people who want to contribute to the process of upgrading Red Hat Enterprise Linux (RHEL) 7 to RHEL 8 using Leapp tool. The upgrade is performed in place meaning that the RHEL 7 installation is replaced by RHEL 8 on the same storage. -After reading through this document, you will be able to transform your expertise in certain parts of RHEL into improvements of the RHEL 7 to 8 upgrade tooling. - -## Setting up the development environment - -Leapp actors are written in Python 2.7+/3.6+ (the resulting code has to be both py2 and py3 compatible), so your usual Python development setup can be used during the process of creating a new actor. - -## Tools - -The main tools you will use for the actor development are listed below. - -### leapp - -The leapp framework provides the libraries required to be imported by any actor and also a binary tool used to control the execution of actors within a workflow. - -### snactor - -Separate tool provided by Leapp to help the process of creating and executing an actor. - -You can see _snactor_ source code [here](https://github.com/oamg/leapp/tree/main/leapp/snactor). - -## Creating an actor - -Every actor needs to be inside a so-called “Leapp repository”, otherwise it won’t be visible to Leapp. A Leapp repository groups actors and many other things which will be discussed later, like models, workflows, tags and topics. You can find all Leapp repositories under /usr/share/leapp-repository/repositories. A Leapp repository can be recognized by containing .leapp folder: - -```shell -$ find -L /etc/leapp/repos.d/ -name ".leapp" -type d | xargs dirname -/etc/leapp/repos.d/common -/etc/leapp/repos.d/system_upgrade/el7toel8 -``` - -First, you need to register repositories with snactor: - -```shell -$ snactor repo find --path /etc/leapp/repos.d/ -Registering /etc/leapp/repos.d/system_upgrade/el7toel8 -Registering /etc/leapp/repos.d/common -``` - -After registering the repositories, you can move inside any of these repositories and use snactor to create a boilerplate of a new actor: - -```shell -# cd /etc/leapp/repos.d/system_upgrade/el7toel8 -# snactor new-actor MyNewActor -New actor MyNewActor has been created at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/mynewactor/actor.py -# cd /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/mynewactor/ -# tree -. -├── actor.py -└── tests -``` - -The main file of the actor is the actor.py in which you’ll write code for logic of the actor. - -For further information about how create an actor read this [document](../first-actor). - -## Including an actor in the RHEL 7 to 8 upgrade process - -Until now, you have created boilerplate of a new actor and made it visible to Leapp. But, Leapp needs some more information about what to do with the actor. Specifically, in which **“workflow”** and in which **“phase”** the actor should be executed. A workflow is a sequence of phases. The only workflow available now is the one solving the upgrade of RHEL 7 to RHEL 8. Each phase is a set of actors that will be executed one after another before the next phase starts. To find out in which workflow and phase should the actor be executed, Leapp looks for **“tags”**. To be part of RHEL 7 to RHEL 8 upgrade workflow, an actor needs to be tagged with **IPUWorkflowTag**. - -The phases of the IPUWorkflow (in order) are: **Facts Collection, Checks, Report, Download, Upgrade RamDisk Preparation, Upgrade RamDisk Start, Late Tests, Preparation, RPM Upgrade, Application Upgrade, Third Party Applications, Finalization** and **First Boot**. Each phase has a specific tag that marks an actor as being part of that phase. You can find descriptions of all the phases and their tags [here](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/common/workflows/inplace_upgrade.py) and workflow diagram [here](../inplace-upgrade-workflow). - -For example, if an actor is to be executed within the Checks phase, it needs to be tagged both with IPUWorkflowTag and ChecksPhaseTag. The result after updating the boilerplate would be: - -```python -from leapp.actors import Actor -from leapp.tags import ChecksPhaseTag, IPUWorkflowTag - -class MyNewActor(Actor): - """ No description has been provided for the my_new_actor actor. """ - - name = 'my_new_actor' - consumes = () - produces = () - tags = (ChecksPhaseTag, IPUWorkflowTag) - - def process(self): - pass -``` - -## Inter-actor communication - -### Receiving data from other actors - -All communication between actors in Leapp is carried out using **“messages”**. An actor can consume or produce messages. A message may contain any data, but the data needs to be in a specific format defined by a **“model”**. If an actor wants to consume a message produced by another actor, it needs to specify the specific model of the consumed messages. Leapp will make sure to execute such an actor only after some message of the specified model was produced by another actor. If no message of the specified model was produced in previous phases or in the current phase, the consuming actor will get no messages of that kind. - -For further information about messaging see [document](../messaging). - -One of the existing models in Leapp is [ActiveKernelModulesFacts](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/el7toel8/models/activekernelmodulesfacts.py). Messages from this model contain data about the system on which Leapp has been started. For example, it contains installed kernel modules. If an actor wants to perform some action based on existing kernel modules on the system, the actor can get list of these modules by consuming the _ActiveKernelModulesFacts_ messages. By extending the boilerplate, the code could look like this: - -```python -from leapp.actors import Actor -from leapp.models import ActiveKernelModulesFacts - -from leapp.tags import ChecksPhaseTag, IPUWorkflowTag - - -class MyNewActor(Actor): - """ No description has been provided for the my_new_actor actor. """ - - name = 'my_new_actor' - consumes = (ActiveKernelModulesFacts,) - produces = () - tags = (ChecksPhaseTag, IPUWorkflowTag) - - def process(self): - for fact in self.consume(ActiveKernelModulesFacts): - for active_module in fact.kernel_modules: - self.log.info(active_module.filename) -``` - -By executing the above actor, all active kernel modules would be logged on output using log utilities inherited from the Actor class. - -### Asking user questions - -In rare cases the actor can't choose a proper scenario of execution and leaves the final decision of how to proceed -to the user. That's where dialogs come into play. - -Please mind that using dialogs should be considered as last resort, when the situation absolutely can't resolve itself -automatically. The rule of a thumb is to make upgrade procedure require as little user input as possible. But if you -feel that there is no way to write a proper safe rhel7->rhel8 conversion logic and you need human to make a decision - -you can go with dialogs. - -The following restrictions apply: - -- At the time only Yes/No questions can be asked. Effectively only leapp.dialogs.components.BooleanComponent can be used. -- Dialogs can't be codependent. Any question asked should be independent of previous question's answer and should not - change the behavior of any other dialog that any actor might create. -- Dialogs can be used only at certain stages of the workflow - ChecksPhase and TargetTransactionChecksPhase. - -For more information and real examples please check [dialogs](../dialogs). - -### Producing data for other actors and reporting - -An actor can produce some data interesting enough for other actors to consume. It could be some parsed data, or content that will be displayed to the user in a report or even shared info between a subset of actors. - -The process is very similar to the one used to consume messages, but now the new actor will produce them. Similar to ActiveKernelModulesFacts, Leapp has a Report model. Messages from this model contain data that will be displayed to the user during ReportsPhase. For example, an actor can warn the user in case a btrfs kernel module is active on the system. Then, the actor could look like this: - -```python -from leapp import reporting -from leapp.actors import Actor -from leapp.models import ActiveKernelModulesFacts -from leapp.reporting import Report, create_report -from leapp.tags import ChecksPhaseTag, IPUWorkflowTag - -class MyNewActor(Actor): - """ No description has been provided for the my_new_actor actor. """ - - name = 'my_new_actor' - consumes = (ActiveKernelModulesFacts,) - produces = (Report,) - tags = (ChecksPhaseTag, IPUWorkflowTag) - - def process(self): - for fact in self.consume(ActiveKernelModulesFacts): - for active_module in fact.kernel_modules: - if active_module.filename == 'btrfs': - create_report([ - reporting.Title('Btrfs has been removed from RHEL8'), - reporting.Summary( - 'The Btrfs file system was introduced as Technology Preview with the initial release' - ' of Red Hat Enterprise Linux 6 and Red Hat Enterprise Linux 7. As of versions 6.6' - ' and 7.4 this technology has been deprecated and removed in RHEL8.'), - reporting.ExternalLink( - title='Considerations in adopting RHEL 8 - btrfs has been removed.', - url='https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/' - 'considerations_in_adopting_rhel_8file-systems-and-storage_considerations-in-' - 'adopting-rhel-8#btrfs-has-been-removed_file-systems-and-storage' - ), - reporting.Severity(reporting.Severity.HIGH), - reporting.Groups([reporting.Groups.INHIBITOR, reporting.Groups.FILESYSTEM]), - reporting.RelatedResource('driver', 'btrfs') - ]) - break - -``` - -Final report is generated in **"txt"** and **"json"** format in `/var/log/leapp` directory at the end of either leapp preupgrade or leapp upgrade execution. - -### Reporting tips and good practices - -**Same type reports share the same title** - -To make your reports easier to read and aggregate please use same report title for checks that have the same semantics. -For example, if your actor is checking for deprecated modules and creating a report entry each time one is found, -the way to represent it in the report will be - -```diff -create_report([ - reporting.Title( -- 'Upgrade process was interrupted because a deprecated module {0} is enabled'.format(module), -+ 'Upgrade process was interrupted because a deprecated module is enabled', - reporting.Summary( - 'Module {0} was surpassed by shiny-new-{0} and therefore it was ' - 'removed from RHEL-8. Keeping it in the configuration may ' - 'lock out the system thus it is necessary to disable it ' - 'before the upgrade process can continue.'.format(module) - ), - ... - ]) -``` - -A better way, if applicable, would be to first collect all deprecated modules with their descendants and produce one -report entry with a list of mappings, like - -``` - -modules_map = {'moduleA': 'shiny-new-moduleA', 'moduleB': 'shiny-new-moduleB'} -... - -create_report([ - reporting.Title( - 'Upgrade process was interrupted because deprecated modules are enabled', - reporting.Summary( - 'Modules below were surpassed by new modules and ' - 'removed from RHEL-8. Keeping them in the configuration may ' - 'lock out the system thus it is necessary to disable them ' - 'before the upgrade process can continue.\n{}'.format( - '\n'.join(" - {0} -> {1}".format(old, new) for old, new in modules_map.iteritems())) - ), - ... - ]) -``` - -**Remediations** - -Apart from the above example you can also suggest a remediation, which is a procedure intended to fix the discovered -issue. Currently remediation can come in 3 flavors: a bash command to execute, a hint for manual action and a playbook. - -``` -reporting.Remediation(commands=[['alternatives', '--set', 'python', '/usr/bin/python3']]) -reporting.Remediation(hint='Please remove the dropped options from your scripts.') -reporting.Remediation(playbook=) -``` - -**Available Groups** - -The following groups were originally known as **Tags**: -``` -'accessibility', 'authentication', 'boot', 'communication', 'drivers', 'email', 'encryption', -'filesystem', 'firewall', 'high availability', 'kernel', 'monitoring', 'network', 'OS facts', -'post', 'python', 'repository', 'sanity', 'security', 'selinux', 'services', 'time management', -'tools', 'upgrade process' -``` - -The following groups were originally known as **Flags**: -``` -'failure', 'inhibitor' -``` - -The **failure** Group is recommended to be used when the report is related to -a command or other action failure. - -If you need additional report groups, please open a GH issue or a PR, -with the description why new required groups are needed. - -**Related resources** - -We recognize the following 6 types of resources: - -``` -reporting.RelatedResource('package', 'memcached') -reporting.RelatedResource('file', '/etc/passwd') -reporting.RelatedResource('service', 'postfix') -reporting.RelatedResource('directory', '/boot') -reporting.RelatedResource('repository', 'RHEL 7 Base') -reporting.RelatedResource('kernel-driver', 'vmxnet3') -reporting.RelatedResource('pam', 'pam_securetty') -``` - -The related resources are especially useful when you have a lot of accompanied objects like files or directories by your report and you would like to present it to the user in a specific way. - -## Testing your new actor - -During development of your new actor, it is expected that you will test your work to verify that results match your expectations. You can do that by manually executing your actor, or writing tests on various levels (i.e unit tests, component tests, E2E tests). - -### Executing a single actor - -You should use snactor tool to run a single actor and verify its output. Assuming that there are no errors, the actor was placed inside a valid leapp repository and snactor tool is aware of such repository, you can call snactor run to execute it. Below we are executing the existing [OSReleaseCollector](https://github.com/oamg/leapp-repository/tree/main/repos/system_upgrade/el7toel8/actors/osreleasecollector) actor that provides information about operating system release from target system. For the `snactor run` command you can use either the actor’s folder name (osreleasecollector), the actor’s class name (OSReleaseCollector) or the value of the name attribute of the actor’s class (os_release_collector). - -```shell -# pwd -/usr/share/leapp-repository/repositories/system_upgrade/el7toel8 -# snactor run --verbose OSReleaseCollector -2018-11-23 11:16:25.126 INFO PID: 4293 leapp: Logging has been initialized -2018-11-23 11:16:25.163 INFO PID: 4293 leapp.repository.system_upgrade_el7toel8: A new repository 'system_upgrade_el7toel8' is initialized at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8 -2018-11-23 11:16:25.212 INFO PID: 4293 leapp.repository.common: A new repository 'common' is initialized at /usr/share/leapp-repository/repositories/common -``` - -As you can see the actor is executed without errors. But, by default, snactor does only display data logged by the actor. In order to display messages generated by the actor you can re-run the above command with _--print-output_ option. - -```shell -# snactor run --verbose --print-output OSReleaseCollector -2018-11-23 11:32:42.193 INFO PID: 4433 leapp: Logging has been initialized -2018-11-23 11:32:42.218 INFO PID: 4433 leapp.repository.system_upgrade_el7toel8: A new repository 'system_upgrade_el7toel8' is initialized at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8 -2018-11-23 11:32:42.265 INFO PID: 4433 leapp.repository.common: A new repository 'common' is initialized at /usr/share/leapp-repository/repositories/common -[ - { - "stamp": "2019-04-30T13:00:13.836063Z", - "hostname": "leapp-20190429150826", - "actor": "os_release_collector", - "topic": "system_info", - "context": "0ac49430-1b29-4940-92bb-3e81da85f8af", - "phase": "NON-WORKFLOW-EXECUTION", - "message": { - "hash": "8305f6a38dcd266ea02bbd2e7c0b799e871d7dbe8734ea4138da53f4779b993e", - "data": "{\"id\": \"rhel\", \"name\": \"Red Hat Enterprise Linux Server\", \"pretty_name\": \"Red Hat Enterprise Linux\", \"variant\": \"Server\", \"variant_id\": \"server\", \"version\": \"7.6 (Maipo)\", \"version_id\": \"7.5\"}" - }, - "type": "OSReleaseFacts" - } -] -``` - -Now we can see that the _OSReleaseCollector_ actor produced a message of the _OSReleaseFacts_ model, containing data like OS Release name and version. - -### Executing a single actor that uses the workflow config - -If you need to execute an actor on its own that requires the `IPUConfig` model you can execute the actor with the -following command: - -```shell -snactor run --actor-config IPUConfig ActorName -``` - -In order for this to work you have to run the `IPUWorkflowConfig` actor before and save its output, so that the config -data is stored in the database for the current session: - -```shell -snactor run --save-output IPUWorkflowConfig -``` -Since the leapp upgrade repositories support several upgrade paths, the `snactor` needs to get additional data for the correct execution, like the release of the target system, the flavor specification, etc. -If you see similar errors when running `snactor` -``` -leapp.models.fields.ModelViolationError: The value of "target" field is None, but this is not allowed -``` -please, set the following environment variables (adjust the `LEAPP_UPGRADE_PATH_TARGET_RELEASE` as needed): -``` -export LEAPP_UPGRADE_PATH_TARGET_RELEASE=8.6 -export LEAPP_UPGRADE_PATH_FLAVOUR=default -snactor run --save-output IPUWorkflowConfig -``` - -### Executing the whole upgrade workflow with the new actor - -Finally, you can make your actor part of the “leapp upgrade” process and check how it behaves when executed together with all the other actors in the workflow. Assuming that your new actor is tagged properly, being part of _IPUWorkflow_, and part of an existing phase, you can place it inside an existing leapp repository on a testing RHEL 7 system. All Leapp components (i.e actors, models, tags) placed inside **/etc/leapp/repos.d/system_upgrade/el7toel8/** will be used by the “leapp upgrade” command during upgrade process. - -### Verifying correct communication between actors - -Leapp provides another actor, named [CheckOSRelease](https://github.com/oamg/leapp-repository/tree/main/repos/system_upgrade/el7toel8/actors/checkosrelease), that consumes messages from model _OSReleaseFacts_ and produces an error message in case system OS Release is not supported by Leapp upgrade process. In order to consume such message, _OSReleaseCollector_ actor needs to be executed before _CheckOSRelease_ and its message needs to be stored inside Leapp database. This process is controlled by the framework during the execution of “leapp upgrade” command. - -But, if you want to execute it manually, for test purposes, you can also use snactor for it. First we need to make sure that all messages that will be consumed are generated and stored. For this example, this means running _OSReleaseCollector_ actor with the _--save-output_ option of snactor: - -```shell -# snactor run --verbose --save-output OSReleaseCollector -2018-11-23 13:06:30.706 INFO PID: 17996 leapp: Logging has been initialized -2018-11-23 13:06:30.753 INFO PID: 17996 leapp.repository.system_upgrade_el7toel8: A new repository 'system_upgrade_el7toel8' is initialized at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8 -2018-11-23 13:06:30.803 INFO PID: 17996 leapp.repository.common: A new repository 'common' is initialized at /usr/share/leapp-repository/repositories/common -``` - -Now, you can execute _CheckOSRelease_ actor and verify that it consumes the previously generated message and produces a message saying that the target system is not supported by Leapp upgrade process. You don’t need to specify which message will be consumed, snactor will take care of it. - -```shell -# snactor run --verbose --print-output CheckOSRelease -2018-11-23 13:11:15.549 INFO PID: 18126 leapp: Logging has been initialized -2018-11-23 13:11:15.578 INFO PID: 18126 leapp.repository.system_upgrade_el7toel8: A new repository 'system_upgrade_el7toel8' is initialized at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8 -2018-11-23 13:11:15.617 INFO PID: 18126 leapp.repository.common: A new repository 'common' is initialized at /usr/share/leapp-repository/repositories/common -[ - { - "stamp": "2019-04-30T13:12:05.706317Z", - "hostname": "leapp-20190429150826", - "actor": "check_os_release", - "topic": "report_topic", - "context": "0ac49430-1b29-4940-92bb-3e81da85f8af", - "phase": "NON-WORKFLOW-EXECUTION", - "message": { - "hash": "ceaf419907ec78a894334b2a331a9ebb0c5a7847c18afc6d7546ba6656959e0d", - "data": "{\"report\": \"{\\\"audience\\\": \\\"sysadmin\\\", \\\"detail\\\": {\\\"related_resources\\\": [{\\\"scheme\\\": \\\"file\\\", \\\"title\\\": \\\"/etc/os-release\\\"}]}, \\\"flags\\\": [\\\"inhibitor\\\"], \\\"severity\\\": \\\"high\\\", \\\"summary\\\": \\\"The supported OS versions for the upgrade process: 7.6\\\", \\\"tags\\\": [\\\"sanity\\\"], \\\"title\\\": \\\"Unsupported OS version\\\"}\"}" - }, - "type": "Report" - } -] - -``` - -To flush all saved messages from the repository database, run `snactor messages clear`. - -## Writing tests for an actor - -[Read the tutorial](../unit-testing) for writing and running unit and component tests - -## Best practices - -Read the best practices [document](../best-practices) and [Python guidelines](https://github.com/oamg/leapp-guidelines/blob/master/python-coding-guidelines.md). - -## Contributing actors to the Leapp project - -Currently all Leapp elements (i.e. actors, models, tags) are stored under a public [GitHub repository](https://github.com/oamg/leapp-repository). - -All new content that needs to be part of Leapp release distributed to all users should be proposed as a Pull Request in this repository. - -**Before submitting your work for review, make sure you have read and followed the contribution guidelines:** - -[Contribution guidelines for writing actors](../contributing) - -This [pull request](https://github.com/oamg/leapp-repository/pull/186) gives a good example of both guidelines-driven actor implementation and thorough test coverage. - -## FAQ - -### In which existing workflow phase should I place my new actor? - -You can decide that based on the description of the phases this information is available in the [code](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/el7toel8/workflows/inplace_upgrade.py) and diagram [here](../inplace-upgrade-workflow). Please note that if your actor depends on some message generated by another actor, it cannot be executed in a phase before the phase of such actor. In a similar way, if your actor produces data, it needs to be executed before the actor consuming the data. - -### How to stop the upgrade in case my actor finds a problem with the system setup? - -The process of inhibiting the upgrade is done by the VerifyCheckResult actor, executed during the ReportPhase. This actor consumes messages from the _Report_ model and if any message with the flag “inhibitor” was generated it will inhibit the upgrade process. So, your actor needs to produce a _Report_ message with the flag “inhibitor” before the upgrade process gets to the ReportPhase. Read more about inhibiting the upgrade process [here](inhibit-rhel7-to-rhel8). - -### How to stop execution of my actor in case of an unexpected error? - -It’s good practice to code defensively so the code is robust. The actor should detect unexpected input or result of some operation and exit gracefully instead of tracebacking. In case you detect an unexpected behavior, let the framework know about it by raising [StopActorExecutionError](leapp.exceptions.StopActorExecutionError). Framework will act based on the [setting of the upgrade workflow](leapp.workflows.policies) in one of the following three ways: - -- end the upgrade process right away, or -- end the upgrade process after finishing the current phase, or -- do not end the upgrade process at all and continue with logging the issue only. - -### How does the logging work? - -For logging of messages not to be visible to the user by default but rather for issue investigation purposes, use simply `self.log.(msg)` within the actor. Or, within the actor’s library this way: - -```python -from leapp.libraries.stdlib import api -api.current_logger().(msg) -``` - -The usual logging practice of [Python’s logger library](https://docs.python.org/2/library/logging.html#logger-objects) applies, i.e. the `` can be for example _debug, warning, error, critical_, etc. Leapp framework will take care of these messages and provide them through appropriate channels (stdout/stderr, log files, journalctl, audit table in /var/lib/leapp/leapp.db). - -### What Python version my actor/tests code should be compatible with? - -Python 2.7+/3.6+, but keep in mind that the resulting code has to be both py2 and py3 compatible. - -### How to add tests to my new actor? - -Under “tests” folder, an actor can have Python files containing tests that will be executed using PyTest. Leapp provide tests utilities to simulate actor consuming and checking actor production. Please, refer to detailed Leapp documentation about how to write tests. - -For further information read: [Writing tests for actors](../unit-testing). - -### How to use libraries or data files in my new actor? - -An actor can have data files under the “files” folder and Python libraries under the “libraries” folder. Leapp does provide utilities to help an actor to access the files and libraries in these folders. Please, refer to detailed Leapp documentation about this. - -### Where can I report an issue or RFE related to the framework or other actors? - -You can open a GitHub issues: - -- [Leapp framework](https://github.com/oamg/leapp/issues/new) -- [Leapp actors](https://github.com/oamg/leapp-repository/issues/new) - -When filing an issue, include: - -- How to reproduce it -- All files in `/var/log/leapp`. -- The `/var/lib/leapp/leapp.db` file. - -## Where can I seek help? - -We’ll gladly answer your questions and lead you to through any troubles with the actor development. You can reach us, the OS and Application Modernization Group, at Libera.Chat IRC server in channel **#leapp**. diff --git a/docs/source/el7toel8/deprecation.md b/docs/source/el7toel8/deprecation.md deleted file mode 100644 index 243972f86..000000000 --- a/docs/source/el7toel8/deprecation.md +++ /dev/null @@ -1,49 +0,0 @@ -# Deprecated functionality in the el7toel8 repository - -Deprecated functionality is listed under the first version that the functionality -is deprecated in. Note that functionality may be deprecated in later versions -but are not listed again. -The dates in brackets correspond to the end of the deprecation protection period, -after which the related functionality can be removed at any time. - -*Note* The lists cover just the functionality provided inside the el7toel8 -repository only. For the functionality deprecated in the leapp -framework, see [List of deprecated functionality in leapp](../deprecation.md#list-of-the-deprecated-functionality-in-leapp) - -## current upstream development (till the next release + 6months) - -- nothing yet... - -## v0.19.0 (till March 2024) - -- Models - - **InstalledTargetKernelVersion** - Deprecated as the new solution has been designed to be able to handle new changes in RHEL 9.3+ system. Use the `InstalledTargetKernelInfo` message instead. - - **GrubInfo.orig_device_name** - The `GrubInfo` message is still valid, but the `orig_device_name` field has been deprecated as multiple devices can exist on a system. Use `GrubInfo.orig_devices` instead. -- Shared libraries - - **leapp.libraries.common.config.version.is_rhel_realtime()** - The function has been deprecated as the information cannot be easily determined based on the information inside `IPUConfig`. Use data in the `KernelInfo` message instead, the field `type`. - - **leapp.libraries.common.grub.get_grub_device()** - The function has been deprecated as multiple grub devices can exists on a system. Use the `leapp.libraries.common.grub.get_grub_devices()` function instead. - -## v0.16.0 (till September 2022) - -- Shared libraries - - **`leapp.libraries.common.utils.apply_yum_workaround`** - The `apply_yum_workaround` function has been deprecated, use `DNFWorkaround` message as used in the successing `RegisterYumAdjustment` actor. - -## v0.15.0 (till April 2022) -- Models - - **RequiredTargetUserspacePackages** - Deprecated because the new solution has been designed. Use the `TargetUserspacePreupgradeTasks` instead (see the `install_rpms` field). - - **RequiredUpgradeInitramPackages** - Deprecated because the new solution around the upgrade initramfs has been designed. Use the `TargetUserspaceUpgradeTasks` instead (see the `install_rpms` field). - - **UpgradeDracutModule** - Replaced by `UpgradeInitramfsTasks` (see the `include_dracut_modules` field). - - **InitrdIncludes** - Deprecated because the new solution around the target initramfs (read: initramfs created for the upgraded system) has been designed. Use the `TargetInitramfsTasks` instead (see the `include_files` field). - -## v0.12.0 (till April 2021) -- Models - - **GrubDevice** - Deprecated because the current implementation is not reliable. GRUB device detection is now in the shared grub library. Use the `leapp.libraries.common.grub.get_grub_device()` function instead. - - **UpdateGrub** - Deprecated because the current implementation is not reliable. GRUB device detection is now in the shared grub library. Use the `leapp.libraries.common.grub.get_grub_device()` function instead. -- Shared libraries - - **`leapp.libraries.common.testutils.logger_mocked.warn()`** - The logging.warn method has been deprecated in Python since version 3.3. Use the warning method instead. - -## v0.11.0 (till April 2021) -- Models - - **TMPTargetRepositoriesFacts** - Deprecated because this model was not intended for customer use. - - diff --git a/docs/source/el7toel8/envars.md b/docs/source/el7toel8/envars.md deleted file mode 100644 index 6eeb81f87..000000000 --- a/docs/source/el7toel8/envars.md +++ /dev/null @@ -1,194 +0,0 @@ -# Environment variables for the el7toel8 repository - -Actors in the el7toel8 repository use environment variables specified below. -All these envars use the suggested prefixes specified in -[the best practices document](../best-practices.md#use-the-leapp-and-leapp_devel-prefixes-for-new-envars) -for the leapp project to distinguish their purpose: _production_ or _devel_ use. - -If the argument for envars below is not specified, it is expected to set `0` -(false) or `1` (true). - -## LEAPP_GRUB_DEVICE - -Overrides the automatically detected storage device with GRUB core (e.g. -/dev/sda). - -## LEAPP_NO_RHSM - -If set to 1, Leapp does not use Red Hat Subscription Management for the upgrade. -It's equivalent to the `--no-rhsm` leapp option. - - -## LEAPP_OVL_SIZE - -For any partition that uses XFS with the ftype option set to 0, Leapp is -creating a file of a specific size in order to proceed with the upgrade. -By default, the size of that file is 2048 MB. In case the size needs to be -increased, Leapp informs you in the pre-upgrade report that the environment -variable needs to be specified. - -## LEAPP_DEBUG - -Enables debug logging. Equivalent to `--debug`, which takes precedence. - -## LEAPP_VERBOSE - -Enables debug logging. Equivalent to `--verbose`, which takes precedence. - -## LEAPP_CONFIG - -Overrides the default location of `leapp.conf`. If not specified, -`leapp/leapp.conf` is used when the command is executed inside a leapp -repository, otherwise the default `/etc/leapp/leapp.conf` is used. - -## LEAPP_LOGGER_CONFIG - -Overrides the default location of `logger.conf`. If not specified, the default -`/etc/leapp/logger.conf` is used. - -## LEAPP_ENABLE_REPOS - -Specify repositories (repoids) split by comma, that should be used during the -in-place upgrade to the target system. It's overwritten automatically in case -the `--enablerepo` option of the leapp utility is used. It's recommended to use -the `--enablerepo` option instead of the envar. - -## LEAPP_SERVICE_HOST - -Overrides the host of the service to which leapp connects to fetch necessary -data files in case they are missing. The used protocol (`http://` or -`https://`) must be specified. Defaults to `https://cert.cloud.redhat.com`. - -## LEAPP_PROXY_HOST - -If set, leapp will use this proxy to fetch necessary data files in case they -are missing. The used protocol (`http://` or `https://`) must be specified. - -## LEAPP_TARGET_PRODUCT_CHANNEL - -The alternative to the `--channel` leapp option. As a parameter accepts -a channel acronym. E.g. `eus` or `e4s`. For more info, see the -`leapp preupgrade --help`. -In case the beta channel is required, use the LEAPP_DEVEL_TARGET_PRODUCT_TYPE -envar instead. - -## LEAPP_NO_NETWORK_RENAMING - -If set to 1, the actor responsible to handle NICs names ends without doing -anything. The actor usually creates UDEV rules to preserve original NICs in -case they are changed. However, in some cases it's not wanted and it leads -in malfunction network configuration (e.g. in case the bonding is configured -on the system). It's expected that NICs have to be handled manually if needed. - -## LEAPP_DATABASE_FORCE_SYNC_ON - -If set to 1, Leapp will explicitly enable synchronization on the SQLite database. -Enabling the synchronization has negative impact on the performance -(sometimes very negative). However, it is more reliable in case of extreme -situations (e.g. lost power). -Note the synchronization is nowadays switched off by default only during the phases -executed before the reboot of the system to the upgrade environment, which we consider -safe. As a result, we do not expect that someone would want to use this option now. - -## LEAPP_NO_INSIGHTS_REGISTER - -If set to 1, Leapp does not register the system into Red Hat Insights automatically. -It's equivalent to the `--no-insights-register` leapp option. - -## LEAPP_NO_RHSM_FACTS -If set to 1, Leapp does not store migration information using Red Hat Subscription Manager. -It's equivalent to the `--no-rhsm-facts` leapp option. - -## LEAPP_NOGPGCHECK -Set to 1 to disable RPM GPG checks (same as yum/dnf --nogpgckeck option). -It's equivalent to the `--nogpgcheck` leapp option. - -## LEAPP_TARGET_ISO -Set the path to the target OS ISO image that should be used for the IPU. -It's equivalent to the `--iso` leapp option. - - -## LEAPP_UNSUPPORTED - -Necessary to use in case you use any envar with the LEAPP_DEVEL prefix -(see the list below). And in case you use the --whitelist-experimental option -for the Leapp tool. - -# Development environment variables for the el7toel8 repository - -## LEAPP_DEVEL_RPMS_ALL_SIGNED - -Leapp will consider all installed pkgs to be signed by RH - that affects -the upgrade process as by default Leapp upgrades only pkgs signed by RH. -Leapp takes care of the RPM transaction (and behaviour of applications) -related to only pkgs signed by Red Hat. What happens with the non-RH signed -RPMs is undefined. - -## LEAPP_DEVEL_TARGET_RELEASE - -Change the default target RHEL 8 minor version. - -## LEAPP_DEVEL_SKIP_CHECK_OS_RELEASE - -Do not check whether the source RHEL 7 version is the supported one. -E.g. right now Leapp does not allow you to proceed with the upgrade -when you’re not on RHEL 7.9. - -## LEAPP_DEVEL_DM_DISABLE_UDEV - -Setting the environment variable provides a more convenient -way of disabling udev support in libdevmapper, dmsetup and LVM2 tools globally -without a need to modify any existing configuration settings. -This is mostly useful if the system environment does not use udev. - -## LEAPP_DEVEL_SOURCE_PRODUCT_TYPE - -By default the upgrade is processed from the GA (general availability) system -using GA repositories. In case you need to do the in-place upgrade from -a Beta system, use the variable to tell which of those you would like -to use. The value is case insensitive and the default value is `ga`. -Expected values: `ga`, `beta`. - -## LEAPP_DEVEL_TARGET_PRODUCT_TYPE - -`LEAPP_DEVEL_TARGET_PRODUCT_TYPE` is an analogy to -`LEAPP_DEVEL_SOURCE_PRODUCT_TYPE` for the target system and an extension to -`LEAPP_TARGET_PRODUCT_CHANNEL`. If used, it replaces any value set via the -`--channel` option or through the `LEAPP_TARGET_PRODUCT_CHANNEL` environment -variable . It consumes the same set of values as the `--channel` option, and -can be extended with the value `beta`. This is the only way how to perform the -inplace upgrade to a beta version of the target system using the -subscription-manager. - -## LEAPP_DEVEL_USE_PERSISTENT_PACKAGE_CACHE - -Caches downloaded packages when equal to 1. This will reduce the time needed -by leapp, when executed multiple times, because it will not have to download -already downloaded packages. However, this can lead to a random issues in case -the data is not fresh or changes of settings and repositories. The environment -variable is meant to be used only for the part before the reboot and has no -effect or use otherwise. - -## LEAPP_DEVEL_DATABASE_SYNC_OFF - -If set to 1, leapp will disable explicit synchronization on the SQLite -database. The positive effect is significant speed up of the leapp execution, -however it comes at the cost of risking a corrupted database, so it is -currently used for testing / development purposes, only. - -## LEAPP_DEVEL_INITRAM_NETWORK -You can specify one of the following values: 'network-manager', 'scripts'. -The 'scripts' value is used for a legacy dracut module when the network is not -handled by NetworkManager. -Using the option allows experimental upgrades, bringing up the networking inside -the upgrade initramfs environment (upgrade phases after the first reboot). -It also allows the upgrade e.g. when a network based storage is used -on the system. Currently it works only for the most simple configurations -(e.g. when only 1 NIC is present, no rdma, no bonding, ...). Network based -storage is not handled anyhow during the upgrade, so it's possible that the network -based storage will not be correctly initialized and usable as expected). - -## LEAPP_DEVEL_KEEP_DISK_IMGS -If set to 1, leapp will skip removal of disk images created for source OVLs. -This is handy for debugging and investigations related to created containers -(the scratch one and the target userspace container). diff --git a/docs/source/el7toel8/inhibit-rhel7-to-rhel8.md b/docs/source/el7toel8/inhibit-rhel7-to-rhel8.md deleted file mode 100644 index c2c592fdc..000000000 --- a/docs/source/el7toel8/inhibit-rhel7-to-rhel8.md +++ /dev/null @@ -1,162 +0,0 @@ -# How to properly inhibit the RHEL 7 to 8 upgrade process - -## Process Inhibition - -With latest changes on Leapp and with new actors added to the el7toel8 Leapp -repository, any actor can inhibit the upgrade process by producing a specific -message when a problem is found. The message model to use in this case is -[Report](leapp.reporting.Report). -If there is at least one Report message with the `'inhibitor'` group produced before -the Report phase, the upgrade will be stopped in the Reports phase, in which the -messages are being collected. It means that any Report message produced -**after** the Report phase will **not** have inhibiting effect. The details -mentioned in the Report messages will be part of the report available to the -user to review. - -### Sample Actor - -Let’s start with a very simple actor that will verify if system architecture is -supported (this actor may be removed in the future as more archs will be supported): - -```python -import platform - -from leapp.actors import Actor -from leapp.tags import ChecksPhaseTag - - -class CheckSystemArch(Actor): - """ - Check if system is running at a supported architecture. If no, inhibit the upgrade process. - - Base on collected system facts, verify if current architecture is supported, otherwise produces - a message to inhibit upgrade process - """ - - name = 'check_system_arch' - consumes = () - produces = () - tags = (ChecksPhaseTag,) - - def process(self): - if platform.machine() != 'x86_64': - self.log.info("Unsupported arch!") -``` - -If this actor is executed using `snactor` tool in a system with unsupported -architecture, we will see the following output on log: - -```sh -$ snactor run CheckSystemArch --verbose -2019-04-16 15:08:59.622 INFO PID: 1996 leapp: Logging has been initialized -2019-04-16 15:08:59.638 INFO PID: 1996 leapp.repository.sandbox: A new repository 'sandbox' is initialized at /home/leapp/sandbox -2019-04-16 15:08:59.695 INFO PID: 2021 leapp.actors.check_system_arch: Unsupported arch! -``` - -If, instead of only adding a message to the log, the actor writer wants to make -sure that the upgrade process will be stopped in case of unsupported arch, the -actor needs to produce a [Report](leapp.reporting.Report) -message using one of the `report_*` functions from the [reporting](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/el7toel8/libraries/reporting.py) -shared library with the `'inhibitor'` group. - -```python -import platform - -from leapp.actors import Actor -from leapp.reporting import Report, create_report -from leapp import reporting -from leapp.tags import ChecksPhaseTag, IPUWorkflowTag - - -class CheckSystemArch(Actor): - """ - Check if system is running at a supported architecture. If no, inhibit the upgrade process. - - Base on collected system facts, verify if current architecture is supported, otherwise produces - a message to inhibit upgrade process - """ - - name = 'check_system_arch' - consumes = () - produces = (Report,) - tags = (ChecksPhaseTag, IPUWorkflowTag) - - def process(self): - if platform.machine() != 'x86_64': - create_report([ - reporting.Title('Unsupported architecture'), - reporting.Summary('Upgrade process is only supported on x86_64 systems.'), - reporting.Severity(reporting.Severity.HIGH), - reporting.Groups([reporting.Groups.INHIBITOR, reporting.Groups.SANITY]), - ]) -``` - -Running the actor again, it is possible to verify that a new message was -generated. We will still use `snactor` tool to run the actor, but passing -`--print-output` this time to output all generated messages by the actor: - -```sh -$ snactor run CheckSystemArch --verbose --print-output -2019-04-16 15:20:32.74 INFO PID: 2621 leapp: Logging has been initialized -2019-04-16 15:20:32.94 INFO PID: 2621 leapp.repository.sandbox: A new repository 'sandbox' is initialized at /home/leapp/sandbox -[ - { - "stamp": "2019-09-05T12:58:56.342095Z", - "hostname": "leapp-20190904152934", - "actor": "check_system_arch", - "topic": "report_topic", - "context": "9a064a30-5d16-44ba-a807-b7f08b3c4215", - "phase": "NON-WORKFLOW-EXECUTION", - "message": { - "hash": "dc95adcfca56eae62b7fcceeb0477a6d8257c3dddd1b05b879ebdcf05f59d504", - "data": "{\"report\": \"{\\\"audience\\\": \\\"sysadmin\\\", \\\"groups\\\": [\\\"inhibitor\\\", \\\"sanity\\\"], \\\"severity\\\": \\\"high\\\", \\\"summary\\\": \\\"Upgrade process is only supported on x86_64 systems.\\\", \\\"title\\\": \\\"Unsupported architecture\\\"}\"}" - }, - "type": "Report" - } -] - -``` - -Or to inspect closely the message.data field, we could use `jq` tool: - -```sh -snactor run CheckSystemArch --verbose --print-output | jq '.[] | .message.data | fromjson' -{ - "report": "{\"audience\": \"sysadmin\", \"groups\": [\"inhibitor\", \"sanity\"], \"severity\": \"high\", \"summary\": \"Upgrade process is only supported on x86_64 systems.\", \"title\": \"Unsupported architecture\"}" -} - -``` - -This is all that an actor needs to do in order to verify if some condition is -present on the system and inhibit the upgrade process based on that check. - -After all the system checks are executed by different actors, an existing actor -==== BASE ==== -named [VerifyCheckResults](https://github.com/oamg/leapp-repository/tree/master/repos/system_upgrade/el7toel8/actors/verifycheckresults) -is scheduled to run in the Leapp upgrade workflow. If some [Report](/pydoc/leapp.reporting.html#leapp.reporting.Report) -==== BASE ==== -message with the `'inhibitor'` group was generated by some previous execution of -another actor in any previous phase of the workflow, like the sample one we just -wrote, the following output will be displayed to the user: - -```sh -$ leapp upgrade -(...) -2019-04-16 15:36:54.696 INFO PID: 7455 leapp.workflow: Starting phase Reports -2019-04-16 15:36:54.715 INFO PID: 7455 leapp.workflow.Reports: Starting stage Before of phase Reports -2019-04-16 15:36:54.764 INFO PID: 7455 leapp.workflow.Reports: Starting stage Main of phase Reports -2019-04-16 15:36:54.788 INFO PID: 7455 leapp.workflow.Reports: Executing actor verify_check_results -2019-04-16 15:36:54.923 INFO PID: 7455 leapp.workflow.Reports: Starting stage After of phase Reports -2019-04-16 15:36:54.970 INFO PID: 7455 leapp.workflow: Workflow interrupted due to the FailPhase error policy - -============================================================ - ERRORS -============================================================ - -2019-04-16 15:36:54.871634 [ERROR] Actor: verify_check_results Message: Unsupported arch -2019-04-16 15:36:54.888818 [ERROR] Actor: verify_check_results Message: Ending process due to errors found during checks, see /var/log/leapp-report.txt for detailed report. - -============================================================ - END OF ERRORS -============================================================ -``` diff --git a/docs/source/el7toel8/leapp-repository-el7toel8.rst b/docs/source/el7toel8/leapp-repository-el7toel8.rst deleted file mode 100644 index 036c6c9c3..000000000 --- a/docs/source/el7toel8/leapp-repository-el7toel8.rst +++ /dev/null @@ -1,11 +0,0 @@ -Leapp repository for RHEL 7 to RHEL 8 upgrade -============================================= -This is the official upstream documentation for the leapp repository for in-place upgrade (IPU) from RHEL 7 to RHEL 8. The homepage of the project is `here `_. - -.. toctree:: - :caption: Contents: - - actor-rhel7-to-rhel8 - inhibit-rhel7-to-rhel8 - envars - deprecation diff --git a/docs/source/faq.md b/docs/source/faq.md index 4e00583de..a8f9e752b 100644 --- a/docs/source/faq.md +++ b/docs/source/faq.md @@ -1,7 +1,6 @@ # Frequently Asked Questions - [What is Leapp?](#what-is-leapp) -- [How can I get on board with contributing to Leapp?](#how-can-i-get-on-board-with-contributing-to-leapp) - [What is an actor and what does it do?](#what-is-an-actor-and-what-does-it-do) - [When and why do I need to write an actor?](#when-and-why-do-i-need-to-write-an-actor) - [How can I exchange any data between actors?](#how-can-i-exchange-any-data-between-actors) @@ -21,10 +20,6 @@ Leapp project aims to enable users to modernize their existing workloads without disrupting them in three different ways: upgrading them in place, migrating them to a different place or containerize them. Currently, the in-place upgrade functionality is being worked on only. -## How can I get on board with contributing to Leapp? - -For the Leapp framework we are currently developing the functionality for the in-place upgrade of RHEL 7 to RHEL 8. You can improve the user experience of the upgrade by creating so called actors for the Leapp framework. We've written a quick guide on how to create such actors for the RHEL 7 to RHEL 8 upgrades: [How to create a Leapp actor for RHEL 7 to 8 upgrade.](el7toel8/actor-rhel7-to-rhel8) - ## What is an actor and what does it do? An actor in the realm of the Leapp project is a step that is executed within a workflow. Actors define what kind of data they expect and what kind of data they produce. @@ -38,18 +33,16 @@ In regards to the upgrades of RHEL 7 to RHEL 8, Leapp should be able to upgrade ## How can I exchange any data between actors? All communication between actors in Leapp is carried out using " messages". An actor can consume or produce messages. A message may contain any data, but the data needs to be in a specific format defined by a "model". If an actor wants to consume a message produced by another actor, it needs to specify the specific model of the consumed messages. Leapp will make sure to execute such an actor only after some message of the specified model was produced by another actor. If no message of the specified model was produced in previous phases or in the current phase, the consuming actor will get no messages of that kind. -Source: [How to create a Leapp actor for RHEL 7 to 8 upgrade](el7toel8/actor-rhel7-to-rhel8) ## What do I have to do in order to execute actor I just wrote? -If you want to execute just a single actor when developing it, then use the snactor tool. [Here's a tutorial](first-actor) on how to use it. -If you want to add your actor to an existing workflow, for example the RHEL 7 to 8 upgrade workflow, then tag your actor with appropriate workflow and phase tags. -Source: [How to create a Leapp actor for RHEL 7 to 8 upgrade](el7toel8/actor-rhel7-to-rhel8) +If you want to execute just a single actor when developing it, then use the snactor tool. [Here's a tutorial](tutorials/first-actor) on how to use it. +If you want to add your actor to an existing workflow, for example the RHEL in-upgrade workflow, then tag your actor with appropriate workflow and phase tags. ## What should I do if I need to execute multiple actors? Can I somehow ensure the dependencies between them? To be sure that your ActorA runs before your ActorB, produce a specific message in ActorA and let ActorB consume it. By doing this you create a dependency of ActorB on ActorA. -To run just your actors during development, use snactor run --save-output ActorA to save the message of ActorA to the Leapp repository database and then snactor run ActorB. This way, the ActorB will be able to consume the ActorA's saved message. Read more about that in the [tutorial about messaging](messaging.md). +To run just your actors during development, use snactor run --save-output ActorA to save the message of ActorA to the Leapp repository database and then snactor run ActorB. This way, the ActorB will be able to consume the ActorA's saved message. Read more about that in the [tutorial about messaging](tutorials/messaging.md). ## How can I specify what run time dependencies will my actor have? @@ -79,8 +72,7 @@ It should follow the [Contribution guidelines](contributing) and the [Best pract ## How can I debug my actor? Is there a standard/supported way how to log and get logs from actors/channels? -You can run your actor using the snactor tool and printing the output. [See the tutorial](first-actor) on how to use snactor. -Source: [How to create a Leapp actor for RHEL 7 to 8 upgrade](el7toel8/actor-rhel7-to-rhel8) +You can run your actor using the snactor tool and printing the output. [See the tutorial](tutorials/first-actor) on how to use snactor. ## Are there some technical limitations for an actor? E.g. maximum time execution, size of the input/output, libraries I can use... In case there are, is it possible to specify that the actor needs e.g. longer time for execution? diff --git a/docs/source/index.rst b/docs/source/index.rst index 87df57277..596c97456 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -8,16 +8,14 @@ Welcome to developer documentation for Leapp! :caption: Contents: :glob: - terminology - tutorials - leapp-repositories - infrastructure + building-blocks-and-architecture + devenv-install + tutorials/index best-practices test-actors - architecture - inplace-upgrade-workflow + libraries-and-api + packaging-and-dependencies contributing - python-coding-guidelines faq pydoc/leapp diff --git a/docs/source/inplace-upgrade-workflow.md b/docs/source/inplace-upgrade-workflow.md deleted file mode 100644 index 2b65c3864..000000000 --- a/docs/source/inplace-upgrade-workflow.md +++ /dev/null @@ -1,3 +0,0 @@ -# Inplace Upgrade Workflow - -[![In Place Upgrade Workflow](_static/images/inplace-upgrade-workflow.svg)](_static/images/inplace-upgrade-workflow.svg) diff --git a/docs/source/leapp-repositories.rst b/docs/source/leapp-repositories.rst deleted file mode 100644 index 8db98569a..000000000 --- a/docs/source/leapp-repositories.rst +++ /dev/null @@ -1,14 +0,0 @@ -Leapp repositories -================== - -Here you can find all the information related to -`leapp repositories `_, including the documentation -for existing leapp repositories managed by the OS and Application -Modernization Group (`OAMG `_) - - -.. toctree:: - :caption: Contents: - - repository-dir-layout - el7toel8/leapp-repository-el7toel8 diff --git a/docs/source/libraries-and-api.md b/docs/source/libraries-and-api.md new file mode 100644 index 000000000..105eada80 --- /dev/null +++ b/docs/source/libraries-and-api.md @@ -0,0 +1,8 @@ +# Libraries and API + +```{toctree} +:maxdepth: 2 +:caption: Contents: + +deprecation +``` diff --git a/docs/source/infrastructure.rst b/docs/source/packaging-and-dependencies.rst similarity index 74% rename from docs/source/infrastructure.rst rename to docs/source/packaging-and-dependencies.rst index c2001519b..2c0a70c9c 100644 --- a/docs/source/infrastructure.rst +++ b/docs/source/packaging-and-dependencies.rst @@ -1,5 +1,5 @@ -Infrastructure -============== +Packaging and dependencies +========================== Here you can find documentation related to our CI/CD, packaging, etc. @@ -7,6 +7,5 @@ Here you can find documentation related to our CI/CD, packaging, etc. :caption: Contents: dependencies - dependencies-leapp-repository compatibility-with-leapp-repository build-schema diff --git a/docs/source/terminology.md b/docs/source/terminology.md deleted file mode 100644 index b1754dc58..000000000 --- a/docs/source/terminology.md +++ /dev/null @@ -1,66 +0,0 @@ -# Terminology - -### Actor - -An actor in terms of the Leapp project is a step that is executed within a workflow. -Actors define what kind of data they expect, and what they produce. Actors also -provide a list of tags, with which actors mark their use cases. - -Actors scan the system and produce the information found as messages. -Other actors consume those messages to make decisions, or process the data -to produce new messages. -Some actors might apply changes to the system based on the information gathered earlier. - -### Message - -A message is produced by an actor, and the payload follows the definition of the [Model](#model) -it is named after. Messaging is a term used to describe the data exchange between [actors](#actor). - -### Model - -Models are the definition of the data model used in [message](#message) payloads. - -### Phase - -Phases are sections in a workflow dedicated to some specific area of execution. -A phase consists of three [stages](#stage): Before, Main, and After. -Phases are defined by assigning one or more tags to them, which will be used -to find actors in the [repositories](#repository) loaded. - -### Repository - -A repository is the place where all actors, models, tags, topics, and workflows are defined. -Additionally to that shared files, libraries and tools can be put into the repository. - -### Stage - -Stage is a part of a [phase](#phase). There are three defined stages: -- Before -- Main -- After - -Before and After stages can be used to allow an actor to be run before or after any -other actors in the phase. This should be useful in some hooking scenarios, where -an action is supposed to be happening before or after another action. This way, other -actors could be influenced. - -### Tag - -A tag allows the framework to find [actors](#actor) in the [repository](#repository) -and group their execution based on that tag. - -### Topic - -Topics are assigned to models and are used for grouping the data into areas of interest. - -### Workflow - -Workflows describe what work is going to be done and when. A workflow is describing a sequence of phases, -where one phase has assigned filters with which the framework selects actors that should be executed from -the repositories on the system. - -### Workflow APIs - -Workflow APIs are custom API classes that actors can use and automatically inherit their consumed and produced -messages. This way one can write a stable API for third party actor writers, without being affected by changes of -message model layout, name changes etc. diff --git a/docs/source/test-actors.md b/docs/source/test-actors.md index 967c2ad14..45f5cb73b 100644 --- a/docs/source/test-actors.md +++ b/docs/source/test-actors.md @@ -6,7 +6,7 @@ The Leapp actors are covered by three types of tests - unit, component and e2e. - Both unit and component tests are to be placed in the actor's _tests_ folder. - Unit and component tests modules should have unique names -- Tutorial on [How to write unit and component tests](unit-testing) +- Tutorial on [How to write unit and component tests](tutorials/unit-testing) ## Naming conventions diff --git a/docs/source/create-repository.md b/docs/source/tutorials/create-repository.md similarity index 87% rename from docs/source/create-repository.md rename to docs/source/tutorials/create-repository.md index 351065f9c..f6351d686 100644 --- a/docs/source/create-repository.md +++ b/docs/source/tutorials/create-repository.md @@ -1,6 +1,6 @@ # Creating a new repository for actor development -Leapp uses repositories with a defined [filesystem layout](repository-dir-layout). +Leapp uses repositories with a defined [filesystem layout](../repository-dir-layout). The snactor tool helps you to create a repository repository, in which you can develop and test your actors, tags, models, and topics. diff --git a/docs/source/debugging.md b/docs/source/tutorials/debugging.md similarity index 96% rename from docs/source/debugging.md rename to docs/source/tutorials/debugging.md index b1e222ec1..7222f6e82 100644 --- a/docs/source/debugging.md +++ b/docs/source/tutorials/debugging.md @@ -1,6 +1,6 @@ -## Debugging actors +# Debugging actors -### Snactor +## Snactor The snactor tool is used to debug your actors. You can execute actors and save their output, so that it can be consumed by other actors. @@ -11,7 +11,7 @@ the --debug parameter which sets the environment variable to '1' when it is used. In that case, it enables the debug logging, so that any actor that logs to self.log.debug gets its output printed on the commandline. -### PyCharm / rpdb +## PyCharm / rpdb You can configure PyCharm to debug by pointing it to the snactor path and passing the arguments on the command line. The PyCharm debugger will also follow the child processes that are created by the snactor tool to execute the actor diff --git a/docs/source/tutorials/deprecation.md b/docs/source/tutorials/deprecation.md new file mode 100644 index 000000000..b90a53ac8 --- /dev/null +++ b/docs/source/tutorials/deprecation.md @@ -0,0 +1,546 @@ +# How to deprecate entities in leapp? + +When you want to deprecate an entity in leapp projects, use the `deprecated` +decorator from `leapp.utils.deprecation` above the definition of the entity. +The decorator has three input parameters: + +- `since` (mandatory) - specifying the start of the deprecation protection period +- `message` (mandatory) - explaining that particular deprecation + (e.g. in case the deprecated functionality has a replacement, it is expected + it will be mentioned in the msg.) +- `stack_level_offset` (optional) - useful to adjust the position of the reported + usage in the deprecation message; e.g. in case of a base class or derived classes + +**Warning: possible change:** *It's possible the `stack_level_offset` parameter +will be removed (or ignored) in future, if we discover a way to improve +the deprecation of derived classes.* + +In case of a class deprecation, all derived classes are considered to be deprecated +as well. However, the current reporting could be a little bit confusing. To +improve that, the `stack_level_offset` option can be specified. +See [examples of the use of the @deprecated decorator for classes](deprecation.md#classes). + +When you mark any entity as deprecated and this entity is then used +in the code, users will be notified about that via a terminal and report +messages (see the previous section). However, as the author of the deprecation, +you know that the entity is deprecated and you do not want +to notify people about the code that still uses the deprecated entity +just for the reason to retain the original functionality. To suppress +the deprecation messages in such cases, use the `suppress_deprecation` +decorator taking as arguments objects that should not be reported +by the deprecation. E.g. in case you use it above the definition +of an actor, any use of the deprecated entity inside the actor +will not be reported. + +WARNING: It is strictly forbidden to use the `suppress_deprecation` decorator +for any purposes but one - retaining the original official functionality over +the deprecation protection period. If you see the message and you are not +the *provider* of the functionality, you have to update your code +to be independent on it. + +## Examples of a model deprecation + +Imagine we want to deprecate a *Foo* model that is produced in an actor called +*FooProducer* and consumed in an actor called *FooConsumer*. Let's keep this +example simple and say that we do not want to set any replacement of this +model. The first thing we have to do is to set the model definition as deprecated: + +```python +from leapp.models import Model, fields +from leapp.topics import SomeTopic +from leapp.utils.deprecation import deprecated + + +@deprecated(since='2020-06-20', message='This model has been deprecated.') +class Foo(Model): + topic = SomeTopic + value = fields.String() +``` + +If we do only this and execute actors that produce/consume messages of this model, +we will obtain messages like these (just example from the actor +producing the message after execution by snactor): + +```bash +# snactor run fooproducer +============================================================ + USE OF DEPRECATED ENTITIES +============================================================ + +Usage of deprecated Model "Foo" @ /path/to/repo/actors/fooproducer/actor.py:17 +Near: self.produce(Foo(value='Answer is: 42')) + +Reason: This model has been deprecated. +------------------------------------------------------------ + +============================================================ + END OF USE OF DEPRECATED ENTITIES +============================================================ +``` + +Apparently, the `Reason` is not so good. It's just example. In real world +example, you would like to provide usually a little bit better explanation. +Anyway, much more interesting is the point, that the message is now printed +every time the actor is executed. + +Obviously we do not want to remove the actor yet, because in such a case, the +model could be hardly called as deprecated - we need to keep the same +functionality during the protection period. But at the same time, we do not +want the deprecation message to be produced in this case, as it would be kind +of a spam for users who don't care about that model at all. + +The warning messages are focused on a "custom" use of the deprecated +model - i.e. when a developer creates their own actor producing/consuming a message +of the model. To fix this, suppress the deprecation message in this actor. + +To do it, the only thing that has to be done is to set the +`suppress_deprecation` decorator with the `Foo` as an argument (in this case) +before the actor, e.g.: + +```python +from leapp.actors import Actor +from leapp.models import Foo # deprecated model +from leapp.tags import IPUWorkflowTag, FactsPhaseTag +from leapp.utils.deprecation import suppress_deprecation + + +@suppress_deprecation(Foo) +class FooProducer(Actor): + """ + Just produce the right answer to the world. + """ + + name = 'foo_producer' + consumes = () + produces = (Foo,) + tags = (IPUWorkflowTag, FactsPhaseTag) + + def process(self): + self.produce(Foo(value='Answer is: 42')) +``` + +This is the most simple case. Let's do a small change and produce the message +inside the private actor's library instead. The library looks like this: + +```python +from leapp.models import Foo # deprecated model +from leapp.libraries.stdlib import api + +def produce_answer(): + api.produce(Foo(value='Answer is: 42')) +``` + +And the updated actor looks like this: + +```python +from leapp.actors import Actor +from leapp.libraries.actor import fooproducer_lib +from leapp.models import Foo # deprecated model +from leapp.tags import IPUWorkflowTag, FactsPhaseTag +from leapp.utils.deprecation import suppress_deprecation + + +@suppress_deprecation(Foo) +class FooProducer(Actor): + """ + Just produce the right answer to the world. + """ + + name = 'foo_producer' + consumes = () + produces = (Foo,) + tags = (IPUWorkflowTag, FactsPhaseTag) + + def process(self): + fooproducer_lib.produce_answer() +``` + +Now, if you execute the actor again you still won't get any +deprecation message. So the `suppress_deprecation` decorator works transitively +as expected. However, even when the actor is treated well, the current +implementation could affect the result of unit tests. To explain the idea of what +could be wrong, imagine a unit test like this one: + +```python +from leapp.libraries.actor import fooproducer_lib +from leapp.libraries.common.testutils import produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import Foo # deprecated model + +def test_process(monkeypatch): + produced_msgs = produce_mocked() + monkeypatch.setattr(api, 'produce', produced_msgs) + fooproducer_lib.produce_answer() + assert Foo(value='Answer is: 42') in produced_msgs.model_instance +``` + +If you run the test, you will get output like this (shortened): + +```python +| 21:48:01 | conftest | INFO | conftest.py | Actor 'foo_producer' context teardown complete + +repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process PASSED + +================================================== warnings summary ================================================== +repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process + /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/libraries/fooproducer_lib.py:5: _DeprecationWarningContext: Usage of deprecated Model "Foo" + api.produce(Foo(value='Answer is: 42')) + /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py:10: _DeprecationWarningContext: Usage of deprecated Model "Foo" + assert Foo(value='Answer is: 42') in produced_msgs.model_instances + +-- Docs: http://doc.pytest.org/en/latest/warnings.html +======================================== 1 passed, 2 warnings in 0.13 seconds ======================================== +``` + +As you can see the warning have been generated again. This time on two places, +in `test_process` and `produce_answer` functions. Unless warning messages affect +results of tests, we do not require strictly to handle them. However, it's a good +practice. But if the warning log could affect the test (e.g. if a test function +checks logs) it should be treated by the `suppress_deprecation` decorator too. +In case of the library function, just add the `@suppress_deprecation(Foo)` line +before the definition of the `produce_answer` function. But if we do the same +for the test function, we will get an error (see that we have now just one +deprecation warning now): + +``` +| 21:59:57 | conftest | INFO | conftest.py | Actor 'foo_producer' context teardown complete +repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process FAILED + +====================================================== FAILURES ====================================================== +____________________________________________________ test_process ____________________________________________________ + +args = (), kwargs = {'monkeypatch': <_pytest.monkeypatch.MonkeyPatch object at 0x7f21924b24d0>} +suppressed = + + @functools.wraps(target_item) + def process_wrapper(*args, **kwargs): + for suppressed in suppressed_items: + _suppressed_deprecations.add(suppressed) + try: + return target_item(*args, **kwargs) + finally: + for suppressed in suppressed_items: +> _suppressed_deprecations.remove(suppressed) +E KeyError: + +tut/lib/python3.7/site-packages/leapp/utils/deprecation.py:35: KeyError +================================================== warnings summary ================================================== +repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process + /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py:13: _DeprecationWarningContext: Usage of deprecated Model "Foo" + assert Foo(value='Answer is: 42') in produced_msgs.model_instances + +-- Docs: http://doc.pytest.org/en/latest/warnings.html +======================================== 1 failed, 1 warnings in 0.21 seconds ======================================== +``` + +It's because the mechanism of decorators in python and how pytest works. +In this case, we need to do a small workaround, like this: + +```python +from leapp.libraries.actor import fooproducer_lib +from leapp.libraries.common.testutils import produce_mocked +from leapp.utils.deprecation import suppress_deprecation +from leapp.libraries.stdlib import api +from leapp.models import Foo # deprecated model + + +@suppress_deprecation(Foo) +def _foo(value): + """Small workaround to suppress deprecation messages in tests.""" + return Foo(value=value) + +def test_process(monkeypatch): + produced_msgs = produce_mocked() + monkeypatch.setattr(api, 'produce', produced_msgs) + fooproducer_lib.produce_answer() + assert _foo(value='Answer is: 42') in produced_msgs.model_instances +``` + +That's the whole solution for the *FooProducer* actor. Analogically to this, +we need to treat the `FooConsumer` actor. You could notice that all imports +of the `Foo` model are commented. It's a good practice as it is more visible +to all developers that a deprecated entity is present. + +## Example of a model replacement + +This is analogous to the previous case. Take the same scenario, but extend it with +the case in which we want to replace the `Foo` model by the `Bar` model. What +will be changed in case of deprecation in the model definition? Just +the deprecation message and the new model definition: + +```python +from leapp.models import Model, fields +from leapp.topics import SomeTopic +from leapp.utils.deprecation import deprecated + + +@deprecated(since='2020-06-20', message='The model has been replaced by Bar.') +class Foo(Model): + topic = SomeTopic + value = fields.String() + + +class Bar(Model): + topic = SomeTopic + value = fields.String() +``` + +You can see that in this case, the model has been just renamed, to keep +it simple. But it's sure that the new model can be different from the original +one (e.g. a different name of fields, a different purpose and set of fields,...). +The change in the `FooProducer` will be just extended by handling the new +model (it should include update of tests as well; I am skippin the example +as the change is trivial): + +```python +from leapp.actors import Actor +from leapp.models import Bar +from leapp.models import Foo # deprecated model +from leapp.tags import IPUWorkflowTag, FactsPhaseTag +from leapp.utils.deprecation import suppress_deprecation + + +@suppress_deprecation(Foo) +class FooProducer(Actor): + """ + Just produce the right answer to the world. + """ + + name = 'foo_producer' + consumes = () + produces = (Bar, Foo) + tags = (IPUWorkflowTag, FactsPhaseTag) + + def process(self): + self.produce(Foo(value='Answer is: 42')) + self.produce(Bar(value='Answer is: 42')) +``` + +As you can see, the only thing that have been changed is the added production of the +`Bar` message. The original functionality is still present. + +## Example of a derived model deprecation + +**Warning: Known issue**: *The deprecation of a derived model is currently buggy and the documented +solution will end probably with traceback right now. The fix will be delivered +in the next release of leapp (current one is 0.11.0). As well, we will try to +simplify the final solution, so maybe this section will be more simple with +the next release.* + +It's a common situation, that some models are derived from others. Typical +example is a base model which is actually not produced or consumed +by actors, but it is used as a base model for other models. For the sake of simplicity, +we'll use one of our previous solutions, but update the definition +of the `Foo` model (skipping imports): + +```python +@deprecated(since='2020-01-1', message='This model has been deprecated.') +class BaseFoo(Model): + topic = SystemInfoTopic + value = fields.String() + + +class Foo(BaseFoo): + pass +``` + +Previously, the content was handled completely, but with the new change, we +will see the warnings again: + +``` +================================================== warnings summary ================================================== +repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py::test_process + /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/libraries/fooproducer_lib.py:7: _DeprecationWarningContext: Usage of deprecated Model "BaseFoo" + api.produce(Foo(value='Answer is: 42')) + /tmp/leapp-repository/repos/system_upgrade/el7toel8/actors/fooproducer/tests/test_unit_fooproducer.py:10: _DeprecationWarningContext: Usage of deprecated Model "BaseFoo" + return Foo(value=value) +``` + +See that the reported deprecated model is `BaseFoo`, however only +the `Foo` model is produced. This could be very confusing to users and developers. The +deprecation of the `BaseFoo` model could resolve our troubles, in the meaning that +the base class and all other classes are covered already, that the deprecated +entity has been used. But it is confusing, that with this solution, you have +to update the code in actor, to suppress the `BaseFoo` model instead of `Foo`, +even when `BaseFoo` is not used anywhere directly in the code. I mean +something like this: + +```python +from leapp.models import Foo, BaseFoo +from leapp.libraries.stdlib import api +from leapp.utils.deprecation import suppress_deprecation + +@suppress_deprecation(BaseFoo) +def produce_answer(): + api.produce(Foo(value='Answer is: 42')) +``` + +Now, I will put here just several ideas what could user do and why these are +wrong (if you are interested just about the working correct solution, skip +after the list): + +1. *Deprecate all models (`BaseFoo`, `Foo`)* - The result will be two + deprecation message per one use of `Foo`. One with the `Foo` msg, one with + `BaseFoo`. That's not good, as we would like to get rid of `BaseFoo` + completely in the messages ideally. +1. *Deprecate just the derived models (`Foo`)* - That could resolve the problem, + but what if someone else derive a new model from the base one? They will not + be notified about the deprecation and removal will break their code instantly. + +If you want to ensure that all models (base and derived) are deprecated, the +best solution we are able to come up with it's little weird and it's going +slightly against our requirement (we are going to do an exception here), that +inside models cannot be defined any method or logic as they are supposed to be +used just as the *data container*. But this one is currently only possible way, +to deprecate such models correctly without confusing messages: + +```python +from leapp.models import Model, fields +from leapp.topics import SystemInfoTopic +from leapp.utils.deprecation import deprecated +from leapp.utils.deprecation import suppress_deprecation + + +@deprecated(since='2020-01-01', message='This model has been deprecated.') +class BaseFoo(Model): + topic = SystemInfoTopic + value = fields.String() + + +@deprecated(since='2020-01-01', message='This model has been deprecated.') +class Foo(BaseFoo): + @suppress_deprecation(BaseFoo) + def __init__(self, *args, **kwargs): + super(Foo, self).__init__(*args, **kwargs) +``` + +As you see, both models are deprecated. But in the derived one, there is the +`__init__` method defined, just for the purpose to be able to suppress the +deprecation message from the base model. Implementing this, the solution for +suppressing the deprecation warning in previous section will be working, without +any confusing messages. As well, this is the only possible usecase for a method +inside the models in official repositories managed by the OAMG team. + +## Additional various outputs of snactor and leapp + +### snactor warning message example + +``` +============================================================ + USE OF DEPRECATED ENTITIES +============================================================ + +Usage of deprecated function "deprecated_function" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:19 +Near: deprecated_function() + +Reason: This function is no longer supported. +------------------------------------------------------------ +Usage of deprecated Model "DeprecatedModel" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:20 +Near: self.produce(DeprecatedModel()) + +Reason: This model is deprecated - Please do not use it anymore +------------------------------------------------------------ +Usage of deprecated class "DeprecatedNoInit" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:21 +Near: DeprecatedNoInit() + +Reason: Deprecated class without __init__ +------------------------------------------------------------ +Usage of deprecated class "DeprecatedBaseNoInit" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:22 +Near: DeprecatedNoInitDerived() + +Reason: Deprecated base class without __init__ +------------------------------------------------------------ +Usage of deprecated class "DeprecatedWithInit" @ /Users/vfeenstr/devel/work/leapp/leapp/tests/data/deprecation-tests/actors/deprecationtests/actor.py:23 +Near: DeprecatedWithInit() + +Reason: Deprecated class with __init__ +------------------------------------------------------------ + +============================================================ + END OF USE OF DEPRECATED ENTITIES +============================================================ +``` + +### leapp report example entries + +``` +---------------------------------------- +Risk Factor: medium +Title: Usage of deprecated class "IsolatedActions" at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/libraries/repofileutils.py:38 +Summary: IsolatedActions are deprecated +Since: 2020-01-02 +Location: /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/libraries/repofileutils.py:38 +Near: def get_parsed_repofiles(context=mounting.NotIsolatedActions(base_dir='/')): + +---------------------------------------- +Risk Factor: medium +Title: Usage of deprecated class "IsolatedActions" at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/scansubscriptionmanagerinfo/libraries/scanrhsm.py:8 +Summary: IsolatedActions are deprecated +Since: 2020-01-02 +Location: /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/scansubscriptionmanagerinfo/libraries/scanrhsm.py:8 +Near: context = NotIsolatedActions(base_dir='/') + +---------------------------------------- +Risk Factor: medium +Title: Usage of deprecated function "deprecated_method" at /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/deprecationdemo/actor.py:21 +Summary: Deprecated for Demo! +Since: 2020-06-17 +Location: /usr/share/leapp-repository/repositories/system_upgrade/el7toel8/actors/deprecationdemo/actor.py:21 +Near: self.deprecated_method() + +---------------------------------------- +``` + +## Additional simple usage examples of @deprecated + +### Functions + +```python +... +from leapp.utils.deprecation import deprecated + + +@deprecated(since='2020-06-20', message='This function has been deprecated!') +def some_deprecated_function(a, b, c): + pass +``` + +### Models + +```python +... +from leapp.utils.deprecation import deprecated + + +@deprecated(since='2020-06-20', message='This model has been deprecated!') +class MyModel(Model): + topic = SomeTopic + some_field = fields.String() +``` + +### Classes + +```python +... +from leapp.utils.deprecation import deprecated + + +@deprecated(since='2020-06-20', message='This class has been deprecated!') +class MyClass(object): + pass + + +# NOTE: Here we need to offset the stacklevel to get the report of the usage not in the derived class constructor +# but where the derived class has been created. +# How many levels you need, you will have to test, it depends on if you use the builtin __init__ methods or not, +# however it gives you the ability to go up the stack until you reach the position you need to. +@deprecated(since='2020-06-20', message='This class has been deprecated!', stack_level_offset=1) +class ABaseClass(object): + def __init__(self): + pass + + +class ADerivedClass(ABaseClass) + def __init__(self): + super(ADerivedClass, self).__init__() +``` diff --git a/docs/source/dialogs.md b/docs/source/tutorials/dialogs.md similarity index 97% rename from docs/source/dialogs.md rename to docs/source/tutorials/dialogs.md index d863fabce..2ae7e3812 100644 --- a/docs/source/dialogs.md +++ b/docs/source/tutorials/dialogs.md @@ -10,7 +10,7 @@ As an example we will change [IpResolver](messaging.md#creating-a-message-consum will decide which hostnames will be resolved. -### Creating the dialog +## Creating the dialog Import Dialog and MultipleChoiceComponent from leapp.dialog and leapp.dialog.components respectively. Create an instance of Dialog, specifying scope which is used to identify data in the answer file, @@ -38,7 +38,7 @@ class IpResolver(Actor): description='No description'),)),) ``` -### Using the dialog and the answers +## Using the dialog and the answers To pose a question that needs to be answered use get_answers method and pass the dialog containing the question. @@ -70,7 +70,7 @@ def process(self): self.produce(ResolvedHostname(name=hostname, ips=ips)) ``` -### Explaining the dialogs processing mechanism during the upgrade +## Explaining the dialogs processing mechanism during the upgrade The upgrade itself, from the operator's point of view, consists of 3 distinct stages: preupgrade, remediate and upgrade. diff --git a/docs/source/first-actor.md b/docs/source/tutorials/first-actor.md similarity index 100% rename from docs/source/first-actor.md rename to docs/source/tutorials/first-actor.md diff --git a/docs/source/tutorials.rst b/docs/source/tutorials/index.rst similarity index 72% rename from docs/source/tutorials.rst rename to docs/source/tutorials/index.rst index 1af89f7bf..7d0301194 100644 --- a/docs/source/tutorials.rst +++ b/docs/source/tutorials/index.rst @@ -4,11 +4,8 @@ Tutorials .. toctree:: :caption: Contents: - devenv-install create-repository first-actor - el7toel8/actor-rhel7-to-rhel8 - el7toel8/inhibit-rhel7-to-rhel8 messaging dialogs repo-linking diff --git a/docs/source/messaging.md b/docs/source/tutorials/messaging.md similarity index 95% rename from docs/source/messaging.md rename to docs/source/tutorials/messaging.md index f1f56368c..6472fd516 100644 --- a/docs/source/messaging.md +++ b/docs/source/tutorials/messaging.md @@ -1,4 +1,4 @@ -## Using messaging to send data between actors +# Using messaging to send data between actors The Leapp framework uses messages to send data to other actors that are executed afterward. Messages are defined through the models declared [earlier](first-actor.md#creating-a-model). Actors can consume these messages and produce data based on their input. @@ -6,7 +6,7 @@ Messages are defined through the models declared [earlier](first-actor.md#creati As an example, the actors consume Hostname messages, resolve IPs for those hostnames, and create the ResolvedHostname model to send a new type of message. -### Creating the ResolvedHostname model +## Creating the ResolvedHostname model Create the ResolvedHostname model by using the snactor tool. @@ -39,7 +39,7 @@ fields.Nullable(fields.String()) are required. -### Creating a message consuming actor +## Creating a message consuming actor Create a new actor that resolves the IPs for the hostnames: @@ -96,7 +96,7 @@ class IpResolver(Actor): self.produce(ResolvedHostname(name=hostname.name, ips=ips)) ``` -### Storing messages in the repository data for reuse +## Storing messages in the repository data for reuse The `snactor` framework tool saves the output of actors as locally stored messages, so that they can be consumed by other actors that are being developed. @@ -110,7 +110,7 @@ $ snactor run --save-output HostnameScanner The output of the actor is stored in the local repository data file, and it can be used by other actors. To flush all saved messages from the repository database, run `snactor messages clear`. -### Testing the new actor +## Testing the new actor With the input messages available and stored, the actor can be tested. @@ -136,6 +136,6 @@ $ snactor run --print-output IpResolver ] ``` -#### Screencast +### Screencast diff --git a/docs/source/repo-linking.md b/docs/source/tutorials/repo-linking.md similarity index 100% rename from docs/source/repo-linking.md rename to docs/source/tutorials/repo-linking.md diff --git a/docs/source/unit-testing.md b/docs/source/tutorials/unit-testing.md similarity index 98% rename from docs/source/unit-testing.md rename to docs/source/tutorials/unit-testing.md index f721d5553..1571ba182 100644 --- a/docs/source/unit-testing.md +++ b/docs/source/tutorials/unit-testing.md @@ -2,7 +2,7 @@ The Leapp framework provides support for easily writing unit and component tests for actors and also allows easy execution of the whole actors within -those tests. See [this document](test-actors) +those tests. See [this document](../test-actors) to find out what is the difference between unit and component tests. ## Getting started with writing tests @@ -161,7 +161,7 @@ shared libraries, models, etc. ### Actors's test dependencies If your **actor's tests** require a special package for their execution, create a -Makefile in the [actor's root directory](repository-dir-layout) with an +Makefile in the [actor's root directory](../repository-dir-layout) with an `install-deps` target calling `yum install -y`. ```sh diff --git a/docs/source/workflow-apis.md b/docs/source/tutorials/workflow-apis.md similarity index 100% rename from docs/source/workflow-apis.md rename to docs/source/tutorials/workflow-apis.md diff --git a/docs/source/working-with-workflows.md b/docs/source/tutorials/working-with-workflows.md similarity index 100% rename from docs/source/working-with-workflows.md rename to docs/source/tutorials/working-with-workflows.md diff --git a/leapp/actors/__init__.py b/leapp/actors/__init__.py index 9d83bf105..7dbc4246e 100644 --- a/leapp/actors/__init__.py +++ b/leapp/actors/__init__.py @@ -43,20 +43,23 @@ class Actor(object): consumes = () """ - Tuple of :py:class:`leapp.models.Model` derived classes defined in the :ref:`repositories ` - that define :ref:`messages ` the actor consumes. + Tuple of :py:class:`leapp.models.Model` derived classes defined in the + :ref:`repositories ` + that define :ref:`messages ` the actor consumes. """ produces = () """ - Tuple of :py:class:`leapp.models.Model` derived classes defined in the :ref:`repositories ` - that define :ref:`messages ` the actor produces. + Tuple of :py:class:`leapp.models.Model` derived classes defined in the + :ref:`repositories ` + that define :ref:`messages ` the actor produces. """ tags = () """ - Tuple of :py:class:`leapp.tags.Tag` derived classes by which :ref:`workflow ` - :ref:`phases ` select actors for execution. + Tuple of :py:class:`leapp.tags.Tag` derived classes by which + :ref:`workflow ` + :ref:`phases ` select actors for execution. """ dialogs = () diff --git a/leapp/topics/__init__.py b/leapp/topics/__init__.py index 90acef44f..7db95d427 100644 --- a/leapp/topics/__init__.py +++ b/leapp/topics/__init__.py @@ -17,7 +17,7 @@ def __new__(mcs, name, bases, attrs): class Topic(with_metaclass(TopicMeta)): - """ Base class for all :ref:`topics `""" + """ Base class for all :ref:`topics `""" name = None """ Name of the topic in snake case """ diff --git a/leapp/workflows/__init__.py b/leapp/workflows/__init__.py index 1b6fc9804..78aab0be7 100644 --- a/leapp/workflows/__init__.py +++ b/leapp/workflows/__init__.py @@ -68,7 +68,7 @@ class _ConfigPhase(Phase): class Workflow(with_metaclass(WorkflowMeta)): """ - Workflow is the base class for all :ref:`workflow ` definitions. + Workflow is the base class for all :ref:`workflow ` definitions. """ name = None