-
Notifications
You must be signed in to change notification settings - Fork 5
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
holo-files: Support patch files (#5) #42
base: master
Are you sure you want to change the base?
Changes from 1 commit
dc0aebd
cf1d60b
aa26621
610d23e
52364c3
d3c9cba
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 |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/******************************************************************************* | ||
* | ||
* Copyright 2017-2018 Luke Shumaker <[email protected]> | ||
* | ||
* This file is part of Holo. | ||
* | ||
* Holo is free software: you can redistribute it and/or modify it under the | ||
* terms of the GNU General Public License as published by the Free Software | ||
* Foundation, either version 3 of the License, or (at your option) any later | ||
* version. | ||
* | ||
* Holo is distributed in the hope that it will be useful, but WITHOUT ANY | ||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License along with | ||
* Holo. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*******************************************************************************/ | ||
|
||
package impl | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/holocm/holo/cmd/holo-files/internal/common" | ||
) | ||
|
||
// Patchfile is a Resource that is a `patch(1)` file that edits the | ||
// current version of the entity. | ||
type Patchfile struct{ rawResource } | ||
|
||
// ApplicationStrategy implements the Resource interface. | ||
func (resource Patchfile) ApplicationStrategy() string { return "patch" } | ||
|
||
// DiscardsPreviousBuffer implements the Resource interface. | ||
func (resource Patchfile) DiscardsPreviousBuffer() bool { return false } | ||
|
||
// ApplyTo implements the Resource interface. | ||
func (resource Patchfile) ApplyTo(entityBuffer common.FileBuffer) (common.FileBuffer, error) { | ||
// `patch` requires that the file it's operating on be a real | ||
// file (not a pipe). So, we'll write entityBuffer to a | ||
// temporary file, run `patch`, then read it back. | ||
|
||
// We really only normally need 1 temporary file, but: | ||
// 1. since common.FileBuffer.Write removes the file and then | ||
// re-creates it, that's a bit racy | ||
// 2. The only way to limit patch to operating on a single | ||
// file is to name that file on the command line, but | ||
// doing that prevents it from unlinking the file, which | ||
// prevents type changes. | ||
// | ||
// Using a temporary directory lets us easily work around both | ||
// of these issues. Unfortunately, this allows the patch to | ||
// create new files other than the one for the entity we are | ||
// applying. However, it can't escape the temporary | ||
// directory, so we'll just "allow" that, and document that we | ||
// ignore those files. | ||
targetDir, err := ioutil.TempDir(os.Getenv("HOLO_CACHE_DIR"), "patch-target.") | ||
if err != nil { | ||
return common.FileBuffer{}, err | ||
} | ||
defer os.RemoveAll(targetDir) | ||
targetPath := filepath.Join(targetDir, filepath.Base(entityBuffer.Path)) | ||
|
||
// Write entityBuffer to the temporary file | ||
err = entityBuffer.Write(targetPath) | ||
if err != nil { | ||
return common.FileBuffer{}, err | ||
} | ||
|
||
// Run `patch` on the temporary file | ||
patchfile, err := filepath.Abs(resource.Path()) | ||
if err != nil { | ||
return common.FileBuffer{}, err | ||
} | ||
cmd := exec.Command("patch", | ||
"-N", | ||
"-i", patchfile, | ||
) | ||
cmd.Dir = targetDir | ||
cmd.Stdout = os.Stderr | ||
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. We should swallow the success-case output ( Swallowing the output in the happy path would also make the tests slightly more portable since we can match and swallow both the GNU patch success message and the Busybox patch success message (and later also the BSD one). |
||
cmd.Stderr = os.Stderr | ||
err = cmd.Run() | ||
if err != nil { | ||
return common.FileBuffer{}, fmt.Errorf("execution failed: %s: %s", strings.Join(cmd.Args, " "), err.Error()) | ||
} | ||
|
||
// Read the result back | ||
// | ||
// Allow `patch` to override everything but the filepath: | ||
// - file type (changable with git-style "deleted file | ||
// mode"/"new file mode" lines, which are implemented by at | ||
// least GNU patch, if not in strict POSIX mode) | ||
// - file permissions (changable with git-style "new mode" | ||
// lines, which are implemented by at least GNU patch) | ||
// - UID/GID (I don't know of a patch syntax that does this, | ||
// but maybe it will exist in the future) | ||
// - contents (obviously) | ||
targetBuffer, err := common.NewFileBuffer(targetPath) | ||
if err != nil { | ||
return common.FileBuffer{}, err | ||
} | ||
targetBuffer.Path = entityBuffer.Path | ||
return targetBuffer, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,12 +66,25 @@ default configuration for L<pacman(8)>, but enables the "Color" and | |
passthru /usr/share/holo/files/20-enable-color/etc/pacman.conf.holoscript | ||
|
||
This allows the file contents to be modified; however, the file permissions and | ||
ownership are not changed, and are inherited from the target base. | ||
ownership are not changed, and are inherited from the target base or the | ||
previous resource application step. | ||
|
||
=item C<.patch> The resource file is understood to be a patch file that can be | ||
fed to the L<patch(1)> program. Some patch formats have the ability to change | ||
file type and file permissions; this is respected, making this is the only | ||
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. s/this is the/this the/ |
||
resource format that can change file permissions. | ||
|
||
The filename to modify is not passed to L<patch(1)>; instead, a copy of the | ||
target is available as the only file in the directory passed to the C<-d> flag. | ||
This means that the filename used within the patch file must have the same | ||
basename as the entity being operated on. Any other files that the patch file | ||
may create are ignored. | ||
|
||
=item Otherwise, the resource file is a plain file or symlink that will just | ||
overwrite the contents of the target base (and all previous resource application | ||
steps). The ownership and file permissions are not set from the resource file, | ||
and are inherited from the target base. | ||
and are inherited from the target base or the previous resource application | ||
step. | ||
|
||
=back | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
|
||
Working on file:/etc/symlink | ||
store at target/var/lib/holo/files/base/etc/symlink | ||
patch target/usr/share/holo/files/17-patches/etc/symlink.patch | ||
|
||
patching symbolic link symlink | ||
|
||
Working on file:/etc/symlink-to-plain | ||
store at target/var/lib/holo/files/base/etc/symlink-to-plain | ||
patch target/usr/share/holo/files/17-patches/etc/symlink-to-plain.patch | ||
|
||
patching symbolic link symlink-to-plain | ||
patching file symlink-to-plain | ||
|
||
Working on file:/etc/txtfile | ||
store at target/var/lib/holo/files/base/etc/txtfile | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile.patch | ||
|
||
patching file txtfile | ||
|
||
Working on file:/etc/txtfile-to-symlink | ||
store at target/var/lib/holo/files/base/etc/txtfile-to-symlink | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile-to-symlink.patch | ||
|
||
patching file txtfile-to-symlink | ||
patching symbolic link txtfile-to-symlink | ||
|
||
Working on file:/etc/txtfile-with-fuzz | ||
store at target/var/lib/holo/files/base/etc/txtfile-with-fuzz | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile-with-fuzz.patch | ||
|
||
patching file txtfile-with-fuzz | ||
Hunk #1 succeeded at 1 with fuzz 1. | ||
|
||
Working on file:/etc/txtfile-with-garbage | ||
store at target/var/lib/holo/files/base/etc/txtfile-with-garbage | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile-with-garbage.patch | ||
|
||
patching file txtfile-with-garbage | ||
patching file garbage | ||
patching file ls | ||
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. I don't know how hard it would be to implement, but to avoid confusion and/or surprises, I'd rather have the apply step fail if some parts of the patch are not applicable to the target file. |
||
|
||
exit status 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
diff --holo target/var/lib/holo/files/provisioned/etc/symlink target/etc/symlink | ||
new file mode 120000 | ||
--- /dev/null | ||
+++ target/etc/symlink | ||
@@ -0,0 +1 @@ | ||
+txtfile | ||
\ No newline at end of file | ||
diff --holo target/var/lib/holo/files/provisioned/etc/symlink-to-plain target/etc/symlink-to-plain | ||
new file mode 120000 | ||
--- /dev/null | ||
+++ target/etc/symlink-to-plain | ||
@@ -0,0 +1 @@ | ||
+txtfile | ||
\ No newline at end of file | ||
diff --holo target/var/lib/holo/files/provisioned/etc/txtfile target/etc/txtfile | ||
new file mode 100644 | ||
--- /dev/null | ||
+++ target/etc/txtfile | ||
@@ -0,0 +1,6 @@ | ||
+foo | ||
+foo | ||
+foo | ||
+baz | ||
+bar | ||
+bar | ||
diff --holo target/var/lib/holo/files/provisioned/etc/txtfile-to-symlink target/etc/txtfile-to-symlink | ||
new file mode 100644 | ||
--- /dev/null | ||
+++ target/etc/txtfile-to-symlink | ||
@@ -0,0 +1,6 @@ | ||
+foo | ||
+foo | ||
+foo | ||
+baz | ||
+bar | ||
+bar | ||
diff --holo target/var/lib/holo/files/provisioned/etc/txtfile-with-fuzz target/etc/txtfile-with-fuzz | ||
new file mode 100644 | ||
--- /dev/null | ||
+++ target/etc/txtfile-with-fuzz | ||
@@ -0,0 +1,6 @@ | ||
+foo | ||
+foo | ||
+foo | ||
+baz | ||
+bar | ||
+bar | ||
diff --holo target/var/lib/holo/files/provisioned/etc/txtfile-with-garbage target/etc/txtfile-with-garbage | ||
new file mode 100644 | ||
--- /dev/null | ||
+++ target/etc/txtfile-with-garbage | ||
@@ -0,0 +1,6 @@ | ||
+foo | ||
+foo | ||
+foo | ||
+baz | ||
+bar | ||
+bar | ||
exit status 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
|
||
file:/etc/symlink | ||
store at target/var/lib/holo/files/base/etc/symlink | ||
patch target/usr/share/holo/files/17-patches/etc/symlink.patch | ||
|
||
file:/etc/symlink-to-plain | ||
store at target/var/lib/holo/files/base/etc/symlink-to-plain | ||
patch target/usr/share/holo/files/17-patches/etc/symlink-to-plain.patch | ||
|
||
file:/etc/txtfile | ||
store at target/var/lib/holo/files/base/etc/txtfile | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile.patch | ||
|
||
file:/etc/txtfile-to-symlink | ||
store at target/var/lib/holo/files/base/etc/txtfile-to-symlink | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile-to-symlink.patch | ||
|
||
file:/etc/txtfile-with-fuzz | ||
store at target/var/lib/holo/files/base/etc/txtfile-with-fuzz | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile-with-fuzz.patch | ||
|
||
file:/etc/txtfile-with-garbage | ||
store at target/var/lib/holo/files/base/etc/txtfile-with-garbage | ||
patch target/usr/share/holo/files/17-patches/etc/txtfile-with-garbage.patch | ||
|
||
exit status 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.
targetPath
is a bit confusing since we already have a "target path" in our jargon at some other point. MaybeworkDir
andworkPath
, ortempDir
andtempPath
?