Add effective diff consumer examples
This commit is contained in:
136
examples/consumers/diff.go
Normal file
136
examples/consumers/diff.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package consumers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
)
|
||||
|
||||
// FileComparisonConfig is a small application-owned shape for comparing
|
||||
// caller-selected configuration files.
|
||||
type FileComparisonConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Entries map[string]any `yaml:"entries"`
|
||||
}
|
||||
|
||||
// CompareFiles loads two caller-selected files with the same application
|
||||
// definition, then formats only the safe records returned by configkit.Diff.
|
||||
func CompareFiles(leftPath, rightPath string) (string, error) {
|
||||
options := configkit.Options[FileComparisonConfig]{
|
||||
Definition: configkit.Definition[FileComparisonConfig]{
|
||||
Defaults: func() FileComparisonConfig {
|
||||
return FileComparisonConfig{Entries: map[string]any{}}
|
||||
},
|
||||
},
|
||||
}
|
||||
left, err := configkit.Load(configkit.File(leftPath), options)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load left configuration: %w", err)
|
||||
}
|
||||
right, err := configkit.Load(configkit.File(rightPath), options)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load right configuration: %w", err)
|
||||
}
|
||||
differences, err := configkit.Diff(left, right)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return FormatDifferences(differences), nil
|
||||
}
|
||||
|
||||
// RolloutConfig models a service endpoint whose URL is derived by the
|
||||
// application resolver and whose credential reference is classified.
|
||||
type RolloutConfig struct {
|
||||
Service RolloutService `yaml:"service"`
|
||||
}
|
||||
|
||||
// RolloutService contains endpoint settings and a classified credential.
|
||||
type RolloutService struct {
|
||||
Endpoint RolloutEndpoint `yaml:"endpoint"`
|
||||
Credential RolloutCredential `yaml:"credential"`
|
||||
}
|
||||
|
||||
// RolloutEndpoint is the effective destination used by the application.
|
||||
type RolloutEndpoint struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
URL string `yaml:"url"`
|
||||
}
|
||||
|
||||
// RolloutCredential holds only a typed secret reference in configuration.
|
||||
type RolloutCredential struct {
|
||||
Token configkit.Secret `yaml:"token" configkit:"secret"`
|
||||
}
|
||||
|
||||
// LoadRollout loads a base file, optionally with one selected configuration
|
||||
// overlay. Resolution derives the effective URL after the overlay is applied.
|
||||
func LoadRollout(basePath, overlayName string) (*configkit.Result[RolloutConfig], error) {
|
||||
return configkit.Load(configkit.File(basePath), configkit.Options[RolloutConfig]{
|
||||
Overlay: overlayName,
|
||||
Definition: configkit.Definition[RolloutConfig]{
|
||||
Resolve: resolveRollout,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CompareRolloutOverlay compares the effective base configuration with that
|
||||
// same file plus one selected overlay. It presents only safe diff records.
|
||||
func CompareRolloutOverlay(basePath, overlayName string) (string, error) {
|
||||
base, err := LoadRollout(basePath, "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load base configuration: %w", err)
|
||||
}
|
||||
withOverlay, err := LoadRollout(basePath, overlayName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load overlay configuration: %w", err)
|
||||
}
|
||||
differences, err := configkit.Diff(base, withOverlay)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return FormatDifferences(differences), nil
|
||||
}
|
||||
|
||||
func resolveRollout(configuration *RolloutConfig, resolution *configkit.Resolution) error {
|
||||
endpoint := &configuration.Service.Endpoint
|
||||
if endpoint.Host == "" || endpoint.Port <= 0 {
|
||||
return nil
|
||||
}
|
||||
url := fmt.Sprintf("https://%s:%d", endpoint.Host, endpoint.Port)
|
||||
if endpoint.URL != url {
|
||||
endpoint.URL = url
|
||||
resolution.Record(configkit.Keys("service", "endpoint", "url"),
|
||||
configkit.ChangeNormalization, "derived endpoint URL",
|
||||
configkit.Keys("service", "endpoint", "host"),
|
||||
configkit.Keys("service", "endpoint", "port"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatDifferences is consumer-owned presentation. It distinguishes absence
|
||||
// through Present and reports withheld changes through HiddenChanged without
|
||||
// interpreting redaction marker text.
|
||||
func FormatDifferences(differences []configkit.Difference) string {
|
||||
if len(differences) == 0 {
|
||||
return "No effective changes.\n"
|
||||
}
|
||||
var output strings.Builder
|
||||
for _, difference := range differences {
|
||||
fmt.Fprintf(&output, "%s: left=%s right=%s", difference.Path.String(),
|
||||
formatSnapshot(difference.Left), formatSnapshot(difference.Right))
|
||||
if difference.HiddenChanged {
|
||||
output.WriteString(" hidden-change")
|
||||
}
|
||||
output.WriteByte('\n')
|
||||
}
|
||||
return output.String()
|
||||
}
|
||||
|
||||
func formatSnapshot(snapshot configkit.ValueSnapshot) string {
|
||||
if !snapshot.Present {
|
||||
return "<absent>"
|
||||
}
|
||||
return strconv.Quote(snapshot.YAML)
|
||||
}
|
||||
Reference in New Issue
Block a user