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:] Added Config Command to Kitops CLI #523

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"path/filepath"

"kitops/pkg/cmd/config"
"kitops/pkg/cmd/dev"
"kitops/pkg/cmd/info"
"kitops/pkg/cmd/inspect"
Expand Down Expand Up @@ -55,6 +56,29 @@ func RunCommand() *cobra.Command {
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
output.SetOut(cmd.OutOrStdout())
output.SetErr(cmd.ErrOrStderr())

// Load config from the file (or default if it doesn't exist)
configHome, err := getConfigHome(opts)
if err != nil {
output.Errorf("Failed to read base config directory")
output.Infof("Use the --config flag or set the $%s environment variable to provide a default", constants.KitopsHomeEnvVar)
output.Debugf("Error: %s", err)
return errors.New("exit")
}

configPath := constants.ConfigFilePath(configHome)
cfg, err := config.LoadConfig(configPath)
if err != nil {
return output.Fatalf("Failed to load config: %s", err)
}
// Override the config values with flag values if the flags were provided
if opts.loglevel == "" && cfg.LogLevel != "" {
opts.loglevel = cfg.LogLevel
}
if opts.progressBars == "" && cfg.Progress != "" {
opts.progressBars = cfg.Progress
}

if err := output.SetLogLevelFromString(opts.loglevel); err != nil {
return output.Fatalln(err)
}
Expand All @@ -75,13 +99,6 @@ func RunCommand() *cobra.Command {
output.SetProgressBars("none")
}

configHome, err := getConfigHome(opts)
if err != nil {
output.Errorf("Failed to read base config directory")
output.Infof("Use the --config flag or set the $%s environment variable to provide a default", constants.KitopsHomeEnvVar)
output.Debugf("Error: %s", err)
return errors.New("exit")
}
ctx := context.WithValue(cmd.Context(), constants.ConfigKey{}, configHome)
cmd.SetContext(ctx)

Expand Down Expand Up @@ -129,6 +146,7 @@ func addSubcommands(rootCmd *cobra.Command) {
rootCmd.AddCommand(pull.PullCommand())
rootCmd.AddCommand(tag.TagCommand())
rootCmd.AddCommand(list.ListCommand())
rootCmd.AddCommand(config.ConfigCommand())
rootCmd.AddCommand(inspect.InspectCommand())
rootCmd.AddCommand(info.InfoCommand())
rootCmd.AddCommand(remove.RemoveCommand())
Expand All @@ -148,6 +166,7 @@ func Execute() {
}

func getConfigHome(opts *rootOptions) (string, error) {
// First check if the config path is provided via flags
if opts.configHome != "" {
output.Debugf("Using config directory from flag: %s", opts.configHome)
amisevsk marked this conversation as resolved.
Show resolved Hide resolved
absHome, err := filepath.Abs(opts.configHome)
Expand All @@ -157,6 +176,7 @@ func getConfigHome(opts *rootOptions) (string, error) {
return absHome, nil
}

// Then check if it's provided via environment variable
envHome := os.Getenv(constants.KitopsHomeEnvVar)
if envHome != "" {
output.Debugf("Using config directory from environment variable: %s", envHome)
Expand All @@ -167,6 +187,7 @@ func getConfigHome(opts *rootOptions) (string, error) {
return absHome, nil
}

// Finally, fall back to the default path
defaultHome, err := constants.DefaultConfigPath()
if err != nil {
return "", err
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/text v0.19.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
167 changes: 167 additions & 0 deletions pkg/cmd/config/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright 2024 The KitOps Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
package config

import (
"context"
"fmt"
"kitops/pkg/lib/constants"
"kitops/pkg/output"

"github.com/spf13/cobra"
)

const (
shortDesc = `Manage configuration for KitOps CLI`
longDesc = `Allows setting, getting, listing, and resetting configuration options for the KitOps CLI.

This command provides functionality to manage configuration settings such as
storage paths, credentials file location, CLI version, and update notification preferences.
The configuration values can be set using specific keys, retrieved for inspection, listed,
or reset to default values.`

example = `# Set a configuration option
kit config set storageSubpath /path/to/storage

# Get a configuration option
kit config get storageSubpath

# List all configuration options
kit config list

# Reset configuration to default values
kit config reset`
)

// Root config command.
func ConfigCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: shortDesc,
Long: longDesc,
Example: example,
}

// Add subcommands to the root config command.
cmd.AddCommand(setCmd())
cmd.AddCommand(getCmd())
cmd.AddCommand(listCmd())
cmd.AddCommand(resetCmd())

return cmd
}

// Subcommand for 'set'
func setCmd() *cobra.Command {
opts := &configOptions{}
cmd := &cobra.Command{
Use: "set [key] [value]",
Short: "Set a configuration value",
Args: cobra.ExactArgs(2), // Ensure exactly 2 arguments: key and value.
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
opts.key, opts.value = args[0], args[1]
if err := opts.complete(ctx); err != nil {
return fmt.Errorf("failed to complete options: %w", err)
}
if err := setConfig(ctx, opts); err != nil {
return fmt.Errorf("failed to set config: %w", err)
SkySingh04 marked this conversation as resolved.
Show resolved Hide resolved
}
output.Infof("Configuration key '%s' set to '%s'", opts.key, opts.value)
return nil
},
}

return cmd
}

// Subcommand for 'get'
func getCmd() *cobra.Command {
opts := &configOptions{}
cmd := &cobra.Command{
Use: "get [key]",
Short: "Get a configuration value",
Args: cobra.ExactArgs(1), // Ensure exactly 1 argument: key.
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
opts.key = args[0]
if err := opts.complete(ctx); err != nil {
return fmt.Errorf("failed to complete options: %w", err)
}
value, err := getConfig(ctx, opts)
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}
output.Infof("Configuration key '%s': '%s'", opts.key, value)
return nil
},
}

return cmd
}

// Subcommand for 'list'
func listCmd() *cobra.Command {
opts := &configOptions{}
cmd := &cobra.Command{
Use: "list",
Short: "List all configuration values",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if err := opts.complete(ctx); err != nil {
return fmt.Errorf("failed to complete options: %w", err)
}
if err := listConfig(ctx, opts); err != nil {
return fmt.Errorf("failed to list configs: %w", err)
}
return nil
},
}

return cmd
}

// Subcommand for 'reset'
func resetCmd() *cobra.Command {
opts := &configOptions{}
cmd := &cobra.Command{
Use: "reset",
Short: "Reset configuration to default values",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if err := opts.complete(ctx); err != nil {
return fmt.Errorf("failed to complete options: %w", err)
}
if err := resetConfig(ctx, opts); err != nil {
return fmt.Errorf("failed to reset config: %w", err)
}
output.Infof("Configuration reset to default values")
return nil
},
}

return cmd
}

// complete populates configOptions fields.
func (opts *configOptions) complete(ctx context.Context) error {
configHome, ok := ctx.Value(constants.ConfigKey{}).(string)
if !ok {
return fmt.Errorf("default config path not set on command context")
}
opts.configHome = configHome
return nil
}
Loading