Skip to content

Commit 8ab48fa

Browse files
committed
feat(api): validate required app config items
1 parent 5472371 commit 8ab48fa

File tree

10 files changed

+498
-40
lines changed

10 files changed

+498
-40
lines changed

api/controllers/kubernetes/install/app.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ func (c *InstallController) SetAppConfigValues(ctx context.Context, values map[s
5151
}
5252
}()
5353

54+
err = c.appConfigManager.ValidateConfigValues(*c.releaseData.AppConfig, values)
55+
if err != nil {
56+
return fmt.Errorf("validate app config values: %w", err)
57+
}
58+
5459
err = c.appConfigManager.SetConfigValues(*c.releaseData.AppConfig, values)
5560
if err != nil {
5661
return fmt.Errorf("set app config values: %w", err)

api/controllers/kubernetes/install/controller.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package install
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"sync"
78

@@ -164,13 +165,6 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
164165
opt(controller)
165166
}
166167

167-
if controller.configValues != nil {
168-
err := controller.store.AppConfigStore().SetConfigValues(controller.configValues)
169-
if err != nil {
170-
return nil, fmt.Errorf("set app config values: %w", err)
171-
}
172-
}
173-
174168
// If none is provided, use the default env settings from helm to create a RESTClientGetter
175169
if controller.restClientGetter == nil {
176170
controller.restClientGetter = helmcli.New().RESTClientGetter()
@@ -208,5 +202,19 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
208202
)
209203
}
210204

205+
if controller.configValues != nil {
206+
if controller.releaseData == nil || controller.releaseData.AppConfig == nil {
207+
return nil, errors.New("app config not found")
208+
}
209+
err := controller.appConfigManager.ValidateConfigValues(*controller.releaseData.AppConfig, controller.configValues)
210+
if err != nil {
211+
return nil, fmt.Errorf("validate app config values: %w", err)
212+
}
213+
err = controller.appConfigManager.SetConfigValues(*controller.releaseData.AppConfig, controller.configValues)
214+
if err != nil {
215+
return nil, fmt.Errorf("set app config values: %w", err)
216+
}
217+
}
218+
211219
return controller, nil
212220
}

api/controllers/linux/install/app.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ func (c *InstallController) SetAppConfigValues(ctx context.Context, values map[s
5151
}
5252
}()
5353

54+
err = c.appConfigManager.ValidateConfigValues(*c.releaseData.AppConfig, values)
55+
if err != nil {
56+
return fmt.Errorf("validate app config values: %w", err)
57+
}
58+
5459
err = c.appConfigManager.SetConfigValues(*c.releaseData.AppConfig, values)
5560
if err != nil {
5661
return fmt.Errorf("set app config values: %w", err)

api/controllers/linux/install/controller.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package install
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"sync"
78

@@ -201,13 +202,6 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
201202
opt(controller)
202203
}
203204

204-
if controller.configValues != nil {
205-
err := controller.store.AppConfigStore().SetConfigValues(controller.configValues)
206-
if err != nil {
207-
return nil, fmt.Errorf("set app config values: %w", err)
208-
}
209-
}
210-
211205
if controller.stateMachine == nil {
212206
controller.stateMachine = NewStateMachine(WithStateMachineLogger(controller.logger))
213207
}
@@ -262,6 +256,20 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController,
262256
)
263257
}
264258

259+
if controller.configValues != nil {
260+
if controller.releaseData == nil || controller.releaseData.AppConfig == nil {
261+
return nil, errors.New("app config not found")
262+
}
263+
err := controller.appConfigManager.ValidateConfigValues(*controller.releaseData.AppConfig, controller.configValues)
264+
if err != nil {
265+
return nil, fmt.Errorf("validate app config values: %w", err)
266+
}
267+
err = controller.appConfigManager.SetConfigValues(*controller.releaseData.AppConfig, controller.configValues)
268+
if err != nil {
269+
return nil, fmt.Errorf("set app config values: %w", err)
270+
}
271+
}
272+
265273
controller.registerReportingHandlers()
266274

267275
return controller, nil

api/internal/managers/app/config/config.go

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package config
22

33
import (
4+
"errors"
45
"fmt"
56

7+
"github.com/replicatedhq/embedded-cluster/api/types"
68
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
79
"github.com/replicatedhq/kotskinds/multitype"
810
"github.com/tiendc/go-deepcopy"
911
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1012
)
1113

14+
var (
15+
ErrConfigItemRequired = errors.New("item is required")
16+
ErrConfigValueNotFound = errors.New("value not found")
17+
)
18+
1219
func (m *appConfigManager) GetConfig(config kotsv1beta1.Config) (kotsv1beta1.Config, error) {
1320
return filterAppConfig(config)
1421
}
@@ -17,6 +24,42 @@ func (m *appConfigManager) GetConfigValues() (map[string]string, error) {
1724
return m.appConfigStore.GetConfigValues()
1825
}
1926

