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:
parent
33f18b404e
commit
fe63071416
3 changed files with 82 additions and 25 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ]")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue