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 { path = mkOption {
description = "Path where the rendered file will be placed"; description = "Path where the rendered file will be placed";
type = types.singleLineStr; type = types.singleLineStr;
# Keep this in sync with `RenderedSubdir` in `pkgs/sops-install-secrets/main.go`
default = "/run/secrets/rendered/${config.name}"; default = "/run/secrets/rendered/${config.name}";
}; };
content = mkOption { content = mkOption {

View file

@ -155,6 +155,9 @@ type appContext struct {
ignorePasswd bool ignorePasswd bool
} }
// Keep this in sync with `modules/sops/templates/default.nix`
const RenderedSubdir string = "rendered"
func readManifest(path string) (*manifest, error) { func readManifest(path string) (*manifest, error) {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
@ -873,7 +876,7 @@ func symlinkWalk(filename string, linkDirname string, walkFn filepath.WalkFunc)
return filepath.Walk(filename, symWalkFunc) 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 restart []string
var reload []string var reload []string
@ -881,6 +884,10 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
modifiedSecrets := make(map[string]bool) modifiedSecrets := make(map[string]bool)
removedSecrets := 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 // 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 // where switch-to-configuration is not run so the services would only be restarted
// the next time switch-to-configuration is run. // 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 { writeLines := func(list []string, file string) error {
if len(list) != 0 { if len(list) != 0 {
f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) 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 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 { err := symlinkWalk(symlinkPath, symlinkPath, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
@ -960,30 +995,49 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
if info.IsDir() { if info.IsDir() {
return nil return nil
} }
path = strings.TrimPrefix(path, symlinkPath+string(os.PathSeparator))
for _, secret := range secrets { // If the path we're looking at isn't in `symlinkRenderedPath`, then
if secret.Name == path { // it's a secret.
return nil 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 return nil
}) })
if err != nil { if err != nil {
return err return err
} }
// Output new/modified/removed secrets // Output new/modified/removed secrets/templates
outputChanged := func(changed map[string]bool, regularPrefix, dryPrefix string) { outputChanged := func(noun string, changed map[string]bool, regularPrefix, dryPrefix string) {
if len(changed) > 0 { if len(changed) > 0 {
s := "" s := ""
if len(changed) != 1 { if len(changed) != 1 {
s = "s" s = "s"
} }
if isDry { if isDry {
fmt.Printf("%s secret%s: ", dryPrefix, s) fmt.Printf("%s %s%s: ", dryPrefix, noun, s)
} else { } else {
fmt.Printf("%s secret%s: ", regularPrefix, s) fmt.Printf("%s %s%s: ", regularPrefix, noun, s)
} }
// Sort the output for deterministic behavior. // Sort the output for deterministic behavior.
@ -996,9 +1050,12 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s
fmt.Println(strings.Join(keys, ", ")) fmt.Println(strings.Join(keys, ", "))
} }
} }
outputChanged(newSecrets, "adding", "would add") outputChanged("secret", newSecrets, "adding", "would add")
outputChanged(modifiedSecrets, "modifying", "would modify") outputChanged("secret", modifiedSecrets, "modifying", "would modify")
outputChanged(removedSecrets, "removing", "would remove") 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 return nil
} }
@ -1210,12 +1267,12 @@ func installSecrets(args []string) error {
return fmt.Errorf("cannot write secrets: %w", err) 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) return fmt.Errorf("cannot render templates: %w", err)
} }
if !manifest.UserMode { 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) return fmt.Errorf("cannot request units to restart: %w", err)
} }
} }

View file

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