-
Notifications
You must be signed in to change notification settings - Fork 19
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
Update installer.py #19
base: master
Are you sure you want to change the base?
Conversation
additional_rpms is json list, not just a single entity. Hence it makes sense to iterate for each dir and invoke the rpm
@spyroot, we have received your signed contributor license agreement. The review is usually completed within a week, but may take longer under certain circumstances. Another comment will be added to the pull request to notify you when the merge can proceed. |
@spyroot, we have received your signed contributor license agreement. The review is usually completed within a week, but may take longer under certain circumstances. Another comment will be added to the pull request to notify you when the merge can proceed. |
@spyroot, VMware has approved your signed contributor license agreement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, what's the need for this requirement?
We expect all extra rpms to be present at one place and keep it simple. RPM files at different locations during install time looks unusual.
@@ -926,10 +926,28 @@ def _setup_install_repo(self): | |||
def _install_additional_rpms(self): | |||
rpms_path = self.install_config.get('additional_rpms_path', None) | |||
|
|||
if not rpms_path or not os.path.exists(rpms_path): | |||
rpm_dir = [] | |||
if not rpms_path or len(rpm_dir) == 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tested this @spyroot ? AFAIK len(rpm_dir)
is always 0 here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, what's the need for this requirement? We expect all extra rpms to be present in one place and keep it simple. RPM files at different locations during installation time look unusual.
Short answer : )
https://github.com/spyroot/photon_rt/blob/main/post.sh
Two cases. Consider during a boot, you really need to figure out where to look for RPMs. For example, you have the latest version for rpm without collision with the main RPM inside ISO in packages_rt_expanded.
- i.e., you want to indicate
/mnt/media
/mnt/cdrom
/mnt/dvd
etc
So if this name changes, you need to change all customization.
Assume you have a set of mandatory RPMs. For example, you need to build
the latest intel driver, you need to build the
latest kernel module, you need to build the latest DPKD, etc. So you really need
a toolchain with all dependencies. Now you want all this toolchain available on
the first boot. Install, reboot, post recompile the kernel module, and you
will have the latest driver.
https://github.com/spyroot/photon_rt/blob/main/post.sh
- If you want split no arch vs. SRPM vs. rpm, i.e., have a similar structure to match ISO.
(i.e., some structure so you don't put rpm and mess all together) I hope it makes sense.
https://packages.vmware.com/photon/5.0/photon_updates_5.0_x86_64/
For example.
- noarch
- x86_64
- arm
If you keep a flat structure, you can technically put RPM that will collide.
So on postscript customization, you can resolve where to look, fallback, etc.
"additional_packages": [
"docker",
"vim",
"gcc",
"git",
"wget",
"linux-rt",
"linux-rt-devel",
"make",
"build-essential"
],
"additional_rpms_path": "/mnt/media/direct_rpms",
"postinstallscripts": [
"post.sh",
"overwrite.env"
],
"search_path": [
"/mnt/media",
"/tmp",
"/mnt/media/direct_rpms",
"/mnt/media/git_images",
"/mnt/media/direct",
"/"
],
if self.cmd.run(['rpm', '--root', self.photon_root, '-U', rpms_path + '/*.rpm']) != 0: | ||
if isinstance(rpms_path, list): | ||
rpm_dir = [d for d in rpm_dir if len(d) > 0 | ||
and os.path.exists(d)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't check for len(d) > 0, if user provides invalid rpm paths, it should be caught.
Throw exception if path doesn't exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I specifically did this because there is an issue with how cmd returns the exit code.
The exit code that we use is not a sufficient condition. I did a separate patch for that.
In essence, rpm can execute, but it returns an exit code that indicates something
different than an error. i.e., it is not a reliable indication. ( I need to cross-check my notes.
But consider the same RPM present in the main RPM dir and additional, i.e., something already installed, something requires another RPM. (From my memory, I'll check the rpm return for all these case exit codes != 0 )
Could you please also check I did a separate commit specifically for run
process = subprocess.Popen(
cmd, shell=use_shell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
So if an exception is raised, that retval is 0.
FileNotFoundError etc.
That way, the logical step considers this as the best-effort operation.
"Attempts" since it is additional RPMs technically optional, you can finish
the installation without crashing the installer. (Consider a person who wrote kick start
and put to additional rpm version of rpm that > then in ISO RPM, Person put the
same rpm by mistake that already in ISO RPM to additional rpm dir)
Because you can debug and fix any issue if we don't crash,
While if you raise an exception, it takes time to nail down the problem.
(When I say debug, I imply someone who wrote the kick-start file needs
the option to install and post-check why some rpm not install, etc.).
So the command run can interleave with the parent PID, then we have a concurrency issue.
Subprocess can raise exceptions ( run time exception, IO exception, etc)
i.e
process = subprocess.Popen(
cmd, shell=use_shell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
This subprocess call might be executed before CDROM is mounted, and we don't read from stderr.
So we need a way to detect it and handle it.
try: | ||
# best effort for each dir in list | ||
cmd_err = self.cmd.run(['rpm', '--root', self.photon_root, '-U', rdir + '/*.rpm']) | ||
except FileNotFoundError as _: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, don't ignore erroneous situations.
Small optimization here can be, passing all rpm dirs at once to rpm --root ... -U ...
Like:
rpm --root ... -U path1/*.rpm path2/*.rpm ...
An rpm in path1 might need an rpm from path2 as a requirement, so it will get satisfied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually we should use tdnf
so it can resolve all dependencies.
@@ -926,10 +926,28 @@ def _setup_install_repo(self): | |||
def _install_additional_rpms(self): | |||
rpms_path = self.install_config.get('additional_rpms_path', None) | |||
|
|||
if not rpms_path or not os.path.exists(rpms_path): | |||
rpm_dir = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: please rename to rpms_dir
.
and os.path.exists(d)] | ||
else: | ||
if os.path.exists(rpms_path): | ||
rpm_dir.append(rpm_dir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you meant to do rpm_dir.append(rpms_path)
, so we have list with just rpms_path
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, So old behavior should work so it won't break old kick-start files., i.e if it is just a single value or if it lists, we check one by one as best effort. i.e., if you wrote kick start and have no clue where CDROM mounted /mnt/cdrom / mnt/media etc. so you can add a bunch of dir.
except FileNotFoundError as _: | ||
pass | ||
|
||
if cmd_err > 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be within the loop above or we'd only see if the last iteration failed.
if self.cmd.run(['rpm', '--root', self.photon_root, '-U', rpms_path + '/*.rpm']) != 0: | ||
if isinstance(rpms_path, list): | ||
rpm_dir = [d for d in rpm_dir if len(d) > 0 | ||
and os.path.exists(d)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should throw an error if the path does not exist.
Better yet, check if the directory contains any rpm files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in this patch, I indicate what the issue is closely related.
https://github.com/spyroot/photon-os-installer/tree/patch-2
The problem is that sub-process might not report the exit code.
So if you have an exception raised. We don't know about that; it not reported from cmd. run,
the same issue if you have a run time exception or if the child PID interleaved.
Since we don't read from stderr and parse. Another issue sub-process might
actually, execute before CDROM is even mounted.
If you check the exit code from the rpm tool, you will see it returns different exit codes
that is another issue.
Suppose RPM in additional rpm has conflict, i.e., ver. > Then, in the RPM in actual ISO,
if RPM is already installed, or exception raised, or a version of rpm i
n additional < than in ISO rpms.
For example, if generated in the script additional rpms, you always
download python3_something. ( latest from our repo).
Since we invoke rpm with a list if one rpm in that list is wrong based
on condition I indicated.
And because it is a sub-process, debugging is tough for someone who wrote Kickstart.
Essentially it boils down. Do you want a crash installer or not? If we want to crash,
we need to dump from the sub-process stderr, read it, and serialize it to a log.
That way, using the best-effort strategy here, IMHO better. so a person will be able to install OS
, figure out that rpms additional did not succeed, resolve conflict ( ver/path, etc.)
go back and fix the kickstart file.
try: | ||
# best effort for each dir in list | ||
cmd_err = self.cmd.run(['rpm', '--root', self.photon_root, '-U', rdir + '/*.rpm']) | ||
except FileNotFoundError as _: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We wouldn't need this if we had checked for rpm files above.
additional_rpms is json list, not just a single entity. Hence it makes sense to iterate for each dir and invoke the rpm.
It also fixes the issue if the rpm_paths is a list; exists will raise an exception and crash since it can't process the list.