@ -16,6 +16,7 @@ limitations under the License.
package chartutil
package chartutil
import (
import (
"fmt"
"log"
"log"
"strings"
"strings"
@ -31,7 +32,7 @@ func ProcessDependencies(c *chart.Chart, v Values) error {
if err := processDependencyEnabled ( c , v , "" ) ; err != nil {
if err := processDependencyEnabled ( c , v , "" ) ; err != nil {
return err
return err
}
}
return processDependencyImport Values( c , false )
return processDependencyImport Export Values( c , false )
}
}
// ProcessDependenciesWithMerge checks through this chart's dependencies, processing accordingly.
// ProcessDependenciesWithMerge checks through this chart's dependencies, processing accordingly.
@ -41,7 +42,8 @@ func ProcessDependenciesWithMerge(c *chart.Chart, v Values) error {
if err := processDependencyEnabled ( c , v , "" ) ; err != nil {
if err := processDependencyEnabled ( c , v , "" ) ; err != nil {
return err
return err
}
}
return processDependencyImportValues ( c , true )
return processDependencyImportExportValues ( c , true )
}
}
// processDependencyConditions disables charts based on condition path value in values
// processDependencyConditions disables charts based on condition path value in values
@ -233,8 +235,9 @@ func set(path []string, data map[string]interface{}) map[string]interface{} {
return cur
return cur
}
}
// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field.
// processImportExportValues merges values between child and parent based on the chart's dependencies' ImportValues
func processImportValues ( c * chart . Chart , merge bool ) error {
// and ExportValues fields.
func processImportExportValues ( c * chart . Chart , merge bool ) error {
if c . Metadata . Dependencies == nil {
if c . Metadata . Dependencies == nil {
return nil
return nil
}
}
@ -250,8 +253,69 @@ func processImportValues(c *chart.Chart, merge bool) error {
return err
return err
}
}
b := make ( map [ string ] interface { } )
b := make ( map [ string ] interface { } )
// import values from each dependency if specified in import-values
// For each dependency export values from parent or import values from child if export-values or import-values
// specified.
for _ , r := range c . Metadata . Dependencies {
for _ , r := range c . Metadata . Dependencies {
if merge {
b = MergeTables ( b , getExportedValues ( c , r , cvals , merge ) )
b = MergeTables ( b , getImportedValues ( r , cvals , merge ) )
} else {
b = CoalesceTables ( b , getExportedValues ( c , r , cvals , merge ) )
b = CoalesceTables ( b , getImportedValues ( r , cvals , merge ) )
}
}
// Imported values from a child to a parent chart have a higher priority than
// values specified in the parent chart.
if merge {
// deep copying the cvals as there are cases where pointers can end
// up in the cvals when they are copied onto b in ways that break things.
cvals = deepCopyMap ( cvals )
c . Values = MergeTables ( b , cvals )
} else {
// Trimming the nil values from cvals is needed for backwards compatibility.
// Previously, the b value had been populated with cvals along with some
// overrides. This caused the coalescing functionality to remove the
// nil/null values. This trimming is for backwards compat.
cvals = trimNilValues ( cvals )
c . Values = CoalesceTables ( b , cvals )
}
return nil
}
func deepCopyMap ( vals map [ string ] interface { } ) map [ string ] interface { } {
valsCopy , err := copystructure . Copy ( vals )
if err != nil {
return vals
}
return valsCopy . ( map [ string ] interface { } )
}
func trimNilValues ( vals map [ string ] interface { } ) map [ string ] interface { } {
valsCopy , err := copystructure . Copy ( vals )
if err != nil {
return vals
}
valsCopyMap := valsCopy . ( map [ string ] interface { } )
for key , val := range valsCopyMap {
if val == nil {
log . Printf ( "trim deleting %q" , key )
// Iterate over the values and remove nil keys
delete ( valsCopyMap , key )
} else if istable ( val ) {
log . Printf ( "trim copying %q" , key )
// Recursively call into ourselves to remove keys from inner tables
valsCopyMap [ key ] = trimNilValues ( val . ( map [ string ] interface { } ) )
}
}
return valsCopyMap
}
// Generates values map which is imported from specified child to parent via import-values.
func getImportedValues ( r * chart . Dependency , cvals Values , merge bool ) map [ string ] interface { } {
b := make ( map [ string ] interface { } )
var outiv [ ] interface { }
var outiv [ ] interface { }
for _ , riv := range r . ImportValues {
for _ , riv := range r . ImportValues {
switch iv := riv . ( type ) {
switch iv := riv . ( type ) {
@ -294,64 +358,118 @@ func processImportValues(c *chart.Chart, merge bool) error {
}
}
}
}
}
}
// set our formatted import values
r . ImportValues = outiv
r . ImportValues = outiv
return b
}
// Generates values map which is exported from parent to specified child via export-values.
func getExportedValues ( c * chart . Chart , r * chart . Dependency , cvals Values , merge bool ) map [ string ] interface { } {
b := make ( map [ string ] interface { } )
var exportValues [ ] interface { }
for _ , rev := range r . ExportValues {
parent , child , err := parseExportValues ( rev , r )
if err != nil {
log . Printf ( "Warning: invalid ExportValues defined in chart %q for its dependency %q: %s" , c . Name ( ) , r . Name , err )
continue
}
}
// Imported values from a child to a parent chart have a higher priority than
exportValues = append ( exportValues , map [ string ] string {
// values specified in the parent chart.
"parent" : parent ,
"child" : child ,
} )
var childValMap map [ string ] interface { }
// try to get parent table
vm , err := cvals . Table ( parent )
if err == nil {
childValMap = pathToMap ( child , vm . AsMap ( ) )
} else {
// still it might be not a table but a simple value
value , e := cvals . PathValue ( parent )
if e != nil {
log . Printf ( "Warning: ExportValues defined in chart %q for its dependency %q can't get the parent path: %v" , c . Name ( ) , r . Name , err )
continue
}
childSlice := parsePath ( child )
childLen := len ( childSlice )
if childLen == 1 {
log . Printf ( "Warning: in ExportValues defined in chart %q for its dependency %q you are trying to assign a primitive data type (string, int, etc) to the root of your dependent chart values. We will ignore this ExportValues, because this is most likely not what you want. Fix the ExportValues to hide this warning." , c . Name ( ) , r . Name )
continue
}
childValMap = pathToMap (
joinPath ( childSlice [ : childLen - 1 ] ... ) ,
map [ string ] interface { } {
childSlice [ childLen - 1 ] : value ,
} ,
)
}
// merge new map with values exported to the child into the resulting values map
if merge {
if merge {
// deep copying the cvals as there are cases where pointers can end
b = MergeTables ( childValMap , b )
// up in the cvals when they are copied onto b in ways that break things.
cvals = deepCopyMap ( cvals )
c . Values = MergeTables ( b , cvals )
} else {
} else {
// Trimming the nil values from cvals is needed for backwards compatibility.
b = CoalesceTables ( childValMap , b )
// Previously, the b value had been populated with cvals along with some
}
// overrides. This caused the coalescing functionality to remove the
// nil/null values. This trimming is for backwards compat.
cvals = trimNilValues ( cvals )
c . Values = CoalesceTables ( b , cvals )
}
}
// set formatted export values
r . ExportValues = exportValues
return nil
return b
}
}
func deepCopyMap ( vals map [ string ] interface { } ) map [ string ] interface { } {
// Parse and validate export-values.
valsCopy , err := copystructure . Copy ( vals )
func parseExportValues ( rev interface { } , r * chart . Dependency ) ( string , string , error ) {
if err != nil {
var parent , child string
return vals
switch ev := rev . ( type ) {
case map [ string ] interface { } :
var ok bool
parent , ok = ev [ "parent" ] . ( string )
if ! ok {
return "" , "" , fmt . Errorf ( "parent must be a string" )
}
child , ok = ev [ "child" ] . ( string )
if ! ok {
return "" , "" , fmt . Errorf ( "child must be a string" )
}
}
return valsCopy . ( map [ string ] interface { } )
}
func trimNilValues ( vals map [ string ] interface { } ) map [ string ] interface { } {
switch parent {
valsCopy , err := copystructure . Copy ( vals )
case "" , "." :
if err != nil {
return "" , "" , fmt . Errorf ( "parent %q is not allowed" , parent )
return vals
}
}
valsCopyMap := valsCopy . ( map [ string ] interface { } )
for key , val := range valsCopyMap {
switch child {
if val == nil {
case "" , "." :
log . Printf ( "trim deleting %q" , key )
child = r . Name
// Iterate over the values and remove nil keys
default :
delete ( valsCopyMap , key )
child = r . Name + "." + child
} else if istable ( val ) {
log . Printf ( "trim copying %q" , key )
// Recursively call into ourselves to remove keys from inner tables
valsCopyMap [ key ] = trimNilValues ( val . ( map [ string ] interface { } ) )
}
}
case string :
switch parent = ev ; parent {
case "" , "." :
parent = "exports"
default :
parent = "exports." + parent
}
child = r . Name
default :
return "" , "" , fmt . Errorf ( "invalid type of ExportValues" )
}
}
return valsCopyMap
return parent, child , nil
}
}
// processDependencyImportValues imports specified chart values from child to parent.
// processDependencyImportExportValues imports (child to parent) and/or exports (parent to child) values if
func processDependencyImportValues ( c * chart . Chart , merge bool ) error {
// import-values or export-values specified for the dependency.
func processDependencyImportExportValues ( c * chart . Chart , merge bool ) error {
for _ , d := range c . Dependencies ( ) {
for _ , d := range c . Dependencies ( ) {
// recurse
// recurse
if err := processDependencyImportValues ( d , merge ) ; err != nil {
if err := processDependencyImport Export Values( d , merge ) ; err != nil {
return err
return err
}
}
}
}
return processImport Values( c , merge )
return processImport Export Values( c , merge )
}
}