pkg/bundle/bundle.go (101 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package bundle
import (
"fmt"
"reflect"
"sort"
"gitlab.com/NebulousLabs/merkletree"
bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1"
csov1 "github.com/elastic/harp/pkg/cso/v1"
"golang.org/x/crypto/blake2b"
)
// Annotate a bundle object.
func Annotate(obj AnnotationOwner, key, value string) {
updateAnnotations(obj, obj.GetAnnotations(), key, value)
}
// Labelize a bundle object.
func Labelize(obj LabelOwner, key, value string) {
updateLabels(obj, obj.GetLabels(), key, value)
}
// Tree returns a merkle tree based on secrets hierarchy
func Tree(b *bundlev1.Bundle) (*merkletree.Tree, *Statistic, error) {
// Check bundle
if b == nil {
return nil, nil, fmt.Errorf("unable to process nil bundle")
}
// Calculate merkle tree root
h, err := blake2b.New512(nil)
if err != nil {
return nil, nil, fmt.Errorf("unable to initialize hash function for merkle tree")
}
// Initialize merkle tree
tree := merkletree.New(h)
if err = tree.SetIndex(1); err != nil {
return nil, nil, fmt.Errorf("unable to initialize merkle tree")
}
// Prepare statistics
stats := &Statistic{
SecretCount: 0,
PackageCount: 0,
CSOCompliantPackageNameCount: 0,
}
// Ensure packages order
sort.SliceStable(b.Packages, func(i, j int) bool {
return b.Packages[i].Name < b.Packages[j].Name
})
// All packages
for _, p := range b.Packages {
// Increment package count
stats.PackageCount++
// Check compliance with CSO
if errValidate := csov1.Validate(p.Name); errValidate == nil {
stats.CSOCompliantPackageNameCount++
}
// Prepare secret uri list
uris := []string{}
// Follow secret chain
if p.Secrets != nil {
for _, s := range p.Secrets.Data {
// Increment secret count
stats.SecretCount++
// Build merkle tree leaf
uris = append(uris, fmt.Sprintf("%s:%d:%s:%x", p.Name, p.Secrets.Version, s.Key, blake2b.Sum512(s.Value)))
}
// Sort them
sort.Strings(uris)
// Push sorted secret uri as proof
for _, u := range uris {
tree.Push([]byte(u))
}
}
}
// Return the tree
return tree, stats, nil
}
// Paths returns ordered bundle secret paths.
func Paths(b *bundlev1.Bundle) ([]string, error) {
// Check input
if b == nil {
return nil, fmt.Errorf("unable to process nil bundle")
}
// Get all paths
res := []string{}
for _, p := range b.Packages {
res = append(res, p.Name)
}
// Sort result
sort.SliceStable(res, func(i, j int) bool {
return res[i] < res[j]
})
// No error
return res, nil
}
// SecretReader is used by template engine to resolve secret from secret container.
func SecretReader(b *bundlev1.Bundle) func(path string) (map[string]interface{}, error) {
return func(secretPath string) (map[string]interface{}, error) {
return Read(b, secretPath)
}
}
// -----------------------------------------------------------------------------
func updateAnnotations(obj interface{}, m map[string]string, key, value string) {
// Check allocation
if m == nil {
m = map[string]string{}
}
// Check if map key is already assigned
if _, ok := m[key]; ok {
return
}
// Assign value
m[key] = value
// Reaffect map to owner
// Really not fan of this ... but protobuf doesn't generate setters for go
mv := reflect.ValueOf(m)
f := reflect.ValueOf(obj).Elem().FieldByName("Annotations")
if f.IsValid() && f.CanSet() && f.Kind() == mv.Kind() {
f.Set(mv)
}
}
func updateLabels(obj interface{}, m map[string]string, key, value string) {
// Check allocation
if m == nil {
m = map[string]string{}
}
// Check if map key is already assigned
if _, ok := m[key]; ok {
return
}
// Assign value
m[key] = value
// Reaffect map to owner
// Really not fan of this ... but protobuf doesn't generate setters for go
mv := reflect.ValueOf(m)
f := reflect.ValueOf(obj).Elem().FieldByName("Labels")
if f.IsValid() && f.CanSet() && f.Kind() == mv.Kind() {
f.Set(mv)
}
}