Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 controller #76

Closed
wants to merge 26 commits into from
Closed

V2 controller #76

wants to merge 26 commits into from

Conversation

x00Pavel
Copy link
Member

@x00Pavel x00Pavel commented Jun 14, 2022

Implementation of Controller class.

The controller contains methods for system preparation and should act as a glue between CLI - models and test - models.

Design changes

None for now

Issue resolved/closed

this list might be updated as PR leaves and new feautres/bugs are fixed:

@x00Pavel x00Pavel self-assigned this Jun 14, 2022
:return: value of the key in section (if set)
"""
if section is None:
# simple config files without sections
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support for getting values from section-less config is fragile and due to lack of "logic" in section-less configs this can not be reliably improved. This will return value for first occurrence, however, there can be multiple occurrences of the same key in section-less file. As we discussed earlier, section-less config files are problematic and we support them rather symbolically. There should be at least some warning saying "You are applying get method on non-standard config file. This method would return only the first match". However, this is problematic itself, because some tools may use last match rather than first. IMHO, better approach would be to check if the key is present in the config multiple times and raise an exception if true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please fix this via PR?

file do not support sections (section for this method is not provided),
then file would be parsed in a simple way with `.readlines()` method and
using separator each line would be split into two parts: key and a
value. If key is matched, the value is striped and returned.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should explicitly mention that first match is returned or rewrite this method (see below).

@@ -414,3 +476,7 @@ def save(self):
else:
# in case set method was used
self._default_parser.write(config)

@property
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method can be removed as it is already in parent class.

@@ -53,6 +54,10 @@ def __init__(self, filepath: Union[str, Path], template: str = None):
if template is not None:
self._template = Path(template)

@property
def path(self):
Copy link
Collaborator

@mahavrila mahavrila Jun 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add simple one-line docstring for purposes of auto-generated documentation?

@@ -144,6 +199,12 @@ def clean(self):
except FileNotFoundError:
logger.info(f"{self._conf_file} does not exist. Nothing to do.")

def backup(self):
new_name = f"{self._conf_file.name}.backup"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring missing, please add at least one-liner so auto-generated documentation print something.

@@ -144,6 +199,12 @@ def clean(self):
except FileNotFoundError:
logger.info(f"{self._conf_file} does not exist. Nothing to do.")

def backup(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to discuss back-ups as for example SSSDConf class has it's own backup mechanism and for other files we did not consider backing-up at all. If we keep this method here, SSSDConf class should return exception if we try to apply it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I don't see backup method in SSSD class. So, this means that backup there is done in some other method of the SSSD class (what is not good) or somewhere else (what is also not very good). To avoid usage of this parent class method SSSDConf class only should implement its own method called backup. I didn't check this because I made changes only related to the controller by itself and didn't look for side effects because this work would be much bigger. From this perspective, I would suggest you add this method via PR. What do you think about this?

@@ -261,6 +322,7 @@ def save(self):
# to empty parser object
self._changes = ConfigParser()
self._changes.optionxform = str
os.chmod(self._conf_file, 0o600)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this should be done here or directly in controller. But if it happens here, docstring needs to be modified.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be done here because after this method is called it is expected that the file is in a ready-to-use state. So, will add docstring

@@ -0,0 +1,104 @@
from cryptography.hazmat.primitives import serialization
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add module docstring please.


run(["semodule", "-i", f"{TEMPLATES_DIR}/virtcacard.cil"])

run(["systemctl", "restart", "pcscd"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To discuss: Should we restart services from one place (controller) or from diverse methods and functions? IMHO, this should be controlled by controller.

Copy link
Member Author

@x00Pavel x00Pavel Jun 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This topic needs more discussion

return OSVersion.RHEL_9
elif "Red Hat Enterprise Linux release 8" in cnt:
return OSVersion.RHEL_8
else:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will consider any other OS version as Fedora. For example RHEL-10 will be fedora and this may cause troubles in the future. In case there is no match fo RHEL-9 and RHEL-8, we should try to match Fedora and raise an exception if non of RHEL-9, RHEL-8 and Fedora is matched.

:param packages: list of packages to be installed
"""
for pkg in packages:
logger.warning(f"Package {pkg} is not installed on the "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does not know if package is installed or not and just try to install it. Log message may be updated to reflect it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, everything is correct. this function is called after _check_packages, which returns a list of missing packages. But yes, this log message is redundant here, will remove it

out = run(["rpm", "-qa", pkg])
if pkg not in out.stdout:
logger.warning(f"Package {pkg} is required for the testing, "
f"but doesn't present in the system")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is not instead of doesn't

run(['userdel', '-f', self.username], check=True)
except KeyError:
pass
logger.info(f"User {self.username} does not present on the system")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is instead of does

run(cmd, check=True)
cmd = ["passwd", self.username, "--stdin"]
run(cmd, input=self.password)
logger.info(f"User {self.username} presents ons the system")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is present instead of presents and fix typo in on

pass
logger.info(f"User {self.username} does not present on the system")

def add_user(self, force=False):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are adding some logic in creating and deleting users, we may want to add also some unit-tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. But again, the same resolution as for this comment, this PR is about the controller and I'm trying to not make it anormous

@@ -167,3 +189,14 @@ def delete_user(self):
run(['rm', '-r', '-f', f"/home/{self.username}"], check=True)
logger.info(f"User {self.username} directory is removed.")
logger.debug(r)

def gen_csr(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring is missing.

@@ -0,0 +1,375 @@
import json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

module docstring missing

if install_missing:
_install_packages(missing)

run(['dnf', 'groupinstall', "Smart Card Support", '-y'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want conditional execution here as Smart Card Support may already be installed at some systems.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to check it explicitly, because this would be skipped by the dnf if all packages are present


with self._lib_conf_path.open("r") as f:
self.lib_conf = json.load(f)
assert self.lib_conf, "Data are not loaded correctly."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this assert be after the validate_configuration function in the next line?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it shouldn't. This assert checks that json.load() loaded the file without problems. In validate_configuration there is schema.validate() method that would raise an expectation if the configuration doesn't match the schema of correctly loaded JSON file

"""
...

def setup_system(self, install_missing: bool, gdm: bool):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install_missing and gdm should default to False probably

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it is. These default values would be set in CLI and passed here in anyway

logger.info("Set force argument to True if you want to remove "
"previous installation.")
return
self.ipa_ca.restore()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need ipa_ca.restore() here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will remove IPA client installation if force option is set

if not force:
logger.info("Set force argument to True if you want to remove "
"previous installation.")
return

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we raise an exception? The user expects IPA to be configured, but if an installation already exists and he didn't use 'force', we should probably raise an exception

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. Imagine that the user installed and configured IPA client on his own and wants to use the library with this configuration. Then it is not a problem that IPA is already installed.

cert=user_dict["cert"],
key=user_dict["key"])

new_user.add_user()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need create and save here also?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create and save for what? user is just added to the system. this action includes "creation" and "saving"

Comment on lines 267 to 268
def cleanup(self):
...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this is still in progress?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Comment on lines +272 to +318
Validate schema of the configuration file. If some value doesn't present
in the config file, this value would be looked in the CLI parameters

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should prioritize CLI parameters over config file, I think it might make more sense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, makes sense. But let's leave this until the implementation of CLI commands would be in progress.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an Issue #84

Comment on lines +321 to +368
with virtual smart card. Also, repository for installing virt_cacard
package is added in this method.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the virt_cacard repo be added in setup_system before we try to install virt_cacard?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it is added before installation here. But this call should be removed from this function

Comment on lines +348 to +395
rmtree("/var/lib/sss/mc/*", ignore_errors=True)
rmtree("/var/lib/sss/db/*", ignore_errors=True)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these directories something that we can safely remove?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. it is cache directories

_nssdb: Path = None
_template: Path = Path(TEMPLATES_DIR, "virt_cacard.service")
_pattern = r"(pkcs11:model=PKCS%2315%20emulated;" \
r"manufacturer=Common%20Access%20Card;serial=.*)"

def __init__(self, user: User, insert: bool = False,
softhsm2_conf: Path = None):
def __init__(self, user, insert: bool = False, ):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing comma

@x00Pavel
Copy link
Member Author

Closing this PR because it is too big. Several smaller PR's will follow

  1. Add "setup" methods in Controller class #86
  2. Dump and load methods #87
  3. Futher will follow

@x00Pavel x00Pavel closed this Jul 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants