如何在不渲染的情况下合并Go模板

问题描述 投票:0回答:1

例如我想合并下面的两个模板,

# v1
instance_db_address: {{ .MYSQL_ADDRESS }}
instance_db_port: {{ .MYSQL_PORT }}
imagePullSecrets: {{ .IMAGE_PULL_SECRET }}
resources:
  requests:
    memory: "1Gi"
    cpu: "512m"
  limits:
    memory: "1Gi"
    cpu: "1000m"
# v2
instance_db_address: {{ .MYSQL_ADDRESS2 }}
instance_db_port: {{ .MYSQL_PORT2 }}

答案应该是

instance_db_address: {{ .MYSQL_ADDRESS2 }}
instance_db_port: {{ .MYSQL_PORT2 }}
imagePullSecrets: {{ .IMAGE_PULL_SECRET }}
resources:
  requests:
    memory: "1Gi"
    cpu: "512m"
  limits:
    memory: "1Gi"
    cpu: "1000m"

我发现很难合并,因为这些不是 yaml,有人可以帮助我吗?

我尝试在每行的大括号中添加一些引号,但我想保留大括号而不使用引号。所以效果不太好。

go kubernetes kubernetes-helm
1个回答
0
投票

假设您正在使用

text/template
来处理这些模板,您可以(并且应该)合并所谓的“解析树”——这是模板被解析成的内容(实际上是
Execute
d),而不是模板的源文本。

实现此目的的一种方法:

package main

import (
    "errors"
    "fmt"
    "log"
    "os"
    "strings"
    "text/template"
    "text/template/parse"
)

const t1Text = `instance_db_address: {{ .MYSQL_ADDRESS }}
instance_db_port: {{ .MYSQL_PORT }}
imagePullSecrets: {{ .IMAGE_PULL_SECRET }}
resources:
  requests:
    memory: "1Gi"
    cpu: "512m"
  limits:
    memory: "1Gi"
    cpu: "1000m"
`

const t2Text = `instance_db_address: {{ .MYSQL_ADDRESS2 }}
instance_db_port: {{ .MYSQL_PORT2 }}
`

func merge(dst, src *template.Template) error {
    dstNodes := dst.Tree.Root.Nodes
    a1, p1, err := findAddrAndPortNodes(dstNodes)
    if err != nil {
        return err
    }

    srcNodes := src.Tree.Root.Nodes
    a2, p2, err := findAddrAndPortNodes(srcNodes)
    if err != nil {
        return err
    }

    dstNodes[a1] = srcNodes[a2]
    dstNodes[p1] = srcNodes[p2]

    return nil
}

func findAddrAndPortNodes(nodes []parse.Node) (
    addrIndex int, portIndex int, err error) {

    type curNode struct {
        text         string
        nodeIndexPtr *int
    }

    const (
        modeText = iota
        modeAction
    )

    toLocate := map[string]*int{
        "instance_db_address:": &addrIndex,
        "instance_db_port:":    &portIndex,
    }

    var cur curNode

    mode := modeText
NODE_LOOP:
    for ix, n := range nodes {
        switch mode {
        case modeText:
            tn, ok := n.(*parse.TextNode)
            if !ok {
                continue
            }

            nodeText := strings.TrimSpace(string(tn.Text))
            for text, dst := range toLocate {
                if nodeText != text {
                    continue
                }

                cur.text = text
                cur.nodeIndexPtr = dst
                mode = modeAction

                continue NODE_LOOP
            }
        case modeAction:
            _, ok := n.(*parse.ActionNode)
            if !ok {
                return 0, 0, fmt.Errorf(
                    "unexpected node type: %T for %q", n, cur.text)
            }

            *cur.nodeIndexPtr = ix

            delete(toLocate, cur.text)
            if len(toLocate) == 0 {
                break NODE_LOOP
            }

            mode = modeText
            continue
        }
    }

    if len(toLocate) != 0 {
        return 0, 0, errors.New("failed to locate all nodes")
    }

    return
}

func main() {
    t1 := template.New("v1")
    t1, err := t1.Parse(t1Text)
    if err != nil {
        log.Fatalln("failed to parse 1st template:", err)
    }

    t2 := template.New("v2")
    t2, err = t2.Parse(t2Text)
    if err != nil {
        log.Fatalln("failed to parse 2nd template:", err)
    }

    ctx := map[string]string{
        "IMAGE_PULL_SECRET": "whatever",
        "MYSQL_ADDRESS":     "orig_addr",
        "MYSQL_PORT":        "orig_port",
        "MYSQL_ADDRESS2":    "new_addr",
        "MYSQL_PORT2":       "new_port",
    }

    err = t1.Execute(os.Stdout, ctx)
    if err != nil {
        log.Fatalln("failed to execute 1st template:", err)
    }

    fmt.Println(strings.Repeat("-", 80))

    err = merge(t1, t2)
    if err != nil {
        log.Fatalln("failed to merge:", err)
    }

    err = t1.Execute(os.Stdout, ctx)
    if err != nil {
        log.Fatal("failed to execute modified template:", err)
    }
}

运行时,该程序会生成:

instance_db_address: orig_addr
instance_db_port: orig_port
imagePullSecrets: whatever
resources:
  requests:
    memory: "1Gi"
    cpu: "512m"
  limits:
    memory: "1Gi"
    cpu: "1000m"
--------------------------------------------------------------------------------
instance_db_address: new_addr
instance_db_port: new_port
imagePullSecrets: whatever
resources:
  requests:
    memory: "1Gi"
    cpu: "512m"
  limits:
    memory: "1Gi"
    cpu: "1000m"

这意味着两个兴趣点的动作实际上在原始模板中发生了变化。

游乐场

要了解合并代码的工作原理,请从

template.Template
的定义开始。

© www.soinside.com 2019 - 2024. All rights reserved.