duaner1634 2019-07-25 14:56
浏览 60

基于拆分字符串和键值对生成动态嵌套映射

I'm trying to generate a map of values based on an API call that returns an interface{} of items. I'm really not sure how to explain it better, but here is my situation.

I'm using Vault to store secrets that will generate a dynamic map to be used in a cluster orchestrator. An Example being:

secret/cluster

secret/cluster can have infinitely nested secrets e.g.

secret/cluster/team1/secreta/

secret/cluster/team2/secretb/app1/
secret/cluster/global

secret/cluster/team1/secreta will have Key-Value Pairs that signify a secret

e.g.

secret/cluster/team1/secreta
username: user
password: pass
database: db1
secret/cluster/team1
checksum: xxxxx

I wrote a function, that will iterate through every nested cluster and return a slice of every path that needs to be checked:

func getChildSecrets(path string) []string {

    for _, v := range vault.List(path) {
        if strings.HasSuffix(v, "/") {
            return append([]string{path}, getChildSecrets(fmt.Sprintf("%s%s", path, v))...)
        }
    }
    return []string{path}
}

My next step is to set a map[string]interface{} based on the name secret path and its values:

map[cluster][team1][secreta]{username: user, password: pass, database:db1}
map[cluster][team1]{checksum:xxxx}
  • 写回答

1条回答 默认 最新

  • doujiao8649 2019-07-25 17:53
    关注

    If I understand your question correctly, you are trying to convert Vault's path's into a map[string]interface{}? That's not going to be very helpful because you're going to have to perform a type assertion on each read. Be warned this is a very expensive N^2 operation against Vault and the algorithm you've proposed could lose data because it's valid to share the same name for a folder and a leaf. Nonetheless, here you go. I think you'll find it's a lot more complicated than you expect:

    package main
    
    import (
        "encoding/json"
        "fmt"
        "strings"
    
        vault "github.com/hashicorp/vault/api"
    )
    
    func main() {
        // Create a client object.
        client, err := vault.NewClient(vault.DefaultConfig())
        if err != nil {
            panic(err)
        }
    
        // Convert everything at the path to a map. This will only work with KV v1.
        m, err := SecretsToMap(client, "secret/")
        if err != nil {
            panic(err)
        }
    
        // Dump the result as JSON for easier viewing.
        j, err := json.MarshalIndent(m, "", "  ")
        if err != nil {
            panic(err)
        }
        fmt.Printf("%s", j)
    }
    
    // SecretsToMap recursively reads all paths in the root and converts them to a
    // map by each path segment.
    func SecretsToMap(client *vault.Client, root string) (map[string]interface{}, error) {
        var m = make(map[string]interface{})
        if err := secretsToMap(client, m, root); err != nil {
            return nil, err
        }
        return m, nil
    }
    
    func secretsToMap(client *vault.Client, m map[string]interface{}, root string) error {
        // List everything at this path
        r, err := client.Logical().List(root)
        if err != nil {
            return fmt.Errorf("failed to list %s: %s", root, err)
        }
    
        // Do nothing if the leaf is empty - this might be undesireable depending on
        // your use case (you may want the empty leaf here with {}). Modify as
        // appropriate.
        if r == nil || len(r.Data) == 0 {
            return nil
        }
    
        // Type conversions to get the response as []string.
        keysRaw, ok := r.Data["keys"]
        if !ok {
            return nil
        }
        keys, ok := keysRaw.([]interface{})
        if !ok {
            return fmt.Errorf("%q is not []interface{}", keysRaw)
        }
        list := make([]string, len(keys))
        for i, v := range keys {
            str, ok := v.(string)
            if !ok {
                return fmt.Errorf("%q is not string", v)
            }
            list[i] = str
        }
    
        // Iterate over each response
        for _, v := range list {
            // pth is the full path (root + child)
            pth := root + v
    
            // If this is a folder, recurse.
            if strings.HasSuffix(v, "/") {
                if err := secretsToMap(client, m, pth); err != nil {
                    return err
                }
            } else {
                // Not a folde,r read the secret, handing errors and nil/empty data
                secret, err := client.Logical().Read(pth)
                if err != nil {
                    return fmt.Errorf("failed to read secret %s: %s", pth, err)
                }
    
                // This might not be what you want - modify as appropriate.
                if secret == nil || secret.Data == nil {
                    return nil
                }
    
                // Iterate over the folder parts and make a map entry if it does not
                // exist. This will panic and fail if you have leafs and folders with the
                // same name, but I'm just following your example.
                ptr := m
                for _, v := range strings.Split(root, "/") {
                    p := strings.Trim(v, "/")
                    if p == "" {
                        continue
                    }
    
                    // If there's no value at ptr (which is a moving target within the map),
                    // create a container for data.
                    if _, ok := ptr[p]; !ok {
                        ptr[p] = map[string]interface{}{}
                    }
    
                    // Advance the map pointer deeper into the map.
                    ptr = ptr[p].(map[string]interface{})
                }
    
                // We've reached the leaf, set the data.
                ptr[v] = secret.Data
            }
        }
    
        return nil
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 stm32开发clion时遇到的编译问题