Skip to content
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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

Conversation

phm07
Copy link
Contributor

@phm07 phm07 commented Apr 19, 2024

This PR implements the new configuration system as described in #762.

Closes #762
In preparation for #434

Copy link

codecov bot commented Apr 19, 2024

Codecov Report

Attention: Patch coverage is 46.35135% with 397 lines in your changes are missing coverage. Please review.

Project coverage is 59.05%. Comparing base (f2fb321) to head (1838ef6).

Files Patch % Lines
internal/state/config/config.go 0.00% 122 Missing ⚠️
internal/state/config/options.go 11.23% 79 Missing ⚠️
internal/cmd/config/config.go 0.00% 36 Missing ⚠️
internal/state/config/context.go 0.00% 29 Missing ⚠️
internal/cmd/config/set.go 65.82% 21 Missing and 6 partials ⚠️
internal/cmd/config/remove.go 64.06% 15 Missing and 8 partials ⚠️
internal/state/config/preferences.go 69.01% 18 Missing and 4 partials ⚠️
internal/cmd/config/add.go 68.25% 15 Missing and 5 partials ⚠️
internal/state/state.go 0.00% 11 Missing ⚠️
internal/cmd/config/unset.go 81.81% 6 Missing and 2 partials ⚠️
... and 5 more
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.
📢 Have feedback on the report? Share it here.

@phm07 phm07 force-pushed the configuration branch 3 times, most recently from f895daf to 8b2856d Compare May 2, 2024 12:33
internal/cmd/cmpl/suggestions.go Show resolved Hide resolved
internal/cmd/firewall/describe.go Outdated Show resolved Hide resolved
Copy link
Member

@jooola jooola left a 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?

Comment on lines +22 to +45
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'.
`,
Copy link
Member

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?

Copy link
Contributor Author

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.

internal/cmd/config/helptext/generate.go Show resolved Hide resolved
internal/cmd/config/list.go Outdated Show resolved Hide resolved
Comment on lines -66 to -67
if err := os.MkdirAll(filepath.Dir(cfg.path), 0777); err != nil {
return err
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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
Copy link
Member

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)

Copy link
Contributor Author

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.

@phm07
Copy link
Contributor Author

phm07 commented May 10, 2024

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?

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

@phm07 phm07 force-pushed the configuration branch 3 times, most recently from dd2eda9 to 69096d4 Compare May 23, 2024 12:29
@phm07 phm07 changed the title feat: add global/per-context configuration preferences, add config command feat: new configuration system, config subcommand May 23, 2024
@phm07 phm07 self-assigned this May 23, 2024
@phm07 phm07 added the feature label May 23, 2024
@phm07 phm07 marked this pull request as ready for review May 23, 2024 12:31
@phm07 phm07 requested a review from a team as a code owner May 23, 2024 12:31
Copy link
Member

@apricote apricote left a 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 :)

internal/cmd/firewall/add_rule.go Outdated Show resolved Hide resolved
internal/cmd/firewall/delete_rule.go Outdated Show resolved Hide resolved
internal/cmd/firewall/describe.go Outdated Show resolved Hide resolved
internal/cmd/firewall/describe_test.go Outdated Show resolved Hide resolved
Comment on lines +239 to +291
// 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
}
Copy link
Member

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?

Copy link
Contributor Author

@phm07 phm07 May 23, 2024

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)
Copy link
Member

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.

Copy link
Contributor Author

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

Comment on lines 49 to 52
var opts []hcloud.ClientOption

token := config.OptionToken.Get(c.config)
opts = append(opts, hcloud.WithToken(token))
Copy link
Member

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)

Suggested change
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)))

Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Design Doc] feat: new configuration system + config subcommand
3 participants