github.com/zntrio/harp/v2@v2.0.9/pkg/bundle/builders.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 bundle 19 20 import ( 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 "strings" 26 27 "google.golang.org/protobuf/encoding/protojson" 28 29 bundlev1 "github.com/zntrio/harp/v2/api/gen/go/harp/bundle/v1" 30 "github.com/zntrio/harp/v2/pkg/bundle/compare" 31 "github.com/zntrio/harp/v2/pkg/bundle/hcl" 32 "github.com/zntrio/harp/v2/pkg/bundle/secret" 33 "github.com/zntrio/harp/v2/pkg/sdk/types" 34 ) 35 36 // FromDump creates a bundle from a JSON Dump. 37 func FromDump(r io.Reader) (*bundlev1.Bundle, error) { 38 // Check parameters 39 if types.IsNil(r) { 40 return nil, fmt.Errorf("unable to process nil reader") 41 } 42 43 // Drain input content 44 content, err := io.ReadAll(r) 45 if err != nil { 46 return nil, fmt.Errorf("unable to read input content: %w", err) 47 } 48 49 // Build the container from json 50 var b bundlev1.Bundle 51 if err = protojson.Unmarshal(content, &b); err != nil { 52 return nil, fmt.Errorf("unable to decode JSON bundle: %w", err) 53 } 54 55 // Convert secret values to current value packing method. 56 for _, p := range b.Packages { 57 for _, s := range p.Secrets.Data { 58 // Decode json encoded value 59 var data interface{} 60 if errJSON := json.Unmarshal(s.Value, &data); errJSON != nil { 61 return nil, fmt.Errorf("unable to decode %q - %q secret value as json: %w", p.Name, s.Key, errJSON) 62 } 63 64 // Pack secret value 65 payload, err := secret.Pack(data) 66 if err != nil { 67 return nil, fmt.Errorf("unable to pack %q - %q secret value: %w", p.Name, s.Key, err) 68 } 69 70 // Replace current json encoded secret value by packed one. 71 s.Value = payload 72 } 73 } 74 75 // No error 76 return &b, nil 77 } 78 79 // FromOpLog convert oplog to a bundle. 80 func FromOpLog(oplog compare.OpLog) (*bundlev1.Bundle, error) { 81 // Create an empty bundle. 82 b := &bundlev1.Bundle{} 83 84 packageMap := map[string]*bundlev1.Package{} 85 86 // Generate patch rules 87 for _, op := range oplog { 88 switch op.Type { 89 case "package": 90 // Ignore package operation 91 continue 92 case "secret": 93 pathParts := strings.SplitN(op.Path, "#", 2) 94 pkg, ok := packageMap[pathParts[0]] 95 if !ok { 96 packageMap[pathParts[0]] = &bundlev1.Package{ 97 Name: pathParts[0], 98 Secrets: &bundlev1.SecretChain{ 99 Data: []*bundlev1.KV{}, 100 }, 101 } 102 pkg = packageMap[pathParts[0]] 103 } 104 105 // Process oplog event 106 switch op.Operation { 107 case compare.Add, compare.Replace: 108 // Pack secret value 109 payload, err := secret.Pack(op.Value) 110 if err != nil { 111 return nil, fmt.Errorf("unable to pack secret value for %q / %q: %w", pathParts[0], pathParts[1], err) 112 } 113 114 // Assign secret data 115 pkg.Secrets.Data = append(pkg.Secrets.Data, &bundlev1.KV{ 116 Key: pathParts[1], 117 Type: "string", 118 Value: payload, 119 }) 120 case compare.Remove: 121 // Ignore secret removal 122 } 123 default: 124 return nil, fmt.Errorf("unknown oplog type %q", op.Type) 125 } 126 } 127 128 // Assign packages 129 for _, p := range packageMap { 130 b.Packages = append(b.Packages, p) 131 } 132 133 // No error 134 return b, nil 135 } 136 137 // FromMap builds a secret container from map K/V. 138 func FromMap(input map[string]KV) (*bundlev1.Bundle, error) { 139 // Check input 140 if input == nil { 141 return nil, fmt.Errorf("unable to process nil map") 142 } 143 144 res := &bundlev1.Bundle{ 145 Packages: []*bundlev1.Package{}, 146 } 147 for packageName, secretKv := range input { 148 // Prepare a package 149 p := &bundlev1.Package{ 150 Name: packageName, 151 Secrets: &bundlev1.SecretChain{}, 152 } 153 154 // Prepare secret data 155 for k, v := range secretKv { 156 // Pack secret value 157 packed, err := secret.Pack(v) 158 if err != nil { 159 return nil, fmt.Errorf("unable to pack secret value for `%s`: %w", fmt.Sprintf("%s.%s", packageName, k), err) 160 } 161 162 // Add to secret package 163 p.Secrets.Data = append(p.Secrets.Data, &bundlev1.KV{ 164 Key: k, 165 Type: fmt.Sprintf("%T", v), 166 Value: packed, 167 }) 168 } 169 170 // Add package to result 171 res.Packages = append(res.Packages, p) 172 } 173 174 // No error 175 return res, nil 176 } 177 178 // FromHCL convert HCL-DSL to a bundle. 179 func FromHCL(input *hcl.Config) (*bundlev1.Bundle, error) { 180 // Check arguments 181 if input == nil { 182 return nil, errors.New("unable to process nil hcl object") 183 } 184 185 // Create an empty bundle. 186 res := &bundlev1.Bundle{ 187 Labels: input.Labels, 188 Annotations: input.Annotations, 189 Packages: []*bundlev1.Package{}, 190 } 191 192 for _, pkg := range input.Packages { 193 // Prepare a package 194 p := &bundlev1.Package{ 195 Name: pkg.Path, 196 Annotations: pkg.Annotations, 197 Labels: pkg.Labels, 198 Secrets: &bundlev1.SecretChain{}, 199 } 200 201 // Prepare secret data 202 for k, v := range pkg.Secrets { 203 // Pack secret value 204 packed, err := secret.Pack(v) 205 if err != nil { 206 return nil, fmt.Errorf("unable to pack secret value for `%s`: %w", fmt.Sprintf("%s.%s", pkg.Path, k), err) 207 } 208 209 // Add to secret package 210 p.Secrets.Data = append(p.Secrets.Data, &bundlev1.KV{ 211 Key: k, 212 Type: fmt.Sprintf("%T", v), 213 Value: packed, 214 }) 215 } 216 217 // Add package to result 218 res.Packages = append(res.Packages, p) 219 } 220 221 // No error 222 return res, nil 223 }