New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: new configuration system, config subcommand #736
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #736 +/- ##
==========================================
- Coverage 60.14% 59.05% -1.10%
==========================================
Files 191 204 +13
Lines 6808 7519 +711
==========================================
+ Hits 4095 4440 +345
- Misses 2099 2436 +337
- Partials 614 643 +29 ☔ View full report in Codecov by Sentry. |
f895daf
to
8b2856d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I like how it is structured.
I think a few refactoring passes are needed before this can be merged.
I also noticed that the viper library already ships our previous toml library, so might be shipping both. Are we ok with the binary size?
Long: `This command allows you to manage options for the Hetzner Cloud CLI. Options can be set inside the | ||
configuration file, through environment variables or with flags. | ||
|
||
The hierarchy for configuration sources is as follows (from highest to lowest priority): | ||
1. Flags | ||
2. Environment variables | ||
3. Configuration file (context) | ||
4. Configuration file (global) | ||
5. Default values | ||
|
||
Most options are 'preferences' - these options can be set globally and can additionally be overridden | ||
for each context. Below is a list of all non-preference options: | ||
|
||
` + nonPreferenceOptions + | ||
` | ||
Since the above options are not preferences, they cannot be modified with 'hcloud config set' or | ||
'hcloud config unset'. However, you are able to retrieve them using 'hcloud config get' and 'hcloud config list'. | ||
Following options are preferences and can be used with these commands: | ||
|
||
` + preferenceOptions + | ||
` | ||
Options will be persisted in the configuration file. To find out where your configuration file is located | ||
on disk, run 'hcloud config get config'. | ||
`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not have everything in external files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how that is a benefit. We would have to join the tables in somehow anyway.
if err := os.MkdirAll(filepath.Dir(cfg.path), 0777); err != nil { | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy not to see this 0777 in the new code, but shouldn't we keep the config dir creation behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we should. Seems like I forgot to copy it over somewhere. What perms do we want to give that folder? 600
seems a bit restrictive since other tools might also want to access ~/.config/hcloud
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in 6eada44
"github.com/hetznercloud/hcloud-go/v2/hcloud" | ||
) | ||
|
||
type OptionFlag int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we rename this to OptionFeature
? I have and will be confused by this naming more than once.
e.g. !o.HasFlag(OptionFlagEnv)
=> !o.HasFeature(OptionFeatureEnv)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OptionFlag
is a bit flag, that's why it has that name. It's very common to call these bit flags. I would find calling it Feature
way more confusing.
I'll put the toml library change into another PR and then we can compare binary sizes. I don't think it's a huge difference. Edit: See #758 |
dd2eda9
to
69096d4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review (its a huge PR ;) ), will continue tomorrow :)
// SliceDiff returns the difference between the two passed slices. The returned slice contains all elements that are present in a but not in b. | ||
// Note that it does not preserve order. | ||
func SliceDiff[S ~[]E, E cmp.Ordered](a, b []E) []E { | ||
m := make(map[E]struct{}) | ||
for _, x := range a { | ||
m[x] = struct{}{} | ||
} | ||
for _, x := range b { | ||
delete(m, x) | ||
} | ||
var diff S | ||
for x := range m { | ||
diff = append(diff, x) | ||
} | ||
slices.Sort(diff) | ||
return diff | ||
} | ||
|
||
func AnyToAnySlice(a any) []any { | ||
val := reflect.ValueOf(a) | ||
if val.Kind() != reflect.Slice { | ||
return nil | ||
} | ||
s := make([]any, val.Len()) | ||
for i := 0; i < val.Len(); i++ { | ||
s[i] = val.Index(i).Interface() | ||
} | ||
return s | ||
} | ||
|
||
func AnyToStringSlice(a any) []string { | ||
var s []string | ||
for _, v := range AnyToAnySlice(a) { | ||
s = append(s, fmt.Sprint(v)) | ||
} | ||
return s | ||
} | ||
|
||
func ToStringSlice(a []any) []string { | ||
var s []string | ||
for _, v := range a { | ||
s = append(s, fmt.Sprint(v)) | ||
} | ||
return s | ||
} | ||
|
||
func ToAnySlice[T any](a []T) []any { | ||
var s []any | ||
for _, v := range a { | ||
s = append(s, any(v)) | ||
} | ||
return s | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add some unit tests for these util functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 8e590b1
opts = append(opts, hcloud.WithDebugWriter(os.Stderr)) | ||
} else { | ||
writer, _ := os.Create(c.debugFilePath) | ||
writer, _ := os.Create(filePath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR: I recently noticed that we always truncate the file (os.Create
). Pretty annoying if you use hcloud
in a script and want all the logs in some file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened #763 so that we can tackle that after this PR
internal/state/state.go
Outdated
var opts []hcloud.ClientOption | ||
|
||
token := config.OptionToken.Get(c.config) | ||
opts = append(opts, hcloud.WithToken(token)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For slightly simpler code, you could init opts
with the options that are always set (token, application)
var opts []hcloud.ClientOption | |
token := config.OptionToken.Get(c.config) | |
opts = append(opts, hcloud.WithToken(token)) | |
opts := []hcloud.ClientOption{ | |
hcloud.WithToken(config.OptionToken.Get(c.config)), | |
hcloud.WithApplication("hcloud-cli", version.Version), | |
} | |
(Also need to remove opts = append(opts, hcloud.WithApplication("hcloud-cli", version.Version))
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, idk why it was done like that. Done in 1838ef6
This PR implements the new configuration system as described in #762.
Closes #762
In preparation for #434