Skip to content

Commit 0c08a1e

Browse files
author
Lukas Gentele
authored
Merge pull request #320 from covexo/issue-315
Issue 315
2 parents 33867d0 + d1ee89d commit 0c08a1e

6 files changed

Lines changed: 464 additions & 393 deletions

File tree

cmd/init.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) {
134134
}
135135
}
136136

137-
configutil.Merge(config, &v1.Config{
137+
configutil.Merge(&config, &v1.Config{
138138
Version: configutil.String(configutil.CurrentConfigVersion),
139139
DevSpace: &v1.DevSpaceConfig{
140140
Deployments: &[]*v1.DeploymentConfig{},
@@ -152,7 +152,7 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) {
152152
Auth: &v1.RegistryAuth{},
153153
},
154154
},
155-
})
155+
}, true)
156156

157157
imageMap := *config.Images
158158
cmd.defaultImage = imageMap["default"]
@@ -259,21 +259,21 @@ func (cmd *InitCmd) useCloudProvider() bool {
259259
err := cloud.Update(providerConfig, config, addToContext, true)
260260
log.StopWait()
261261
if err != nil {
262-
log.Fatalf("Couldn't authenticate to devspace cloud: %v", err)
262+
log.Fatalf("Couldn't authenticate to DevSpace Cloud: %v", err)
263263
}
264264

265265
return true
266266
}
267267
} else {
268268
useDevSpaceCloud := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
269-
Question: "Do you want to use the devspace cloud? (free ready-to-use kubernetes) (yes | no)",
269+
Question: "Do you want to use the DevSpace Cloud? (free ready-to-use Kubernetes) (yes | no)",
270270
DefaultValue: "yes",
271271
ValidationRegexPattern: "^(yes)|(no)$",
272272
}) == "yes"
273273

274274
if useDevSpaceCloud {
275275
addToContext := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
276-
Question: "Do you want to add the devspace-cloud to the $HOME/.kube/config file? (yes | no)",
276+
Question: "Do you want to add the DevSpace Cloud to the $HOME/.kube/config file? (yes | no)",
277277
DefaultValue: "yes",
278278
ValidationRegexPattern: "^(yes)|(no)$",
279279
}) == "yes"
@@ -284,7 +284,7 @@ func (cmd *InitCmd) useCloudProvider() bool {
284284
err := cloud.Update(providerConfig, config, addToContext, true)
285285
log.StopWait()
286286
if err != nil {
287-
log.Fatalf("Couldn't authenticate to devspace cloud: %v", err)
287+
log.Fatalf("Couldn't authenticate to DevSpace Cloud: %v", err)
288288
}
289289

290290
return true

cmd/up.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) {
121121
initCmd.Run(nil, []string{})
122122

123123
// Ensure that config is initialized correctly
124-
config := configutil.GetConfig()
125-
configutil.SetDefaults(config)
124+
configutil.SetDefaultsOnce()
126125
}
127126

128127
// Create kubectl client

pkg/devspace/config/configutil/get.go

Lines changed: 72 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ const DefaultDevspaceDeploymentName = "devspace-default"
3131
const CurrentConfigVersion = "v1alpha1"
3232

3333
// Global config vars
34-
var config *v1.Config
35-
var configRaw *v1.Config
36-
var overwriteConfig *v1.Config
37-
var overwriteConfigRaw *v1.Config
34+
var config *v1.Config // merged config
35+
var configRaw *v1.Config // config from .devspace/config.yaml
36+
var overwriteConfig *v1.Config // overwrite config from .devspace/config.yaml
37+
var defaultConfig *v1.Config // default config values
3838

3939
// Thread-safety helper
4040
var getConfigOnce sync.Once
41+
var setDefaultsOnce sync.Once
4142

