mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
add missing config keys on provision
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
//go:embed systemd/appview.service.tmpl
|
||||
@@ -189,3 +191,106 @@ func generateCloudInit(p cloudInitParams) (string, error) {
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// syncConfigKeys fetches the existing config from a server and merges in any
|
||||
// missing keys from the rendered template. Existing values are never overwritten.
|
||||
func syncConfigKeys(name, ip, configPath, templateYAML string) error {
|
||||
remote, err := runSSH(ip, fmt.Sprintf("cat %s 2>/dev/null || echo '__MISSING__'", configPath), false)
|
||||
if err != nil {
|
||||
fmt.Printf(" config sync: could not reach %s (%v)\n", name, err)
|
||||
return nil
|
||||
}
|
||||
remote = strings.TrimSpace(remote)
|
||||
|
||||
if remote == "__MISSING__" {
|
||||
fmt.Printf(" config sync: %s not yet created (cloud-init will handle it)\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse both into yaml.Node trees
|
||||
var templateDoc yaml.Node
|
||||
if err := yaml.Unmarshal([]byte(templateYAML), &templateDoc); err != nil {
|
||||
return fmt.Errorf("parse template yaml: %w", err)
|
||||
}
|
||||
var existingDoc yaml.Node
|
||||
if err := yaml.Unmarshal([]byte(remote), &existingDoc); err != nil {
|
||||
return fmt.Errorf("parse remote yaml: %w", err)
|
||||
}
|
||||
|
||||
// Unwrap document nodes to get the root mapping
|
||||
templateRoot := unwrapDocNode(&templateDoc)
|
||||
existingRoot := unwrapDocNode(&existingDoc)
|
||||
if templateRoot == nil || existingRoot == nil {
|
||||
fmt.Printf(" config sync: %s skipped (unexpected YAML structure)\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
added := mergeYAMLNodes(templateRoot, existingRoot)
|
||||
if !added {
|
||||
fmt.Printf(" config sync: %s up to date\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal the modified tree back
|
||||
merged, err := yaml.Marshal(&existingDoc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal merged yaml: %w", err)
|
||||
}
|
||||
|
||||
// Write back to server
|
||||
script := fmt.Sprintf("cat > %s << 'CFGEOF'\n%sCFGEOF", configPath, string(merged))
|
||||
if _, err := runSSH(ip, script, false); err != nil {
|
||||
return fmt.Errorf("write merged config: %w", err)
|
||||
}
|
||||
fmt.Printf(" config sync: %s updated with new keys\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// unwrapDocNode returns the root mapping node, unwrapping a DocumentNode wrapper if present.
|
||||
func unwrapDocNode(n *yaml.Node) *yaml.Node {
|
||||
if n.Kind == yaml.DocumentNode && len(n.Content) > 0 {
|
||||
return n.Content[0]
|
||||
}
|
||||
if n.Kind == yaml.MappingNode {
|
||||
return n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeYAMLNodes recursively adds keys from base into existing that are not
|
||||
// already present. Existing values are never overwritten. Returns true if any
|
||||
// new keys were added.
|
||||
func mergeYAMLNodes(base, existing *yaml.Node) bool {
|
||||
if base.Kind != yaml.MappingNode || existing.Kind != yaml.MappingNode {
|
||||
return false
|
||||
}
|
||||
|
||||
added := false
|
||||
for i := 0; i+1 < len(base.Content); i += 2 {
|
||||
baseKey := base.Content[i]
|
||||
baseVal := base.Content[i+1]
|
||||
|
||||
// Look for this key in existing
|
||||
found := false
|
||||
for j := 0; j+1 < len(existing.Content); j += 2 {
|
||||
if existing.Content[j].Value == baseKey.Value {
|
||||
found = true
|
||||
// If both are mappings, recurse to merge sub-keys
|
||||
if baseVal.Kind == yaml.MappingNode && existing.Content[j+1].Kind == yaml.MappingNode {
|
||||
if mergeYAMLNodes(baseVal, existing.Content[j+1]) {
|
||||
added = true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// Append the missing key+value pair
|
||||
existing.Content = append(existing.Content, baseKey, baseVal)
|
||||
added = true
|
||||
}
|
||||
}
|
||||
|
||||
return added
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ cat > {{.ConfigPath}} << 'CFGEOF'
|
||||
{{.ConfigYAML}}
|
||||
CFGEOF
|
||||
else
|
||||
echo "Config {{.ConfigPath}} already exists, skipping"
|
||||
echo "Config {{.ConfigPath}} already exists, skipping overwrite (missing keys merged separately)"
|
||||
fi
|
||||
|
||||
# Systemd service
|
||||
|
||||
@@ -189,6 +189,13 @@ func cmdProvision(token, zone, plan, sshKeyPath, s3Secret string) error {
|
||||
if err := syncCloudInit("appview", state.Appview.PublicIP, appviewScript); err != nil {
|
||||
return err
|
||||
}
|
||||
appviewConfigYAML, err := renderConfig(appviewConfigTmpl, vals)
|
||||
if err != nil {
|
||||
return fmt.Errorf("render appview config: %w", err)
|
||||
}
|
||||
if err := syncConfigKeys("appview", state.Appview.PublicIP, naming.AppviewConfigPath(), appviewConfigYAML); err != nil {
|
||||
return fmt.Errorf("appview config sync: %w", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Creating appview server...")
|
||||
appviewUserData, err := generateAppviewCloudInit(cfg, vals, goVersion)
|
||||
@@ -214,6 +221,13 @@ func cmdProvision(token, zone, plan, sshKeyPath, s3Secret string) error {
|
||||
if err := syncCloudInit("hold", state.Hold.PublicIP, holdScript); err != nil {
|
||||
return err
|
||||
}
|
||||
holdConfigYAML, err := renderConfig(holdConfigTmpl, vals)
|
||||
if err != nil {
|
||||
return fmt.Errorf("render hold config: %w", err)
|
||||
}
|
||||
if err := syncConfigKeys("hold", state.Hold.PublicIP, naming.HoldConfigPath(), holdConfigYAML); err != nil {
|
||||
return fmt.Errorf("hold config sync: %w", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Creating hold server...")
|
||||
holdUserData, err := generateHoldCloudInit(cfg, vals, goVersion)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b h1:5/++qT1/z812ZqBvqQt6ToRswSuPZ/B33m6xVHRzADU=
|
||||
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b/go.mod h1:4+EPqMRApwwE/6yo6CxiHoSnBzjRr3jsqer7frxP8y4=
|
||||
@@ -78,6 +79,7 @@ github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TI
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
@@ -95,6 +97,7 @@ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHz
|
||||
github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
|
||||
github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU=
|
||||
@@ -161,6 +164,7 @@ github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkM
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
@@ -291,6 +295,7 @@ github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR9
|
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM=
|
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
@@ -345,6 +350,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0=
|
||||
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
|
||||
Reference in New Issue
Block a user