Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability to convert a helm chart #736

Merged
merged 3 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions tools/convert/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func memorysize(data map[string]any, key, oldkey string, example string) string
case int:
i64 = int64(i)
}
return units.HumanSize(float64(i64))
return fmt.Sprintf(`# %s: %s`, key, units.HumanSize(float64(i64)))
}
return fmt.Sprintf(`# %s: %v`, key, yamlf(example))
}
Expand Down Expand Up @@ -411,7 +411,14 @@ func yamlf(a any) string {
if pat.MatchString(v) {
return v
}
return fmt.Sprintf(`"%s"`, v)
hasSingleQuote := strings.Contains(v, "'")
hasDoubleQuote := strings.Contains(v, `"`)
switch {
case hasDoubleQuote && !hasSingleQuote:
return fmt.Sprintf(`'%s'`, v)
default:
return fmt.Sprintf("%#v", v)
}
case int:
return _formatIntWithUnderscores(v)
case float64:
Expand Down
108 changes: 90 additions & 18 deletions tools/convert/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"text/template"

"github.com/honeycombio/refinery/config"
Expand Down Expand Up @@ -63,6 +65,11 @@ func getType(filename string) string {
}
}

type configTemplateData struct {
Input string
Data map[string]any
}

func main() {
opts := Options{}

Expand All @@ -86,9 +93,14 @@ func main() {
filetype of the input file based on the extension, but you can override that with
the --type flag.

Because many organizations use helm charts to manage their refinery deployments, there
is a subcommand that can read a helm chart, extract both the rules and config from it,
and write them back out to a helm chart, while preserving the non-refinery portions.

It has other commands to help with the conversion process. Valid commands are:
convert config: convert a config file
convert rules: convert a rules file
convert helm: convert a helm values file
convert validate config: validate a config file against the 2.0 format
convert validate rules: validate a rules file against the 2.0 format
convert doc config: generate markdown documentation for the config file
Expand Down Expand Up @@ -161,7 +173,7 @@ func main() {
os.Exit(1)
}
os.Exit(0)
case "config", "rules", "validate":
case "config", "rules", "validate", "helm":
// do nothing yet because we need to parse the input file
default:
fmt.Fprintf(os.Stderr, "unknown subcommand %s; valid commands are config, doc config, validate config, rules, doc rules, validate rules\n", args[0])
Expand Down Expand Up @@ -190,31 +202,18 @@ func main() {
os.Exit(1)
}

tmplData := struct {
Input string
Data map[string]any
}{
tmplData := &configTemplateData{
Input: opts.Input,
Data: userConfig,
}

switch args[0] {
case "config":
tmpl := template.New("configV2.tmpl")
tmpl.Funcs(helpers())
tmpl, err = tmpl.ParseFS(filesystem, "templates/configV2.tmpl")
if err != nil {
fmt.Fprintf(os.Stderr, "template error %v\n", err)
os.Exit(1)
}

err = tmpl.Execute(output, tmplData)
if err != nil {
fmt.Fprintf(os.Stderr, "template error %v\n", err)
os.Exit(1)
}
ConvertConfig(tmplData, output)
case "rules":
ConvertRules(userConfig, output)
case "helm":
ConvertHelm(tmplData, output)
case "validate":
if args[1] == "config" {
if !ValidateFromMetadata(userConfig, output) {
Expand Down Expand Up @@ -256,6 +255,79 @@ func loadRulesMetadata() *config.Metadata {
return m
}

func ConvertConfig(tmplData *configTemplateData, w io.Writer) {
tmpl := template.New("configV2.tmpl")
tmpl.Funcs(helpers())
tmpl, err := tmpl.ParseFS(filesystem, "templates/configV2.tmpl")
if err != nil {
fmt.Fprintf(os.Stderr, "template error %v\n", err)
os.Exit(1)
}

err = tmpl.Execute(w, tmplData)
if err != nil {
fmt.Fprintf(os.Stderr, "template error %v\n", err)
os.Exit(1)
}
}

func ConvertHelm(tmplData *configTemplateData, w io.Writer) {
// convert config if we have it
helmConfigAny, ok := tmplData.Data["config"]
if ok {
helmConfig, ok := helmConfigAny.(map[string]any)
if !ok {
panic("config in helm chart is the wrong format!")
}
convertedConfig := &bytes.Buffer{}
// make a copy of this tmplData and overwrite the Data part
config := *tmplData
config.Data = helmConfig
// convert the config into the buffer
ConvertConfig(&config, convertedConfig)
kentquirk marked this conversation as resolved.
Show resolved Hide resolved
// read the buffer as YAML
decoder := yaml.NewDecoder(convertedConfig)
var decodedConfig map[string]any
saved := convertedConfig.String()
err := decoder.Decode(&decodedConfig)
if err != nil {
s := err.Error()
pat := regexp.MustCompile("yaml: line ([0-9]+):")
m := pat.FindStringSubmatch(s)
if len(m) > 1 {
linenum, _ := strconv.Atoi(m[1])
lines := strings.Split(saved, "\n")
for i := linenum - 15; i < linenum+5; i++ {
if len(lines) > i {
fmt.Printf("%d: %s\n", i, lines[i])
}
}
}
panic(err)
}
tmplData.Data["config"] = decodedConfig
}

// now try the rules
helmRulesAny, ok := tmplData.Data["rules"]
if ok {
helmRules, ok := helmRulesAny.(map[string]any)
if !ok {
panic("config in helm chart is the wrong format!")
}
rules := convertRulesToNewConfig(helmRules)
kentquirk marked this conversation as resolved.
Show resolved Hide resolved
tmplData.Data["rules"] = rules
}

// now we have our tmplData.Data with converted contents
// so we can just write it all back as YAML now
encoder := yaml.NewEncoder(w)
err := encoder.Encode(tmplData.Data)
if err != nil {
panic(err)
}
}

// This generates the template used by the convert tool.
func GenerateTemplate(w io.Writer) {
metadata := loadConfigMetadata()
Expand Down
6 changes: 5 additions & 1 deletion tools/convert/ruleconvert.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func CheckConfigForSampleRateChanges(cfg *config.V2SamplerConfig) map[string][]s
return warnings
}

func ConvertRules(rules map[string]any, w io.Writer) {
func convertRulesToNewConfig(rules map[string]any) *config.V2SamplerConfig {
// get the sampler type for the default rule
defaultSamplerType, _ := getValueForCaseInsensitiveKey(rules, "sampler", "DeterministicSampler")

Expand Down Expand Up @@ -167,7 +167,11 @@ func ConvertRules(rules map[string]any, w io.Writer) {

newConfig.Samplers[k] = sampler
}
return newConfig
}

func ConvertRules(rules map[string]any, w io.Writer) {
newConfig := convertRulesToNewConfig(rules)
w.Write([]byte(fmt.Sprintf("# Automatically generated on %s\n", time.Now().Format(time.RFC3339))))
yaml.NewEncoder(w).Encode(newConfig)

Expand Down