27+
func (m *appConfigManager) ValidateConfigValues(config kotsv1beta1.Config, configValues map[string]string) error {
28+
var ve *types.APIError
29+
30+
configItems := make(map[string]kotsv1beta1.ConfigItem)
31+
configChildItems := make(map[string]kotsv1beta1.ConfigChildItem)
32+
33+
// first check required items
34+
for _, group := range config.Spec.Groups {
35+
for _, item := range group.Items {
36+
configItems[item.Name] = item
37+
38+
configValue := getConfigValueFromItem(item, configValues)
39+
if isRequiredItem(item) && isUnsetItem(configValue) {
40+
ve = types.AppendFieldError(ve, item.Name, ErrConfigItemRequired)
41+
}
42+
43+
for _, childItem := range item.Items {
44+
configChildItems[childItem.Name] = childItem
45+
}
46+
}
47+
}
48+
49+
// then check if all the config values are present
50+
for name := range configValues {
51+
if _, ok := configItems[name]; ok {
52+
continue
53+
}
54+
if _, ok := configChildItems[name]; ok {
55+
continue
56+
}
57+
ve = types.AppendFieldError(ve, name, ErrConfigValueNotFound)
58+
}
59+
60+
return ve.ErrorOrNil()
61+
}
62+
2063
func (m *appConfigManager) SetConfigValues(config kotsv1beta1.Config, configValues map[string]string) error {
2164
filteredValues := make(map[string]string)
2265

@@ -42,14 +85,18 @@ func (m *appConfigManager) SetConfigValues(config kotsv1beta1.Config, configValu
4285
}
4386

4487
func (m *appConfigManager) GetKotsadmConfigValues(config kotsv1beta1.Config) (kotsv1beta1.ConfigValues, error) {
45-
filteredConfig, err := m.GetConfig(config)
88+
storedValues, err := m.GetConfigValues()
4689
if err != nil {
47-
return kotsv1beta1.ConfigValues{}, fmt.Errorf("get config: %w", err)
90+
return kotsv1beta1.ConfigValues{}, fmt.Errorf("get config values: %w", err)
4891
}
4992

50-
storedValues, err := m.GetConfigValues()
93+
return m.getKotsadmConfigValues(config, storedValues)
94+
}
95+
96+
func (m *appConfigManager) getKotsadmConfigValues(config kotsv1beta1.Config, configValues map[string]string) (kotsv1beta1.ConfigValues, error) {
97+
filteredConfig, err := m.GetConfig(config)
5198
if err != nil {
52-
return kotsv1beta1.ConfigValues{}, fmt.Errorf("get config values: %w", err)
99+
return kotsv1beta1.ConfigValues{}, fmt.Errorf("get config: %w", err)
53100
}
54101

55102
kotsadmConfigValues := kotsv1beta1.ConfigValues{
@@ -68,33 +115,43 @@ func (m *appConfigManager) GetKotsadmConfigValues(config kotsv1beta1.Config) (ko
68115
// add values from the filtered config
69116
for _, group := range filteredConfig.Spec.Groups {
70117
for _, item := range group.Items {
71-
configValue := kotsv1beta1.ConfigValue{
72-
Value: item.Value.String(),
73-
Default: item.Default.String(),
74-
}
75-
// override values from the config values store
76-
if value, ok := storedValues[item.Name]; ok {
77-
configValue.Value = value
78-
}
79-
kotsadmConfigValues.Spec.Values[item.Name] = configValue
118+
kotsadmConfigValues.Spec.Values[item.Name] = getConfigValueFromItem(item, configValues)
80119

81-
for _, subItem := range item.Items {
82-
subConfigValue := kotsv1beta1.ConfigValue{
83-
Value: subItem.Value.String(),
84-
Default: subItem.Default.String(),
85-
}
86-
// override values from the config values store
87-
if value, ok := storedValues[subItem.Name]; ok {
88-
subConfigValue.Value = value
89-
}
90-
kotsadmConfigValues.Spec.Values[subItem.Name] = subConfigValue
120+
for _, childItem := range item.Items {
121+
kotsadmConfigValues.Spec.Values[childItem.Name] = getConfigValueFromChildItem(childItem, configValues)
91122
}
92123
}
93124
}
94125

95126
return kotsadmConfigValues, nil
96127
}
97128

129+
func getConfigValueFromItem(item kotsv1beta1.ConfigItem, configValues map[string]string) kotsv1beta1.ConfigValue {
130+
configValue := kotsv1beta1.ConfigValue{
131+
Value: item.Value.String(),
132+
Default: item.Default.String(),
133+
}
134+
// override values from the config values store
135+
value, ok := configValues[item.Name]
136+
if ok {
137+
configValue.Value = value
138+
}
139+
return configValue
140+
}
141+
142+
func getConfigValueFromChildItem(item kotsv1beta1.ConfigChildItem, configValues map[string]string) kotsv1beta1.ConfigValue {
143+
configValue := kotsv1beta1.ConfigValue{
144+
Value: item.Value.String(),
145+
Default: item.Default.String(),
146+
}
147+
// override values from the config values store
148+
value, ok := configValues[item.Name]
149+
if ok {
150+
configValue.Value = value
151+
}
152+
return configValue
153+
}
154+
98155
// filterAppConfig filters out disabled groups and items based on their 'when' condition
99156
func filterAppConfig(config kotsv1beta1.Config) (kotsv1beta1.Config, error) {
100157
// deepcopy the config to avoid mutating the original config
@@ -129,3 +186,19 @@ func filterAppConfig(config kotsv1beta1.Config) (kotsv1beta1.Config, error) {
129186
func isItemEnabled(when multitype.QuotedBool) bool {
130187
return when != "false"
131188
}
189+
190+
func isRequiredItem(item kotsv1beta1.ConfigItem) bool {
191+
if !item.Required {
192+
return false
193+
}
194+
// TODO: should an item really not be required if it's hidden?
195+
if item.Hidden || item.When == "false" {
196+
return false
197+
}
198+
return true
199+
}
200+
201+
func isUnsetItem(configValue kotsv1beta1.ConfigValue) bool {
202+
// TODO: repeatable items
203+
return configValue.Value == "" && configValue.Default == ""
204+
}

0 commit comments

Comments
 (0)