forked from getporter/porter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstrategy.go
199 lines (167 loc) · 5.15 KB
/
strategy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package secrets
import (
"encoding/json"
"errors"
"fmt"
"github.com/cnabio/cnab-go/valuesource"
"gopkg.in/yaml.v3"
)
// Set is an actual set of resolved values.
// This is the output of resolving a parameter or credential set file.
type Set map[string]string
// IsValid determines if the provided key (designating a name of a parameter
// or credential) is included in the provided set
func (s Set) IsValid(key string) bool {
for name := range s {
if name == key {
return true
}
}
return false
}
// ToCNAB converts this to a type accepted by the cnab-go runtime.
func (s Set) ToCNAB() valuesource.Set {
return valuesource.Set(s)
}
// SourceMap maps from a parameter or credential name to a source strategy for resolving its value.
type SourceMap struct {
// Name is the name of the parameter or credential.
Name string `json:"name" yaml:"name"`
// Source defines a strategy for resolving a value from the specified source.
Source Source `json:"source,omitempty" yaml:"source,omitempty"`
// ResolvedValue holds the resolved parameter or credential value.
// When a parameter or credential is resolved, it is loaded into this field. In all
// other cases, it is empty. This field is omitted during serialization.
ResolvedValue string `json:"-" yaml:"-"`
}
// Source specifies how to resolve a parameter or credential from an external
// source.
type Source struct {
// Strategy to resolve the source value, e.g. "secret" or "env".
Strategy string
// Hint to the strategy handler on how to resolve the value.
// For example the name of the secret in a secret store or name of an environment variable.
Hint string
}
func (s Source) MarshalRaw() interface{} {
if s.Strategy == "" {
return nil
}
return map[string]interface{}{s.Strategy: s.Hint}
}
func (s *Source) UnmarshalRaw(raw map[string]interface{}) error {
switch len(raw) {
case 0:
s.Strategy = ""
s.Hint = ""
return nil
case 1:
for k, v := range raw {
s.Strategy = k
if value, ok := v.(string); ok {
s.Hint = value
} else {
s.Hint = fmt.Sprintf("%v", s.Hint)
}
}
return nil
default:
return errors.New("multiple key/value pairs specified for source but only one may be defined")
}
}
var (
_ json.Marshaler = Source{}
_ json.Unmarshaler = &Source{}
_ yaml.Marshaler = Source{}
_ yaml.Unmarshaler = &Source{}
)
func (s Source) MarshalJSON() ([]byte, error) {
raw := s.MarshalRaw()
return json.Marshal(raw)
}
func (s *Source) UnmarshalJSON(data []byte) error {
var raw map[string]interface{}
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
return s.UnmarshalRaw(raw)
}
func (s *Source) UnmarshalYAML(value *yaml.Node) error {
var raw map[string]interface{}
err := value.Decode(&raw)
if err != nil {
return err
}
return s.UnmarshalRaw(raw)
}
func (s Source) MarshalYAML() (interface{}, error) {
return s.MarshalRaw(), nil
}
// SourceMapList is a list of mappings that can be access via index or the item name.
type SourceMapList []SourceMap
func (l SourceMapList) Less(i, j int) bool {
return l[i].Name < l[j].Name
}
func (l SourceMapList) Swap(i, j int) {
tmp := l[i]
l[i] = l[j]
l[j] = tmp
}
func (l SourceMapList) Len() int {
return len(l)
}
// HasName determines if the specified name is defined in the set.
func (l SourceMapList) HasName(name string) bool {
_, ok := l.GetByName(name)
return ok
}
// GetByName returns the resolution strategy for the specified name and a bool indicating if it was found.
func (l SourceMapList) GetByName(name string) (SourceMap, bool) {
for _, item := range l {
if item.Name == name {
return item, true
}
}
return SourceMap{}, false
}
// GetResolvedValue returns the resolved value of the specified name and a bool indicating if it was found.
// You must resolve the value before calling, it does not do resolution for you.
func (l SourceMapList) GetResolvedValue(name string) (interface{}, bool) {
item, ok := l.GetByName(name)
if ok {
return item.ResolvedValue, true
}
return nil, false
}
// ToResolvedValues converts the items to a map of key/value pairs, with the resolved values represented as CNAB-compatible strings
func (l SourceMapList) ToResolvedValues() map[string]string {
values := make(map[string]string, len(l))
for _, item := range l {
values[item.Name] = item.ResolvedValue
}
return values
}
// Merge applies the specified values on top of a base set of values. When a
// name exists in both sets, use the value from the overrides
func (l SourceMapList) Merge(overrides SourceMapList) SourceMapList {
result := l
// Make a lookup from the name to its index in result so that we can quickly find a
// named item while merging
lookup := make(map[string]int, len(result))
for i, item := range result {
lookup[item.Name] = i
}
for _, item := range overrides {
// If the name is in the base, overwrite its value with the override provided
if i, ok := lookup[item.Name]; ok {
result[i].Source = item.Source
// Just in case the value was resolved, include in the merge results
result[i].ResolvedValue = item.ResolvedValue
} else {
// Append the override to the list of results if it's not in the base set of values
result = append(result, item)
}
}
return result
}