I have a short GO app that I built to modify config files and had expected them to overwrite, but instead they are appending and I'm not sure why. I've tried os.Truncate()
prior to my file.Sync()
but end up with a malformed file. I've also attempted to write to a tmpfile, but end up with index errors and feel this is not worth the attempt. I expect the file to open, read in, modify and overwrite the existing content. It also looks as though the file is not completing to write? Maybe the buffer? What am I doing wrong?
App
package main
import (
"strings"
"bufio"
"fmt"
"regexp"
"flag"
"os"
"io"
"bytes"
)
var (
filename string
)
func isCommentOrBlank(line string) bool {
return strings.HasPrefix(line, "#") || "" == strings.TrimSpace(line)
}
func fileExists(filename string) bool {
if _, err := os.Stat(filename); err == nil {
return true
}
return false
}
func limitLength(s string, length int) string {
if len(s) < length {
return s
}
return s[:length]
}
func padWithSpace(str, pad string, length int) string {
for {
str += pad
if len(str) > length {
return str[0:length]
}
}
}
func ingest(filename string) (err error) {
file, err := os.OpenFile(filename, os.O_RDWR, 0644)
defer file.Close()
if err != nil {
return err
}
reader := bufio.NewReader(file) // Start reading from the file with a reader.
for {
var buffer bytes.Buffer
var l []byte
var isPrefix bool
for {
l, isPrefix, err = reader.ReadLine()
buffer.Write(l)
if !isPrefix {
break
}
}
if err == io.EOF || err != nil {
break
}
line := buffer.String()
pattern := regexp.MustCompile(`^#([^=]+)=(.+)$`)
matches := pattern.FindAllStringSubmatch(line, -1) // matches is [][]string
for _, string := range matches {
n := strings.Replace(strings.TrimSpace(string[1]), "#", "", -1)
v := strings.TrimSpace(string[2])
e := strings.ToLower(strings.Replace(n, ".", "/", -1))
// newline creation for if block
nl := "{{if exists \"/" + e + "\" -}}
"
nl += padWithSpace(n, " ", 80) + "= {{getv \"/" + e + "\" \"" + v + "\"}}
"
nl += "{{end -}}
"
file.WriteString(nl)
}
// dont proccess comments
if (isCommentOrBlank(line)) {
if (!strings.Contains(line, "=")) {
file.WriteString(line + "
")
}
} else {
a := strings.Split(line, "=")
n := strings.TrimSpace(a[0])
v := strings.TrimSpace(a[1])
e := strings.ToLower(strings.Replace(n, ".", "/", -1))
file.WriteString(padWithSpace(n, " ", 80) + "= {{getv \"/" + e + "\" \"" + v + "\"}}
")
}
}
file.Sync()
if err != io.EOF {
fmt.Printf(" > Failed!: %v
", err)
}
return
}
func main() {
flag.StringVar(&filename, "f", "filename", "The file location to be parsed")
flag.Parse()
if !fileExists(filename) {
fmt.Println(" > File does not exist
")
}
ingest(filename)
}
Config to rewrite
# The file contains the properties for Chaos Monkey.
# see documentation at:
# https://github.com/Netflix/SimianArmy/wiki/Configuration
# let chaos run
simianarmy.chaos.enabled = true
# don't allow chaos to kill (ie dryrun mode)
simianarmy.chaos.leashed = true
# set to "false" for Opt-In behavior, "true" for Opt-Out behavior
simianarmy.chaos.ASG.enabled = false
# uncomment this line to use tunable aggression
#simianarmy.client.chaos.class = com.netflix.simianarmy.tunable.TunablyAggressiveChaosMonkey
# default probability for all ASGs
simianarmy.chaos.ASG.probability = 1.0
# increase or decrease the termination limit
simianarmy.chaos.ASG.maxTerminationsPerDay = 1.0
# Strategies
simianarmy.chaos.shutdowninstance.enabled = true
simianarmy.chaos.blockallnetworktraffic.enabled = false
simianarmy.chaos.burncpu.enabled = false
simianarmy.chaos.killprocesses.enabled = false
simianarmy.chaos.nullroute.enabled = false
simianarmy.chaos.failapi.enabled = false
simianarmy.chaos.faildns.enabled = false
simianarmy.chaos.faildynamodb.enabled = false
simianarmy.chaos.fails3.enabled = false
simianarmy.chaos.networkcorruption.enabled = false
simianarmy.chaos.networklatency.enabled = false
simianarmy.chaos.networkloss.enabled = false
# Force-detaching EBS volumes may cause data loss
simianarmy.chaos.detachvolumes.enabled = false
# FillDisk fills the root disk.
# NOTE: This may incur charges for an EBS root volume. See burnmoney option.
simianarmy.chaos.burnio.enabled = false
# BurnIO causes disk activity on the root disk.
# NOTE: This may incur charges for an EBS root volume. See burnmoney option.
simianarmy.chaos.filldisk.enabled = false
# Where we know the chaos strategy will incur charges, we won't run it unless burnmoney is true.
simianarmy.chaos.burnmoney = false
# enable a specific ASG
# simianarmy.chaos.ASG.<asgName>.enabled = true
# simianarmy.chaos.ASG.<asgName>.probability = 1.0
# increase or decrease the termination limit for a specific ASG
# simianarmy.chaos.ASG.<asgName>.maxTerminationsPerDay = 1.0
# Enroll in mandatory terminations. If a group has not had a
# termination within the windowInDays range then it will terminate
# one instance in the group with a 0.5 probability (at some point in
# the next 2 days an instance should be terminated), then
# do nothing again for windowInDays. This forces "enabled" groups
# that have a probability of 0.0 to have terminations periodically.
simianarmy.chaos.mandatoryTermination.enabled = false
simianarmy.chaos.mandatoryTermination.windowInDays = 32
simianarmy.chaos.mandatoryTermination.defaultProbability = 0.5
# Enable notification for Chaos termination for a specific instance group
# simianarmy.chaos.<groupType>.<groupName>.notification.enabled = true
# Set the destination email the termination notification sent to for a specific instance group
# simianarmy.chaos.<groupType>.<groupName>.ownerEmail = foo@bar.com
# Set the source email that sends the termination notification
# simianarmy.chaos.notification.sourceEmail = foo@bar.com
# Enable notification for Chaos termination for all instance groups
#simianarmy.chaos.notification.global.enabled = true
# Set the destination email the termination notification is sent to for all instance groups
#simianarmy.chaos.notification.global.receiverEmail = foo@bar.com
# Set a prefix applied to the subject of all termination notifications
# Probably want to include a trailing space to separate from start of default text
#simianarmy.chaos.notification.subject.prefix = SubjectPrefix
# Set a suffix applied to the subject of all termination notifications
# Probably want to include an escaped space " \ " to separate from end of default text
#simianarmy.chaos.notification.subject.suffix = \ SubjectSuffix
# Set a prefix applied to the body of all termination notifications
# Probably want to include a trailing space to separate from start of default text
#simianarmy.chaos.notification.body.prefix = BodyPrefix
# Set a suffix applied to the body of all termination notifications
# Probably want to include an escaped space " \ " to separate from end of default text
#simianarmy.chaos.notification.body.suffix = \ BodySuffix
# Enable the email subject to be the same as the body, to include terminated instance and group information
#simianarmy.chaos.notification.subject.isBody = true
#set the tag filter on the ASGs to terminate only instances from the ASG with the this tag key and value
#simianarmy.chaos.ASGtag.key = chaos_monkey
#simianarmy.chaos.ASGtag.value = true
Actual Output
# The file contains the properties for Chaos Monkey.
# see documentation at:
# https://github.com/Netflix/SimianArmy/wiki/Configuration
# let chaos run
simianarmy.chaos.enabled = true
# don't allow chaos to kill (ie dryrun mode)
simianarmy.chaos.leashed = true
# set to "false" for Opt-In behavior, "true" for Opt-Out behavior
simianarmy.chaos.ASG.enabled = false
# uncomment this line to use tunable aggression
#simianarmy.client.chaos.class = com.netflix.simianarmy.tunable.TunablyAggressiveChaosMonkey
# default probability for all ASGs
simianarmy.chaos.ASG.probability = 1.0
# increase or decrease the termination limit
simianarmy.chaos.ASG.maxTerminationsPerDay = 1.0
# Strategies
simianarmy.chaos.shutdowninstance.enabled = true
simianarmy.chaos.blockallnetworktraffic.enabled = false
simianarmy.chaos.burncpu.enabled = false
simianarmy.chaos.killprocesses.enabled = false
simianarmy.chaos.nullroute.enabled = false
simianarmy.chaos.failapi.enabled = false
simianarmy.chaos.faildns.enabled = false
simianarmy.chaos.faildynamodb.enabled = false
simianarmy.chaos.fails3.enabled = false
simianarmy.chaos.networkcorruption.enabled = false
simianarmy.chaos.networklatency.enabled = false
simianarmy.chaos.networkloss.enabled = false
# Force-detaching EBS volumes may cause data loss
simianarmy.chaos.detachvolumes.enabled = false
# FillDisk fills the root disk.
# NOTE: This may incur charges for an EBS root volume. See burnmoney option.
simianarmy.chaos.burnio.enabled = false
# BurnIO causes disk activity on the root disk.
# NOTE: This may incur charges for an EBS root volume. See burnmoney option.
simianarmy.chaos.filldisk.enabled = false
# Where we know the chaos strategy will incur charges, we won't run it unless burnmoney is true.
simianarmy.chaos.burnmoney = false
# enable a specific ASG
# simianarmy.chaos.ASG.<asgName>.enabled = true
# simianarmy.chaos.ASG.<asgName>.probability = 1.0
# increase or decrease the termination limit for a specific ASG
# simianarmy.chaos.ASG.<asgName>.maxTerminationsPerDay = 1.0
# Enroll in mandatory terminations. If a group has not had a
# termination within the windowInDays range then it will terminate
# one instance in the group with a 0.5 probability (at some point in
# the next 2 days an instance should be terminated), then
# do nothing again for windowInDays. This forces "enabled" groups
# that have a probability of 0.0 to have terminations periodically.
simianarmy.chaos.mandatoryTermination.enabled = false
simianarmy.chaos.mandatoryTermination.windowInDays = 32
simianarmy.chaos.mandatoryTermination.defaultProbability = 0.5
# Enable notification for Chaos termination for a specific instance group
# simianarmy.chaos.<groupType>.<groupName>.notification.enabled = true
# Set the destination email the termination notification sent to for a specific instance group
# simianarmy.chaos.<groupType>.<groupName>.ownerEmail = foo@bar.com
# Set the source email that sends the termination notification
# simianarmy.chaos.notification.sourceEmail = foo@bar.com
# Enable notification for Chaos termination for all instance groups
#simianarmy.chaos.notification.global.enabled = true
# Set the destination email the termination notification is sent to for all instance groups
#simianarmy.chaos.notification.global.receiverEmail = foo@bar.com
# Set a prefix applied to the subject of all termination notifications
# Probably want to include a trailing space to separate from start of default text
#simianarmy.chaos.notification.subject.prefix = SubjectPrefix
# Set a suffix applied to the subject of all termination notifications
# Probably want to include an escaped space " \ " to separate from end of default text
#simianarmy.chaos.notification.subject.suffix = \ SubjectSuffix
# Set a prefix applied to the body of all termination notifications
# Probably want to include a trailing space to separate from start of default text
#simianarmy.chaos.notification.body.prefix = BodyPrefix
# Set a suffix applied to the body of all termination notifications
# Probably want to include an escaped space " \ " to separa# The file contains the properties for Chaos Monkey.
# see documentation at:
# https://github.com/Netflix/SimianArmy/wiki/Configuration
# let chaos run
simianarmy.chaos.enabled = {{getv "/simianarmy/chaos/enabled" "true"}}
# don't allow chaos to kill (ie dryrun mode)
simianarmy.chaos.leashed = {{getv "/simianarmy/chaos/leashed" "true"}}
# set to "false" for Opt-In behavior, "true" for Opt-Out behavior
simianarmy.chaos.ASG.enabled = {{getv "/simianarmy/chaos/asg/enabled" "false"}}
# uncomment this line to use tunable aggression
{{if exists "/simianarmy/client/chaos/class" -}}
simianarmy.client.chaos.class = {{getv "/simianarmy/client/chaos/class" "com.netflix.simianarmy.tunable.TunablyAggressiveChaosMonkey"}}
{{end -}}
# default probability for all ASGs
simianarmy.chaos.ASG.probability = {{getv "/simianarmy/chaos/asg/probability" "1.0"}}
# increase or decrease the termination limit
simianarmy.chaos.ASG.maxTerminationsPerDay = {{getv "/simianarmy/chaos/asg/maxterminationsperday" "1.0"}}
# Strategies
simianarmy.chaos.shutdowninstance.enabled = {{getv "/simianarmy/chaos/shutdowninstance/enabled" "true"}}
simianarmy.chaos.blockallnetworktraffic.enabled = {{getv "/simianarmy/chaos/blockallnetworktraffic/enabled" "false"}}
simianarmy.chaos.burncpu.enabled = {{getv "/simianarmy/chaos/burncpu/enabled" "false"}}
simianarmy.chaos.killprocesses.enabled = {{getv "/simianarmy/chaos/killprocesses/enabled" "false"}}
simianarmy.chaos.nullroute.enabled = {{getv "/simianarmy/chaos/nullroute/enabled" "false"}}
simianarmy.chaos.failapi.enabled = {{getv "/simianarmy/chaos/failapi/enabled" "false"}}
simianarmy.chaos.faildns.enabled = {{getv "/simianarmy/chaos/faildns/enabled" "false"}}
simianarmy.chaos.faildynamodb.enabled = {{getv "/simianarmy/chaos/faildynamodb/enabled" "false"}}
simianarmy.chaos.fails3.enabled = {{getv "/simianarmy/chaos/fails3/enabled" "false"}}
simianarmy.chaos.networkcorruption.enabled = {{getv "/simianarmy/chaos/networkcorruption/enabled" "false"}}
simianarmy.chaos.networklatency.enabled = {{getv "/simianarmy/chaos/networklatency/enabled" "false"}}
simianarmy.chaos.networkloss.enabled = {{getv "/simianarmy/chaos/networkloss/enabled" "false"}}
# Force-detaching EBS volumes may cause data loss
simianarmy.chaos.detachvolumes.enabled = {{getv "/simianarmy/chaos/detachvolumes/enabled" "false"}}
# FillDisk fills the root disk.
# NOTE: This may incur charges for an EBS root volume. See burnmoney option.
simianarmy.chaos.burnio.enabled = {{getv "/simianarmy/chaos/burnio/enabled" "false"}}
# BurnIO causes disk activity on the root disk.
# NOTE: This may incur charges for an EBS root volume. See burnmoney option.
simianarmy.chaos.filldisk.enabled = {{getv "/simianarmy/chaos/filldisk/enabled" "false"}}
# Where we know the chaos strategy will incur charges, we won't run it unless burnmoney is true.
simianarmy.chaos.burnmoney = {{getv "/simianarmy/chaos/burnmoney" "false"}}
# enable a specific ASG
{{if exists "/simianarmy/chaos/asg/<asgname>/enabled" -}}
simianarmy.chaos.ASG.<asgName>.enabled = {{getv "/simianarmy/chaos/asg/<asgname>/enabled" "true"}}
{{end -}}
{{if exists "/simianarmy/chaos/asg/<asgname>/probability" -}}
simianarmy.chaos.ASG.<asgName>.probability = {{getv "/simianarmy/chaos/asg/<asgname>/probability" "1.0"}}
{{end -}}
# increase or decrease the termination limit for a specific ASG
{{if exists "/simianarmy/chaos/asg/<asgname>/maxterminationsperday" -}}
simianarmy.chaos.ASG.<asgName>.maxTerminationsPerDay = {{getv "/simianarmy/chaos/asg/<asgname>/maxterminationsperday" "1.0"}}
{{end -}}
# Enroll in mandatory terminations. If a group has not had a
# termination within the windowInDays range then it will terminate
# one instance in the group with a 0.5 probability (at some point in
# the next 2 days an instance should be terminated), then
# do nothing again for windowInDays. This forces "enabled" groups
# that have a probability of 0.0 to have terminations periodically.
simianarmy.chaos.mandatoryTermination.enabled = {{getv "/simianarmy/chaos/mandatorytermination/enabled" "false"}}
simianarmy.chaos.mandatoryTermination.windowInDays = {{getv "/simianarmy/chaos/mandatorytermination/windowindays" "32"}}
simianarmy.chaos.mandatoryTermination.defaultProbability = {{getv "/simianarmy/chaos/mandatorytermination/defaultprobability" "0.5"}}
# Enable notification for Chaos termination for a specific instance group
{{if exists "/simianarmy/chaos/<grouptype>/<groupname>/notification/enabled" -}}
simianarmy.chaos.<groupType>.<groupName>.notification.enabled = {{getv "/simianarmy/chaos/<grouptype>/<groupname>/notification/enabled" "true"}}
{{end -}}
# Set the destination email the termination notification sent to for a specific instance group
{{if exists "/simianarmy/chaos/<grouptype>/<groupname>/owneremail" -}}
simianarmy.chaos.<groupType>.<groupName>.ownerEmail = {{getv "/simianarmy/chaos/<grouptype>/<groupname>/owneremail" "foo@bar.com"}}
{{end -}}
# Set the source email that sends the termination notification
{{if exists "/simianarmy/chaos/notification/sourceemail" -}}
simianarmy.chaos.notification.sourceEmail = {{getv "/simianarmy/chaos/notification/sourceemail" "foo@bar.com"}}
{{end -}}
# Enable notification for Chaos termination for all instance groups
{{if exists "/simianarmy/chaos/notification/global/enabled" -}}
simianarmy.chaos.notification.global.enabled = {{getv "/simianarmy/chaos/notification/global/enabled" "true"}}
{{end -}}
# Set the destination email the termination notification is sent to for all instance groups
{{if exists "/simianarmy/chaos/notification/global/receiveremail" -}}
simianarmy.chaos.notification.global.receiverEmail = {{getv "/simianarmy/chaos/notification/global/receiveremail" "foo@bar.com"}}
{{end -}}
# Set a prefix applied to the subject of all termination notifications
# Probably want to include a trailing space to separate from start of default text
{{if exists "/simianarmy/chaos/notification/subject/prefix" -}}
simianarmy.chaos.notification.subject.prefix = {{getv "/simianarmy/chaos/notification/subject/prefix" "SubjectPrefix"}}
{{end -}}
# Set a suffix applied to the subject of all termination notifications
# Probably want to include an escaped space " \ " to separate from end of default text
{{if exists "/simianarmy/chaos/notification/subject/suffix" -}}
simianarmy.chaos.notification.subject.suffix = {{getv "/simianarmy/chaos/notification/subject/suffix" "\ SubjectSuffix"}}
{{end -}}
# Set a prefix applied to the body of all termination notifications
# Probably want to include a trailing space to separate from start of default text
{{if exists "/simianarmy/chaos/notification/body/prefix" -}}
simianarmy.chaos.notification.body.prefix = {{getv "/simianarmy/chaos/notification/body/prefix" "BodyPrefix"}}
{{end -}}
# Set a suffix applied to the body of all termination notifications
# Probably want to include an escaped space " \ " to separa