4243
// ConfigExists checks whether the yaml file for the config exists
4344
func ConfigExists() (bool, error) {
@@ -57,9 +58,10 @@ func ConfigExists() (bool, error) {
5758
func InitConfig() *v1.Config {
5859
getConfigOnce.Do(func() {
5960
config = makeConfig()
61+
overwriteConfig = makeConfig()
6062
configRaw = makeConfig()
6163
overwriteConfig = makeConfig()
62-
overwriteConfigRaw = makeConfig()
64+
defaultConfig = makeConfig()
6365
})
6466

6567
return config
@@ -69,28 +71,28 @@ func InitConfig() *v1.Config {
6971
func GetConfig() *v1.Config {
7072
getConfigOnce.Do(func() {
7173
config = makeConfig()
74+
overwriteConfig = makeConfig()
7275
configRaw = makeConfig()
7376
overwriteConfig = makeConfig()
74-
overwriteConfigRaw = makeConfig()
77+
defaultConfig = makeConfig()
7578

7679
err := loadConfig(configRaw, ConfigPath)
7780
if err != nil {
7881
log.Errorf("Loading config: %v", err)
7982
log.Fatal("Please run `devspace init -r` to repair your config")
8083
}
8184

82-
//ignore error as overwrite.yaml is optional
83-
loadConfig(overwriteConfigRaw, OverwriteConfigPath)
84-
85-
Merge(&config, configRaw)
86-
Merge(&overwriteConfig, overwriteConfigRaw)
87-
Merge(&config, overwriteConfig)
88-
89-
if config.Version == nil || *config.Version != CurrentConfigVersion {
85+
if configRaw.Version == nil || *configRaw.Version != CurrentConfigVersion {
9086
log.Fatal("Your config is out of date. Please run `devspace init -r` to update your config")
9187
}
9288

93-
SetDefaults(config)
89+
//ignore error as overwrite.yaml is optional
90+
loadConfig(overwriteConfig, OverwriteConfigPath)
91+
92+
Merge(&config, configRaw, false)
93+
Merge(&config, overwriteConfig, true)
94+
95+
SetDefaultsOnce()
9496
})
9597

9698
return config
@@ -103,67 +105,78 @@ func GetOverwriteConfig() *v1.Config {
103105
return overwriteConfig
104106
}
105107

106-
// SetDefaults ensures that specific values are set in the config
107-
func SetDefaults(config *v1.Config) {
108-
defaultNamespace, err := GetDefaultNamespace(config)
109-
if err != nil {
110-
log.Fatalf("Error retrieving default namespace: %v", err)
111-
}
112-
113-
// Initialize Namespaces
114-
if config.DevSpace != nil {
115-
needTiller := config.InternalRegistry != nil
108+
// SetDefaultsOnce ensures that specific values are set in the config
109+
func SetDefaultsOnce() {
110+
setDefaultsOnce.Do(func() {
111+
defaultNamespace, err := GetDefaultNamespace(config)
112+
if err != nil {
113+
log.Fatalf("Error retrieving default namespace: %v", err)
114+
}
116115

117-
if config.DevSpace.Deployments != nil {
118-
for index, deployConfig := range *config.DevSpace.Deployments {
119-
if deployConfig.Name == nil {
120-
log.Fatalf("Error in config: Unnamed deployment at index %d", index)
121-
}
122-
if deployConfig.Namespace == nil {
123-
deployConfig.Namespace = String("")
124-
}
125-
if deployConfig.Helm != nil {
126-
needTiller = true
116+
// Initialize Namespaces
117+
if config.DevSpace != nil {
118+
needTiller := config.InternalRegistry != nil
119+
120+
if config.DevSpace.Deployments != nil {
121+
for index, deployConfig := range *config.DevSpace.Deployments {
122+
if deployConfig.Name == nil {
123+
log.Fatalf("Error in config: Unnamed deployment at index %d", index)
124+
}
125+
126+
if deployConfig.Namespace == nil {
127+
defaultDeployments := *defaultConfig.DevSpace.Deployments
128+
defaultValue := String("")
129+
130+
defaultDeployments = append(defaultDeployments, &v1.DeploymentConfig{
131+
Namespace: defaultValue,
132+
})
133+
defaultConfig.DevSpace.Deployments = &defaultDeployments
134+
deployConfig.Namespace = defaultValue
135+
}
136+
137+
if deployConfig.Helm != nil {
138+
needTiller = true
139+
}
127140
}
128141
}
129-
}
130142

131-
if config.DevSpace.Sync != nil {
132-
for _, syncPath := range *config.DevSpace.Sync {
133-
if syncPath.Namespace == nil {
134-
syncPath.Namespace = String("")
143+
if config.DevSpace.Sync != nil {
144+
for _, syncPath := range *config.DevSpace.Sync {
145+
if syncPath.Namespace == nil {
146+
syncPath.Namespace = String("")
147+
}
135148
}
136149
}
137-
}
138150

139-
if config.DevSpace.Ports != nil {
140-
for _, portForwarding := range *config.DevSpace.Ports {
141-
if portForwarding.Namespace == nil {
142-
portForwarding.Namespace = String("")
151+
if config.DevSpace.Ports != nil {
152+
for _, portForwarding := range *config.DevSpace.Ports {
153+
if portForwarding.Namespace == nil {
154+
portForwarding.Namespace = String("")
155+
}
143156
}
144157
}
145-
}
146158

147-
if needTiller && config.Tiller == nil {
148-
config.Tiller = &v1.TillerConfig{
149-
Namespace: &defaultNamespace,
159+
if needTiller && config.Tiller == nil {
160+
config.Tiller = &v1.TillerConfig{
161+
Namespace: &defaultNamespace,
162+
}
150163
}
151164
}
152-
}
153165

154-
if config.Images != nil {
155-
for _, buildConfig := range *config.Images {
156-
if buildConfig.Build != nil && buildConfig.Build.Kaniko != nil {
157-
if buildConfig.Build.Kaniko.Namespace == nil {
158-
buildConfig.Build.Kaniko.Namespace = String("")
166+
if config.Images != nil {
167+
for _, buildConfig := range *config.Images {
168+
if buildConfig.Build != nil && buildConfig.Build.Kaniko != nil {
169+
if buildConfig.Build.Kaniko.Namespace == nil {
170+
buildConfig.Build.Kaniko.Namespace = String("")
171+
}
159172
}
160173
}
161174
}
162-
}
163175

164-
if config.InternalRegistry != nil {
165-
config.InternalRegistry.Namespace = &defaultNamespace
166-
}
176+
if config.InternalRegistry != nil {
177+
config.InternalRegistry.Namespace = &defaultNamespace
178+
}
179+
})
167180
}
168181

169182
// GetDefaultNamespace retrieves the default namespace where to operate in, either from devspace config or kube config

pkg/devspace/config/configutil/merge.go

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package configutil
33
import (
44
"reflect"
55
"unsafe"
6+
7+
"gopkg.in/yaml.v2"
68
)
79

810
type pointerInterface struct {
@@ -12,25 +14,26 @@ type pointerInterface struct {
1214
// Merge deeply merges two objects
1315
// object MUST be a pointer of a pointer
1416
// overwriteObject MUST be a pointer
15-
func Merge(object interface{}, overwriteObject interface{}) {
16-
objectPointerUnsafe := (*(*pointerInterface)(unsafe.Pointer(&object))).Data
17-
overwriteObjectPointerUnsafe := (*(*pointerInterface)(unsafe.Pointer(&overwriteObject))).Data
18-
19-
merge(object, overwriteObject, objectPointerUnsafe, overwriteObjectPointerUnsafe)
20-
}
21-
22-
func merge(objectPointer interface{}, overwriteObjectPointer interface{}, objectPointerUnsafe unsafe.Pointer, overwriteObjectPointerUnsafe unsafe.Pointer) {
23-
overwriteObjectRef := reflect.ValueOf(overwriteObjectPointer)
17+
func Merge(object interface{}, overwriteObject interface{}, unifyPointers bool) {
18+
overwriteObjectRef := reflect.ValueOf(overwriteObject)
2419

2520
if !overwriteObjectRef.IsNil() {
2621
if overwriteObjectRef.Kind() == reflect.Ptr {
2722
overwriteObjectRef = overwriteObjectRef.Elem()
2823
}
29-
objectPointerReal := reflect.ValueOf(objectPointer).Elem().Interface()
24+
objectPointerReal := reflect.ValueOf(object).Elem().Interface()
3025
overwriteObject := overwriteObjectRef.Interface()
26+
27+
if !unifyPointers {
28+
overwriteObject = deepCopy(overwriteObject)
29+
30+
// ensure deepCopy only runs once
31+
unifyPointers = true
32+
}
3133
overwriteObjectType := reflect.TypeOf(overwriteObject)
3234
overwriteObjectKind := overwriteObjectType.Kind()
3335
objectPointerRef := reflect.ValueOf(objectPointerReal)
36+
3437
var objectRef reflect.Value
3538

3639
if !objectPointerRef.IsNil() {
@@ -59,10 +62,10 @@ func merge(objectPointer interface{}, overwriteObjectPointer interface{}, object
5962
overwriteValue := getMapValue(overwriteObject, key, genericPointerType)
6063
valuePointerRef := objectRef.MapIndex(keyRef)
6164

62-
if isZero(valuePointerRef) == false {
65+
if isZero(valuePointerRef) == false && !isTrivialDataType(valuePointerRef) {
6366
valuePointer := valuePointerRef.Interface()
6467

65-
Merge(&valuePointer, overwriteValue)
68+
Merge(&valuePointer, overwriteValue, unifyPointers)
6669
} else {
6770
keyRef := reflect.ValueOf(key)
6871
overwriteValueRef := reflect.ValueOf(overwriteValue)
@@ -74,23 +77,54 @@ func merge(objectPointer interface{}, overwriteObjectPointer interface{}, object
7477
case reflect.Struct:
7578
for i := 0; i < overwriteObjectRef.NumField(); i++ {
7679
overwriteValueRef := overwriteObjectRef.Field(i)
77-
overwriteValuePointerRef := reflect.ValueOf(overwriteValueRef.Interface())
7880

79-
if !overwriteValuePointerRef.IsNil() {
81+
if !overwriteValueRef.IsNil() {
8082
overwriteValue := overwriteValueRef.Interface()
8183
valuePointerRef := objectRef.Field(i)
8284

83-
if valuePointerRef.IsNil() {
84-
valuePointerRef.Set(reflect.ValueOf(overwriteValue))
85+
if valuePointerRef.IsNil() || isTrivialDataType(valuePointerRef) {
86+
valuePointerRef.Set(overwriteValueRef)
8587
} else {
8688
valuePointer := valuePointerRef.Interface()
8789

88-
Merge(&valuePointer, overwriteValue)
90+
Merge(&valuePointer, overwriteValue, unifyPointers)
8991
}
9092
}
9193
}
9294
default:
95+
objectPointerUnsafe := (*(*pointerInterface)(unsafe.Pointer(&object))).Data
96+
overwriteObjectPointerUnsafe := (*(*pointerInterface)(unsafe.Pointer(&overwriteObject))).Data
97+
9398
*(*unsafe.Pointer)(objectPointerUnsafe) = overwriteObjectPointerUnsafe
9499
}
95100
}
96101
}
102+
103+
func isTrivialDataType(value reflect.Value) bool {
104+
valueType := value.Type()
105+
106+
if valueType.Kind() == reflect.Ptr {
107+
valueType = valueType.Elem()
108+
}
109+
110+
switch valueType.Kind() {
111+
case reflect.Slice:
112+
return false
113+
case reflect.Map:
114+
return false
115+
case reflect.Struct:
116+
return false
117+
}
118+
return true
119+
}
120+
121+
func deepCopy(from interface{}) interface{} {
122+
yamlData, _ := yaml.Marshal(from)
123+
objectType := reflect.TypeOf(from)
124+
125+
copy := reflect.New(objectType).Interface()
126+
127+
yaml.Unmarshal(yamlData, copy)
128+
129+
return copy
130+
}

0 commit comments

Comments
 (0)