1 package preference
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "os/user"
8 "path/filepath"
9 "strconv"
10 "strings"
11 "time"
12
13 "github.com/redhat-developer/odo/pkg/api"
14 envcontext "github.com/redhat-developer/odo/pkg/config/context"
15 "github.com/redhat-developer/odo/pkg/log"
16 "github.com/redhat-developer/odo/pkg/odo/cli/ui"
17 "github.com/redhat-developer/odo/pkg/util"
18
19 dfutil "github.com/devfile/library/v2/pkg/util"
20
21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22 "k8s.io/klog"
23 kpointer "k8s.io/utils/pointer"
24 )
25
26
27
28 type odoSettings struct {
29
30 UpdateNotification *bool `yaml:"UpdateNotification,omitempty"`
31
32
33 Timeout *time.Duration `yaml:"Timeout,omitempty"`
34
35
36 PushTimeout *time.Duration `yaml:"PushTimeout,omitempty"`
37
38
39 RegistryList *[]Registry `yaml:"RegistryList,omitempty"`
40
41
42 RegistryCacheTime *time.Duration `yaml:"RegistryCacheTime,omitempty"`
43
44
45 Ephemeral *bool `yaml:"Ephemeral,omitempty"`
46
47
48 ConsentTelemetry *bool `yaml:"ConsentTelemetry,omitempty"`
49
50
51
52 ImageRegistry *string `yaml:"ImageRegistry,omitempty"`
53 }
54
55
56 type Registry struct {
57 Name string `yaml:"Name,omitempty" json:"name"`
58 URL string `yaml:"URL,omitempty" json:"url"`
59 Secure bool `json:"secure"`
60 }
61
62
63 type Preference struct {
64 metav1.TypeMeta `yaml:",inline"`
65
66
67 OdoSettings odoSettings `yaml:"OdoSettings,omitempty"`
68 }
69
70
71
72 type preferenceInfo struct {
73 Filename string `yaml:"FileName,omitempty"`
74 Preference `yaml:",omitempty"`
75 }
76
77 var _ Client = (*preferenceInfo)(nil)
78
79 func getPreferenceFile(ctx context.Context) (string, error) {
80 envConfig := envcontext.GetEnvConfig(ctx)
81 if envConfig.Globalodoconfig != nil {
82 return *envConfig.Globalodoconfig, nil
83 }
84
85 if len(customHomeDir) != 0 {
86 return filepath.Join(customHomeDir, ".odo", configFileName), nil
87 }
88
89 currentUser, err := user.Current()
90 if err != nil {
91 return "", err
92 }
93 return filepath.Join(currentUser.HomeDir, ".odo", configFileName), nil
94 }
95
96 func NewClient(ctx context.Context) (Client, error) {
97 return newPreferenceInfo(ctx)
98 }
99
100
101 func newPreference() Preference {
102 return Preference{
103 TypeMeta: metav1.TypeMeta{
104 Kind: preferenceKind,
105 APIVersion: preferenceAPIVersion,
106 },
107 }
108 }
109
110
111
112 func newPreferenceInfo(ctx context.Context) (*preferenceInfo, error) {
113 preferenceFile, err := getPreferenceFile(ctx)
114 klog.V(4).Infof("The path for preference file is %+v", preferenceFile)
115 if err != nil {
116 return nil, err
117 }
118
119 c := preferenceInfo{
120 Preference: newPreference(),
121 Filename: preferenceFile,
122 }
123
124
125 defaultRegistryList := []Registry{
126 {
127 Name: DefaultDevfileRegistryName,
128 URL: DefaultDevfileRegistryURL,
129 Secure: false,
130 },
131 }
132
133
134 if _, err = os.Stat(preferenceFile); os.IsNotExist(err) {
135 c.OdoSettings.RegistryList = &defaultRegistryList
136 return &c, nil
137 }
138
139 err = util.GetFromFile(&c.Preference, c.Filename)
140 if err != nil {
141 return nil, err
142 }
143
144
145
146 var requiresChange []string
147 if c.OdoSettings.Timeout != nil && *c.OdoSettings.Timeout < minimumDurationValue {
148 requiresChange = append(requiresChange, TimeoutSetting)
149 }
150 if c.OdoSettings.PushTimeout != nil && *c.OdoSettings.PushTimeout < minimumDurationValue {
151 requiresChange = append(requiresChange, PushTimeoutSetting)
152 }
153 if c.OdoSettings.RegistryCacheTime != nil && *c.OdoSettings.RegistryCacheTime < minimumDurationValue {
154 requiresChange = append(requiresChange, RegistryCacheTimeSetting)
155 }
156 if len(requiresChange) != 0 {
157 log.Warningf("Please change the preference value for %s, the value does not comply with the minimum value of %s; e.g. of acceptable formats: 4s, 5m, 1h", strings.Join(requiresChange, ", "), minimumDurationValue)
158 }
159
160
161 if c.OdoSettings.RegistryList == nil {
162 c.OdoSettings.RegistryList = &defaultRegistryList
163 }
164
165
166 if c.OdoSettings.RegistryList != nil {
167 for index, registry := range *c.OdoSettings.RegistryList {
168 if registry.Name == DefaultDevfileRegistryName && registry.URL == OldDefaultDevfileRegistryURL {
169 registryList := *c.OdoSettings.RegistryList
170 registryList[index].URL = DefaultDevfileRegistryURL
171 break
172 }
173 }
174 }
175
176 return &c, nil
177 }
178
179
180 func (c *preferenceInfo) RegistryHandler(operation string, registryName string, registryURL string, forceFlag bool, isSecure bool) error {
181 var registryList []Registry
182 var err error
183 var registryExist bool
184
185
186 if c.OdoSettings.RegistryList == nil {
187 registryList, err = handleWithoutRegistryExist(registryList, operation, registryName, registryURL, isSecure)
188 if err != nil {
189 return err
190 }
191 } else {
192
193 registryList = *c.OdoSettings.RegistryList
194 for index, registry := range registryList {
195 if registry.Name == registryName {
196 registryExist = true
197 registryList, err = handleWithRegistryExist(index, registryList, operation, registryName, forceFlag)
198 if err != nil {
199 return err
200 }
201 }
202 }
203
204
205 if !registryExist {
206 registryList, err = handleWithoutRegistryExist(registryList, operation, registryName, registryURL, isSecure)
207 if err != nil {
208 return err
209 }
210 }
211 }
212
213 c.OdoSettings.RegistryList = ®istryList
214 err = util.WriteToYAMLFile(&c.Preference, c.Filename)
215 if err != nil {
216 return fmt.Errorf("unable to write the configuration of %q operation to preference file", operation)
217 }
218
219 return nil
220 }
221
222
223 func handleWithoutRegistryExist(registryList []Registry, operation string, registryName string, registryURL string, isSecure bool) ([]Registry, error) {
224 switch operation {
225
226 case "add":
227 registry := Registry{
228 Name: registryName,
229 URL: registryURL,
230 Secure: isSecure,
231 }
232 registryList = append(registryList, registry)
233
234 case "remove":
235 return nil, fmt.Errorf("failed to %v registry: registry %q doesn't exist or it is not managed by odo", operation, registryName)
236 }
237
238 return registryList, nil
239 }
240
241
242 func handleWithRegistryExist(index int, registryList []Registry, operation string, registryName string, forceFlag bool) ([]Registry, error) {
243 switch operation {
244
245 case "add":
246 return nil, fmt.Errorf("failed to %s registry: registry %q already exists", operation, registryName)
247
248 case "remove":
249 if !forceFlag {
250 proceed, err := ui.Proceed(fmt.Sprintf("Are you sure you want to %s registry %q", operation, registryName))
251 if err != nil {
252 return nil, err
253 }
254 if !proceed {
255 log.Info("Aborted by the user")
256 return registryList, nil
257 }
258 }
259
260 copy(registryList[index:], registryList[index+1:])
261 registryList[len(registryList)-1] = Registry{}
262 registryList = registryList[:len(registryList)-1]
263 log.Info("Successfully removed registry")
264 }
265
266 return registryList, nil
267 }
268
269
270
271 func (c *preferenceInfo) SetConfiguration(parameter string, value string) error {
272 if p, ok := asSupportedParameter(parameter); ok {
273
274 switch p {
275
276 case "timeout":
277 typedval, err := parseDuration(value, parameter)
278 if err != nil {
279 return err
280 }
281 c.OdoSettings.Timeout = &typedval
282
283 case "pushtimeout":
284 typedval, err := parseDuration(value, parameter)
285 if err != nil {
286 return err
287 }
288 c.OdoSettings.PushTimeout = &typedval
289
290 case "registrycachetime":
291 typedval, err := parseDuration(value, parameter)
292 if err != nil {
293 return err
294 }
295 c.OdoSettings.RegistryCacheTime = &typedval
296
297 case "updatenotification":
298 val, err := strconv.ParseBool(strings.ToLower(value))
299 if err != nil {
300 return fmt.Errorf("unable to set %q to %q, value must be a boolean", parameter, value)
301 }
302 c.OdoSettings.UpdateNotification = &val
303
304 case "ephemeral":
305 val, err := strconv.ParseBool(strings.ToLower(value))
306 if err != nil {
307 return fmt.Errorf("unable to set %q to %q, value must be a boolean", parameter, value)
308 }
309 c.OdoSettings.Ephemeral = &val
310
311 case "consenttelemetry":
312 val, err := strconv.ParseBool(strings.ToLower(value))
313 if err != nil {
314 return fmt.Errorf("unable to set %q to %q, value must be a boolean", parameter, value)
315 }
316 c.OdoSettings.ConsentTelemetry = &val
317
318 case "imageregistry":
319 c.OdoSettings.ImageRegistry = &value
320 }
321 } else {
322 return fmt.Errorf("unknown parameter : %q is not a parameter in odo preference, run `odo preference -h` to see list of available parameters", parameter)
323 }
324
325 err := util.WriteToYAMLFile(&c.Preference, c.Filename)
326 if err != nil {
327 return fmt.Errorf("unable to set %q, something is wrong with odo, kindly raise an issue at https://github.com/redhat-developer/odo/issues/new?template=Bug.md", parameter)
328 }
329 return nil
330 }
331
332
333
334
335 func parseDuration(value, parameter string) (time.Duration, error) {
336 typedval, err := time.ParseDuration(value)
337 if err != nil {
338 return typedval, fmt.Errorf("unable to set %q to %q; cause: %w\n%s", parameter, value, err, NewMinimumDurationValueError().Error())
339 }
340 if typedval < minimumDurationValue {
341 return typedval, fmt.Errorf("unable to set %q to %q; cause: %w", parameter, value, NewMinimumDurationValueError())
342 }
343 return typedval, nil
344 }
345
346
347 func (c *preferenceInfo) DeleteConfiguration(parameter string) error {
348 if p, ok := asSupportedParameter(parameter); ok {
349
350
351 if err := util.DeleteConfiguration(&c.OdoSettings, p); err != nil {
352 return err
353 }
354 } else {
355 return fmt.Errorf("unknown parameter :%q is not a parameter in the odo preference", parameter)
356 }
357
358 err := util.WriteToYAMLFile(&c.Preference, c.Filename)
359 if err != nil {
360 return fmt.Errorf("unable to set %q, something is wrong with odo, kindly raise an issue at https://github.com/redhat-developer/odo/issues/new?template=Bug.md", parameter)
361 }
362 return nil
363 }
364
365
366 func (c *preferenceInfo) IsSet(parameter string) bool {
367 return util.IsSet(c.OdoSettings, parameter)
368 }
369
370
371
372 func (c *preferenceInfo) GetTimeout() time.Duration {
373
374 return kpointer.DurationDeref(c.OdoSettings.Timeout, DefaultTimeout)
375 }
376
377
378 func (c *preferenceInfo) GetPushTimeout() time.Duration {
379
380 return kpointer.DurationDeref(c.OdoSettings.PushTimeout, DefaultPushTimeout)
381 }
382
383
384 func (c *preferenceInfo) GetRegistryCacheTime() time.Duration {
385 return kpointer.DurationDeref(c.OdoSettings.RegistryCacheTime, DefaultRegistryCacheTime)
386 }
387
388
389
390 func (c *preferenceInfo) GetImageRegistry() string {
391 return kpointer.StringDeref(c.OdoSettings.ImageRegistry, "")
392 }
393
394
395
396 func (c *preferenceInfo) GetUpdateNotification() bool {
397 return kpointer.BoolDeref(c.OdoSettings.UpdateNotification, true)
398 }
399
400
401
402 func (c *preferenceInfo) GetEphemeralSourceVolume() bool {
403 return kpointer.BoolDeref(c.OdoSettings.Ephemeral, DefaultEphemeralSetting)
404 }
405
406
407
408
409 func (c *preferenceInfo) GetConsentTelemetry() bool {
410 return kpointer.BoolDeref(c.OdoSettings.ConsentTelemetry, DefaultConsentTelemetrySetting)
411 }
412
413
414
415
416 func (c *preferenceInfo) GetEphemeral() bool {
417 return kpointer.BoolDeref(c.OdoSettings.Ephemeral, DefaultEphemeralSetting)
418 }
419
420 func (c *preferenceInfo) UpdateNotification() *bool {
421 return c.OdoSettings.UpdateNotification
422 }
423
424 func (c *preferenceInfo) Ephemeral() *bool {
425 return c.OdoSettings.Ephemeral
426 }
427
428 func (c *preferenceInfo) Timeout() *time.Duration {
429 return c.OdoSettings.Timeout
430 }
431
432 func (c *preferenceInfo) PushTimeout() *time.Duration {
433 return c.OdoSettings.PushTimeout
434 }
435
436 func (c *preferenceInfo) RegistryCacheTime() *time.Duration {
437 return c.OdoSettings.RegistryCacheTime
438 }
439
440 func (c *preferenceInfo) EphemeralSourceVolume() *bool {
441 return c.OdoSettings.Ephemeral
442 }
443
444 func (c *preferenceInfo) ConsentTelemetry() *bool {
445 return c.OdoSettings.ConsentTelemetry
446 }
447
448
449
450
451
452
453 func (c *preferenceInfo) RegistryList() []api.Registry {
454 if c.OdoSettings.RegistryList == nil {
455 return nil
456 }
457 regList := make([]api.Registry, 0, len(*c.OdoSettings.RegistryList))
458 for _, registry := range *c.OdoSettings.RegistryList {
459 regList = append(regList, api.Registry{
460 Name: registry.Name,
461 URL: registry.URL,
462 Secure: registry.Secure,
463 })
464 }
465 i := 0
466 j := len(regList) - 1
467 for i < j {
468 regList[i], regList[j] = regList[j], regList[i]
469 i++
470 j--
471 }
472 return regList
473 }
474
475 func (c *preferenceInfo) RegistryNameExists(name string) bool {
476 for _, registry := range *c.OdoSettings.RegistryList {
477 if registry.Name == name {
478 return true
479 }
480 }
481 return false
482 }
483
484
485 func FormatSupportedParameters() (result string) {
486 for _, v := range GetSupportedParameters() {
487 result = result + " " + v + " - " + supportedParameterDescriptions[v] + "\n"
488 }
489 return "\nAvailable Global Parameters:\n" + result
490 }
491
492
493 func asSupportedParameter(param string) (string, bool) {
494 lower := strings.ToLower(param)
495 return lower, lowerCaseParameters[lower]
496 }
497
498
499 func GetSupportedParameters() []string {
500 return dfutil.GetSortedKeys(supportedParameterDescriptions)
501 }
502
View as plain text