1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2024-12-14 11:57:52 +00:00

Improve activation messages about rendered templates

This fixes https://github.com/Mic92/sops-nix/issues/652
This commit is contained in:
Jeremy Fleischman 2024-11-07 13:39:26 -06:00 committed by mergify[bot]
parent 33f18b404e
commit fe63071416
3 changed files with 82 additions and 25 deletions

View file

@ -24,6 +24,7 @@ in {
path = mkOption {
description = "Path where the rendered file will be placed";
type = types.singleLineStr;
# Keep this in sync with `RenderedSubdir` in `pkgs/sops-install-secrets/main.go`
default = "/run/secrets/rendered/${config.name}";
};
content = mkOption {

View file

@ -155,6 +155,9 @@ type appContext struct {
ignorePasswd bool
}
// Keep this in sync with `modules/sops/templates/default.nix`
const RenderedSubdir string = "rendered"
func readManifest(path string) (*manifest, error) {
file, err := os.Open(path)
if err != nil {
@ -873,7 +876,7 @@ func symlinkWalk(filename string, linkDirname string, walkFn filepath.WalkFunc)
return filepath.Walk(filename, symWalkFunc)
}
func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, secretDir string, secrets []secret) error {
func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, secretDir string, secrets []secret, templates map[string]*template) error {
var restart []string
var reload []string
@ -881,6 +884,10 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
modifiedSecrets := make(map[string]bool)
removedSecrets := make(map[string]bool)
newTemplates := make(map[string]bool)
modifiedTemplates := make(map[string]bool)
removedTemplates := make(map[string]bool)
// When the symlink path does not exist yet, we are being run in stage-2-init.sh
// where switch-to-configuration is not run so the services would only be restarted
// the next time switch-to-configuration is run.
@ -919,6 +926,33 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
}
}
// Find modified/new templates
for _, template := range templates {
oldPath := filepath.Join(symlinkPath, RenderedSubdir, template.Name)
newPath := filepath.Join(secretDir, RenderedSubdir, template.Name)
// Read the old file
oldData, err := os.ReadFile(oldPath)
if err != nil {
if os.IsNotExist(err) {
// File did not exist before
newTemplates[template.Name] = true
continue
}
return err
}
// Read the new file
newData, err := os.ReadFile(newPath)
if err != nil {
return err
}
if !bytes.Equal(oldData, newData) {
modifiedTemplates[template.Name] = true
}
}
writeLines := func(list []string, file string) error {
if len(list) != 0 {
f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
@ -952,7 +986,8 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
return nil
}
// Find removed secrets
// Find removed secrets/templates.
symlinkRenderedPath := filepath.Join(symlinkPath, RenderedSubdir)
err := symlinkWalk(symlinkPath, symlinkPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
@ -960,30 +995,49 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
if info.IsDir() {
return nil
}
path = strings.TrimPrefix(path, symlinkPath+string(os.PathSeparator))
for _, secret := range secrets {
if secret.Name == path {
return nil
}
// If the path we're looking at isn't in `symlinkRenderedPath`, then
// it's a secret.
rel, err := filepath.Rel(symlinkRenderedPath, path)
if err != nil {
return err
}
isSecret := strings.HasPrefix(rel, "..")
if isSecret {
path = strings.TrimPrefix(path, symlinkPath+string(os.PathSeparator))
for _, secret := range secrets {
if secret.Name == path {
return nil
}
}
removedSecrets[path] = true
} else {
path = strings.TrimPrefix(path, symlinkRenderedPath+string(os.PathSeparator))
for _, template := range templates {
if template.Name == path {
return nil
}
}
removedTemplates[path] = true
}
removedSecrets[path] = true
return nil
})
if err != nil {
return err
}
// Output new/modified/removed secrets
outputChanged := func(changed map[string]bool, regularPrefix, dryPrefix string) {
// Output new/modified/removed secrets/templates
outputChanged := func(noun string, changed map[string]bool, regularPrefix, dryPrefix string) {
if len(changed) > 0 {
s := ""
if len(changed) != 1 {
s = "s"
}
if isDry {
fmt.Printf("%s secret%s: ", dryPrefix, s)
fmt.Printf("%s %s%s: ", dryPrefix, noun, s)
} else {
fmt.Printf("%s secret%s: ", regularPrefix, s)
fmt.Printf("%s %s%s: ", regularPrefix, noun, s)
}
// Sort the output for deterministic behavior.
@ -996,9 +1050,12 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
fmt.Println(strings.Join(keys, ", "))
}
}
outputChanged(newSecrets, "adding", "would add")
outputChanged(modifiedSecrets, "modifying", "would modify")
outputChanged(removedSecrets, "removing", "would remove")
outputChanged("secret", newSecrets, "adding", "would add")
outputChanged("secret", modifiedSecrets, "modifying", "would modify")
outputChanged("secret", removedSecrets, "removing", "would remove")
outputChanged("rendered secret", newTemplates, "adding", "would add")
outputChanged("rendered secret", modifiedTemplates, "modifying", "would modify")
outputChanged("rendered secret", removedTemplates, "removing", "would remove")
return nil
}
@ -1210,12 +1267,12 @@ func installSecrets(args []string) error {
return fmt.Errorf("cannot write secrets: %w", err)
}
if err := writeTemplates(path.Join(*secretDir, "rendered"), manifest.Templates, keysGID, manifest.UserMode); err != nil {
if err := writeTemplates(path.Join(*secretDir, RenderedSubdir), manifest.Templates, keysGID, manifest.UserMode); err != nil {
return fmt.Errorf("cannot render templates: %w", err)
}
if !manifest.UserMode {
if err := handleModifications(isDry, manifest.Logging, manifest.SymlinkPath, *secretDir, manifest.Secrets); err != nil {
if err := handleModifications(isDry, manifest.Logging, manifest.SymlinkPath, *secretDir, manifest.Secrets, manifest.Templates); err != nil {
return fmt.Errorf("cannot request units to restart: %w", err)
}
}

View file

@ -421,6 +421,7 @@ in {
assertOutput(
out,
"adding secret: test_key",
"adding rendered secret: test_template",
)
machine.succeed(": > /run/secrets/test_key")
@ -429,8 +430,7 @@ in {
assertOutput(
out,
"modifying secret: test_key",
# This is wrong. TODO: fix https://github.com/Mic92/sops-nix/issues/652
"removing secret: rendered/test_template",
"modifying rendered secret: test_template",
)
machine.succeed(": > /run/secrets/another_key")
@ -438,8 +438,8 @@ in {
out = machine.succeed("/run/current-system/bin/switch-to-configuration test")
assertOutput(
out,
# This is wrong. TODO: fix https://github.com/Mic92/sops-nix/issues/652
"removing secrets: another_key, rendered/another_template, rendered/test_template",
"removing secret: another_key",
"removing rendered secret: another_template",
)
with subtest("dry activation"):
@ -451,8 +451,9 @@ in {
assertOutput(
out,
"would add secret: test_key",
# This is wrong. TODO: fix https://github.com/Mic92/sops-nix/issues/652
"would remove secrets: another_key, rendered/another_template",
"would remove secret: another_key",
"would add rendered secret: test_template",
"would remove rendered secret: another_template",
)
# Verify that we did not actually activate the new configuration.
@ -477,8 +478,6 @@ in {
assertOutput(
out,
"would modify secret: test_key",
# This is wrong. TODO: fix https://github.com/Mic92/sops-nix/issues/652
"would remove secret: rendered/test_template",
)
machine.succeed("[ $(cat /run/secrets/test_key | wc -c) = 0 ]")