-
Notifications
You must be signed in to change notification settings - Fork 45
Add a feature to pass user provided files during qcow2ova image conversion #851
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ package prep | |
|
|
||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
@@ -34,7 +35,7 @@ var ( | |
| // - Install and configure multipath for rootfs | ||
| // - Install all the required modules for PowerVM | ||
| // - Sets the root password | ||
| func prepare(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd string) error { | ||
| func prepare(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd, writeToDirPath string, writeFilesList []string) error { | ||
| lo, err := setupLoop(volume) | ||
| if err != nil { | ||
| return err | ||
|
|
@@ -144,6 +145,19 @@ func prepare(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd string) error { | |
| return err | ||
| } | ||
|
|
||
| // Write user provided files to given path in the image | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Security] No path traversal guard on
Add a check after constructing if !strings.HasPrefix(filepath.Clean(imageWritePath), filepath.Clean(mnt)+"/") {
return fmt.Errorf("write-to-dir-path %q escapes the image mount root", writeToDirPath)
} |
||
| imageWritePath := filepath.Join(mnt, writeToDirPath) | ||
| if err := os.MkdirAll(imageWritePath, 0755); err != nil { | ||
| return fmt.Errorf("failed to create directory %s: %w", imageWritePath, err) | ||
| } | ||
|
|
||
| for _, filePath := range writeFilesList { | ||
| destinationPath := filepath.Join(imageWritePath, filepath.Base(filePath)) | ||
| if err := copyFiles(filePath, destinationPath); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| err = Chroot(mnt) | ||
| if err != nil { | ||
| return err | ||
|
|
@@ -169,19 +183,90 @@ func UmountHostPartitions(mnt string) { | |
| } | ||
| } | ||
|
|
||
| func Prepare4capture(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd string) error { | ||
| func Prepare4capture(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd, writeToDirPath string, writeFilesList []string) error { | ||
| //cwd, err := os.Getwd() | ||
| //if err != nil { | ||
| // return err | ||
| //} | ||
| //defer os.Chdir(cwd) | ||
| switch dist := strings.ToLower(dist); dist { | ||
| case "rhel", "centos": | ||
| return prepare(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd) | ||
| return prepare(mnt, volume, dist, rhnuser, rhnpasswd, rootpasswd, writeToDirPath, writeFilesList) | ||
| case "coreos": | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Correctness] The file-copy block only runs inside Either propagate the copy step for case "coreos":
if len(writeFilesList) > 0 {
return fmt.Errorf("--write-files-list is not supported for coreos distro")
}
klog.Info("No image preparation required for the coreos.")
return nil |
||
| klog.Info("No image preparation required for the coreos.") | ||
| return nil | ||
| default: | ||
| return fmt.Errorf("not a supported distro: %s", dist) | ||
| } | ||
| } | ||
|
|
||
| func copyFiles(src, dest string) error { | ||
| fileInfo, err := os.Stat(src) | ||
| if err != nil { | ||
| return err | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Security]
|
||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Readability] Trailing blank line before closing brace return copyFile(src, dest)
// ← remove this blank line
}
|
||
| if fileInfo.IsDir() { | ||
| return copyDir(src, dest) | ||
| } | ||
|
|
||
| return copyFile(src, dest) | ||
|
|
||
| } | ||
|
|
||
| func copyFile(src, dest string) error { | ||
| in, err := os.Open(src) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Performance] Double
func copyFile(src, dest string, srcInfo os.FileInfo) error {
in, err := os.Open(src)
...
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, srcInfo.Mode())
...
} |
||
| defer in.Close() | ||
|
|
||
| srcInfo, err := in.Stat() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, srcInfo.Mode()) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if _, err := io.Copy(out, in); err != nil { | ||
| out.Close() | ||
| return err | ||
| } | ||
|
|
||
| return out.Close() | ||
| } | ||
|
|
||
| func copyDir(src, dest string) error { | ||
| srcInfo, err := os.Stat(src) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if err := os.MkdirAll(dest, srcInfo.Mode()); err != nil { | ||
| return fmt.Errorf("failed to create directory %s: %w", dest, err) | ||
| } | ||
|
|
||
| entries, err := os.ReadDir(src) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for _, entry := range entries { | ||
| srcPath := filepath.Join(src, entry.Name()) | ||
| destPath := filepath.Join(dest, entry.Name()) | ||
|
|
||
| if entry.IsDir() { | ||
| if err := copyDir(srcPath, destPath); err != nil { | ||
| return err | ||
| } | ||
| } else { | ||
| if err := copyFile(srcPath, destPath); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,6 +60,9 @@ Examples: | |
| # Step 2 - Make the necessary changes to the above generated template file(bash shell script) - image-prep.template | ||
| # Step 3 - Run the qcow2ova with the modified image preparation template | ||
| pvsadm image qcow2ova --image-name centos-82 --image-dist centos --image-url /root/CentOS-8-GenericCloud-8.2.2004-20200611.2.ppc64le.qcow2 --prep-template image-prep.template | ||
|
|
||
| # For adding custom files to the image, run the qcow2ova with flags --write-files-list and --write-to-dir-path | ||
| pvsadm image qcow2ova --image-name --image-dist centos --image-url /root/CentOS-8-GenericCloud-8.2.2004-20200611.2.ppc64le.qcow2 --prep-template image-prep.template --write-files-list a.txt,b.log --write-to-dir-path /home/user | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Correctness — High] Missing flag co-validation: When Add a validation check in if len(opt.WriteFilesList) > 0 && opt.WriteToDirPath == "" {
return fmt.Errorf("--write-to-dir-path is required when --write-files-list is specified")
} |
||
| # Customize the cloud config and Convert image with user defined cloud config template. | ||
| # Step 1 - Dump the default cloud config template | ||
|
|
@@ -263,7 +266,7 @@ Qcow2 images location: | |
| klog.Info("Resize completed") | ||
|
|
||
| klog.Info("Preparing the image") | ||
| err = prep.Prepare4capture(mnt, rawImg, opt.ImageDist, opt.RHNUser, opt.RHNPassword, opt.OSPassword) | ||
| err = prep.Prepare4capture(mnt, rawImg, opt.ImageDist, opt.RHNUser, opt.RHNPassword, opt.OSPassword, opt.WriteToDirPath, opt.WriteFilesList) | ||
| if err != nil { | ||
| return fmt.Errorf("failed while preparing the image for %s distro, err: %v", opt.ImageDist, err) | ||
| } | ||
|
|
@@ -300,6 +303,8 @@ func init() { | |
| Cmd.Flags().StringVar(&pkg.ImageCMDOptions.OSPassword, "os-password", "", "Root user password, will auto-generate the 12 bits password(applicable only for redhat and cento distro)") | ||
| Cmd.Flags().StringVarP(&pkg.ImageCMDOptions.TempDir, "temp-dir", "t", os.TempDir(), "Scratch space to use for OVA generation") | ||
| Cmd.Flags().StringVar(&pkg.ImageCMDOptions.PrepTemplate, "prep-template", "", "Image preparation script template, use --prep-template-default to print the default template(supported distros: rhel and centos)") | ||
| Cmd.Flags().StringSliceVar(&pkg.ImageCMDOptions.WriteFilesList, "write-files-list", []string{}, "List of files to be copied to the image") | ||
| Cmd.Flags().StringVar(&pkg.ImageCMDOptions.WriteToDirPath, "write-to-dir-path", "", "User provided directory path where the provided files will be copied to") | ||
| Cmd.Flags().BoolVar(&pkg.ImageCMDOptions.PrepTemplateDefault, "prep-template-default", false, "Prints the default image preparation script template, use --prep-template to set the custom template script(supported distros: rhel and centos)") | ||
| Cmd.Flags().StringSliceVar(&pkg.ImageCMDOptions.PreflightSkip, "skip-preflight-checks", []string{}, "Skip the preflight checks(e.g: diskspace, platform, tools) - dev-only option") | ||
| Cmd.Flags().BoolVar(&pkg.ImageCMDOptions.OSPasswordSkip, "skip-os-password", false, "Skip the root user password") | ||
|
|
||
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.
[Correctness] Unconditional
MkdirAllwhen no files are specifiedWhen neither flag is provided,
writeToDirPathis""andwriteFilesListis empty.filepath.Join(mnt, "")resolves tomnt(the image root), soMkdirAllruns on the root — harmless but unnecessary. More importantly, if a user passes--write-to-dir-path /some/pathwithout--write-files-list, a spurious empty directory is silently created inside the image.Wrap the entire block in a guard: