get.porter.sh/porter@v1.3.0/pkg/cnab/parameter_sources.go (about) 1 package cnab 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 ) 8 9 const ( 10 // ParameterSourcesExtensionShortHand is the short suffix of the ParameterSourcesExtensionKey. 11 ParameterSourcesExtensionShortHand = "parameter-sources" 12 13 // ParameterSourcesExtensionKey represents the full key for the Parameter Sources Extension. 14 ParameterSourcesExtensionKey = OfficialExtensionsPrefix + ParameterSourcesExtensionShortHand 15 16 // ParameterSourcesExtensionSchema represents the schema for the Docker Extension. 17 ParameterSourcesSchema = "https://cnab.io/v1/parameter-sources.schema.json" 18 // ParameterSourceTypeOutput defines a type of parameter source that is provided by a bundle output. 19 ParameterSourceTypeOutput = "output" 20 // ParameterSourceTypeDependencyOutput defines a type of parameter source that is provided by a bundle's dependency 21 // output. 22 ParameterSourceTypeDependencyOutput = "dependencies.output" 23 ) 24 25 // ParameterSourcesExtension represents a required extension that specifies how 26 // to default parameter values. 27 var ParameterSourcesExtension = RequiredExtension{ 28 Shorthand: ParameterSourcesExtensionShortHand, 29 Key: ParameterSourcesExtensionKey, 30 Schema: ParameterSourcesSchema, 31 Reader: ParameterSourcesReader, 32 } 33 34 // ParameterSources describes the set of custom extension metadata associated 35 // with the Parameter Sources extension 36 type ParameterSources map[string]ParameterSource 37 38 // SetParameterFromOutput creates an entry in the parameter sources section setting 39 // the parameter's value using the specified output's value. 40 func (ps *ParameterSources) SetParameterFromOutput(parameter string, output string) { 41 if *ps == nil { 42 *ps = ParameterSources{} 43 } 44 45 (*ps)[parameter] = ParameterSource{ 46 Priority: []string{ParameterSourceTypeOutput}, 47 Sources: ParameterSourceMap{ 48 ParameterSourceTypeOutput: OutputParameterSource{OutputName: output}, 49 }, 50 } 51 } 52 53 // SetParameterFromDependencyOutput creates an entry in the parameter sources section setting 54 // the parameter's value using the specified dependency's output value. 55 func (ps *ParameterSources) SetParameterFromDependencyOutput(parameter string, dep string, output string) { 56 if *ps == nil { 57 *ps = ParameterSources{} 58 } 59 60 (*ps)[parameter] = ParameterSource{ 61 Priority: []string{ParameterSourceTypeDependencyOutput}, 62 Sources: ParameterSourceMap{ 63 ParameterSourceTypeDependencyOutput: DependencyOutputParameterSource{ 64 Dependency: dep, 65 OutputName: output}, 66 }, 67 } 68 } 69 70 type ParameterSource struct { 71 // Priority is an array of source types in the priority order that they should be used to 72 // populated the parameter. 73 Priority []string `json:"priority" mapstructure:"priority"` 74 75 // Sources is a map of key/value pairs of a source type and definition for 76 // the parameter value. 77 Sources ParameterSourceMap `json:"sources" mapstructure:"sources"` 78 } 79 80 // ListSourcesByPriority returns the parameter sources by the requested priority, 81 // if none is specified, they are unsorted. 82 func (s ParameterSource) ListSourcesByPriority() []ParameterSourceDefinition { 83 sources := make([]ParameterSourceDefinition, 0, len(s.Sources)) 84 if len(s.Priority) == 0 { 85 for _, source := range s.Sources { 86 sources = append(sources, source) 87 } 88 } else { 89 for _, sourceType := range s.Priority { 90 sources = append(sources, s.Sources[sourceType]) 91 } 92 } 93 return sources 94 } 95 96 type ParameterSourceMap map[string]ParameterSourceDefinition 97 98 func (m *ParameterSourceMap) UnmarshalJSON(data []byte) error { 99 if *m == nil { 100 *m = ParameterSourceMap{} 101 } 102 103 var rawMap map[string]interface{} 104 err := json.Unmarshal(data, &rawMap) 105 if err != nil { 106 return err 107 } 108 109 for sourceKey, sourceDef := range rawMap { 110 rawDef, err := json.Marshal(sourceDef) 111 if err != nil { 112 return fmt.Errorf("error re-marshaling parameter source definition: %w", err) 113 } 114 115 switch sourceKey { 116 case ParameterSourceTypeOutput: 117 var output OutputParameterSource 118 err := json.Unmarshal(rawDef, &output) 119 if err != nil { 120 return fmt.Errorf("invalid parameter source definition for key %s: %w", sourceKey, err) 121 } 122 (*m)[ParameterSourceTypeOutput] = output 123 case ParameterSourceTypeDependencyOutput: 124 var depOutput DependencyOutputParameterSource 125 err := json.Unmarshal(rawDef, &depOutput) 126 if err != nil { 127 return fmt.Errorf("invalid parameter source definition for key %s: %w", sourceKey, err) 128 } 129 (*m)[ParameterSourceTypeDependencyOutput] = depOutput 130 default: 131 return fmt.Errorf("unsupported parameter source key %s", sourceKey) 132 } 133 } 134 135 return nil 136 } 137 138 type ParameterSourceDefinition interface { 139 } 140 141 // OutputParameterSource represents a parameter that is set using the value from 142 // a bundle output. 143 type OutputParameterSource struct { 144 OutputName string `json:"name" mapstructure:"name"` 145 } 146 147 // DependencyOutputParameterSource represents a parameter that is set using the value 148 // from a bundle's dependency output. 149 type DependencyOutputParameterSource struct { 150 Dependency string `json:"dependency" mapstructure:"dependency"` 151 OutputName string `json:"name" mapstructure:"name"` 152 } 153 154 // ReadParameterSources is a convenience method for returning a bonafide 155 // ParameterSources reference after reading from the applicable section from 156 // the provided bundle 157 func (b ExtendedBundle) ReadParameterSources() (ParameterSources, error) { 158 raw, err := b.ParameterSourcesReader() 159 if err != nil { 160 return nil, err 161 } 162 163 ps, ok := raw.(ParameterSources) 164 if !ok { 165 return nil, errors.New("unable to read parameter sources extension data") 166 } 167 168 return ps, nil 169 } 170 171 // ParameterSourcesReader is a Reader for the ParameterSourcesExtension, 172 // which reads from the applicable section in the provided bundle and 173 // returns the raw data in the form of an interface 174 func ParameterSourcesReader(bun ExtendedBundle) (interface{}, error) { 175 return bun.ParameterSourcesReader() 176 } 177 178 // ParameterSourcesReader is a Reader for the ParameterSourcesExtension, 179 // which reads from the applicable section in the provided bundle and 180 // returns the raw data in the form of an interface 181 func (b ExtendedBundle) ParameterSourcesReader() (interface{}, error) { 182 data, ok := b.Custom[ParameterSourcesExtensionKey] 183 if !ok { 184 return nil, errors.New("attempted to read parameter sources from bundle but none are defined") 185 } 186 187 dataB, err := json.Marshal(data) 188 if err != nil { 189 return nil, fmt.Errorf("could not marshal the untyped %q extension data %q: %w", 190 ParameterSourcesExtensionKey, string(dataB), err) 191 } 192 193 ps := ParameterSources{} 194 err = json.Unmarshal(dataB, &ps) 195 if err != nil { 196 return nil, fmt.Errorf("could not unmarshal the %q extension %q: %w", 197 ParameterSourcesExtensionKey, string(dataB), err) 198 } 199 200 return ps, nil 201 } 202 203 // SupportsParameterSources checks if the bundle supports parameter sources. 204 func (b ExtendedBundle) SupportsParameterSources() bool { 205 return b.SupportsExtension(ParameterSourcesExtensionKey) 206 } 207 208 // GetParameterSources checks if the parameter sources extension is present and returns its 209 // extension configuration. 210 func (e ProcessedExtensions) GetParameterSources() (ParameterSources, bool, error) { 211 rawExt, required := e[ParameterSourcesExtensionKey] 212 213 ext, ok := rawExt.(ParameterSources) 214 if !ok && required { 215 return ParameterSources{}, required, fmt.Errorf("unable to parse Parameter Sources extension config: %+v", rawExt) 216 } 217 218 return ext, required, nil 219 } 220 221 // HasParameterSources returns whether or not the bundle has parameter sources defined. 222 func (b ExtendedBundle) HasParameterSources() bool { 223 _, ok := b.Custom[ParameterSourcesExtensionKey] 224 return ok 225 } 226 227 // ParameterHasSource determines if the specified parameter has a parameter 228 // source defined. 229 func (b ExtendedBundle) ParameterHasSource(paramName string) bool { 230 sources, err := b.ReadParameterSources() 231 if err != nil { 232 return false 233 } 234 _, hasSource := sources[paramName] 235 return hasSource 236 }