github.com/zntrio/harp/v2@v2.0.9/pkg/tasks/from/kv.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package from 19 20 import ( 21 "context" 22 "encoding/json" 23 "fmt" 24 "strings" 25 26 "go.uber.org/zap" 27 28 bundlev1 "github.com/zntrio/harp/v2/api/gen/go/harp/bundle/v1" 29 "github.com/zntrio/harp/v2/pkg/bundle" 30 "github.com/zntrio/harp/v2/pkg/bundle/secret" 31 "github.com/zntrio/harp/v2/pkg/kv" 32 "github.com/zntrio/harp/v2/pkg/sdk/log" 33 "github.com/zntrio/harp/v2/pkg/tasks" 34 ) 35 36 type ExtractKVTask struct { 37 _ struct{} 38 ContainerWriter tasks.WriterProvider 39 BasePaths []string 40 Store kv.Store 41 LastPathItemAsSecretKey bool 42 } 43 44 func (t *ExtractKVTask) Run(ctx context.Context) error { 45 packages := map[string]*bundlev1.Package{} 46 47 // For each base path 48 for _, basePath := range t.BasePaths { 49 // List recusively items 50 items, err := t.Store.List(ctx, basePath) 51 if err != nil { 52 return fmt.Errorf("unable to extract key from store: %w", err) 53 } 54 55 // Prepare a package using each item 56 for _, item := range items { 57 // Extract packageName 58 packageName := t.extractPackageName(item.Key) 59 60 // Check if packages is already instancied 61 p, ok := packages[packageName] 62 if !ok { 63 p = &bundlev1.Package{ 64 Name: packageName, 65 Secrets: &bundlev1.SecretChain{ 66 Version: uint32(0), 67 Data: make([]*bundlev1.KV, 0), 68 NextVersion: nil, 69 PreviousVersion: nil, 70 }, 71 } 72 } 73 74 // Try to extract value as a json map 75 var secretData map[string]interface{} 76 errJSON := json.Unmarshal(item.Value, &secretData) 77 if errJSON != nil { 78 log.For(ctx).Debug("data could not be decoded as json", zap.Error(errJSON)) 79 80 // Create an arbitrary secret key 81 secretKey := strings.TrimPrefix(strings.TrimPrefix(item.Key, kv.GetDirectory(item.Key)), "/") 82 83 log.For(ctx).Debug("Creating secret for package", zap.String("package", packageName), zap.String("secret", secretKey)) 84 85 // Pack secret value 86 s, errPack := t.packSecret(secretKey, string(item.Value)) 87 if errPack != nil { 88 return fmt.Errorf("unable to pack secret value for path %q with key %q : %w", item.Key, secretKey, errPack) 89 } 90 91 // Add secret to package 92 p.Secrets.Data = append(p.Secrets.Data, s) 93 } else { 94 // Iterate over secret bundle 95 for k, v := range secretData { 96 // Pack secret value 97 s, errPack := t.packSecret(k, v) 98 if errPack != nil { 99 return fmt.Errorf("unable to pack secret value for path %q with key %q : %w", item.Key, k, errPack) 100 } 101 102 // Add secret to package 103 p.Secrets.Data = append(p.Secrets.Data, s) 104 } 105 } 106 107 // Update package map 108 packages[packageName] = p 109 } 110 } 111 112 // Prepare a bundle 113 b := &bundlev1.Bundle{ 114 Packages: make([]*bundlev1.Package, 0), 115 } 116 117 // Copy package to bundle 118 for _, p := range packages { 119 b.Packages = append(b.Packages, p) 120 } 121 122 // Create container 123 writer, err := t.ContainerWriter(ctx) 124 if err != nil { 125 return fmt.Errorf("unable to initialize container writer: %w", err) 126 } 127 128 // Dump bundle 129 if err = bundle.ToContainerWriter(writer, b); err != nil { 130 return fmt.Errorf("unable to produce exported bundle: %w", err) 131 } 132 133 return nil 134 } 135 136 // -----------------------------------------------------------------------------. 137 func (t *ExtractKVTask) packSecret(key string, value interface{}) (*bundlev1.KV, error) { 138 // Pack secret value 139 payload, err := secret.Pack(value) 140 if err != nil { 141 return nil, fmt.Errorf("unable to pack secret %q: %w", key, err) 142 } 143 144 // Build the secret object 145 return &bundlev1.KV{ 146 Key: key, 147 Type: fmt.Sprintf("%T", value), 148 Value: payload, 149 }, nil 150 } 151 152 func (t *ExtractKVTask) extractPackageName(key string) string { 153 if !t.LastPathItemAsSecretKey { 154 return strings.TrimPrefix(strings.TrimSuffix(key, "/"), "/") 155 } 156 157 // Extract directory 158 return strings.TrimPrefix(kv.GetDirectory(key), "/") 159 }