From db4e7959ad7abcdb781bd52c576ac9301ba175b7 Mon Sep 17 00:00:00 2001 From: Youngjin Jo Date: Wed, 13 Nov 2024 13:51:18 +0900 Subject: [PATCH] feat: add init subcommand Signed-off-by: Youngjin Jo --- cmd/config.go | 6 +- cmd/init.go | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++ config.yaml | 0 3 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 cmd/init.go create mode 100644 config.yaml diff --git a/cmd/config.go b/cmd/config.go index fc5c74f..2cb930f 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -27,8 +27,8 @@ var configCmd = &cobra.Command{ switch environments, and display the current configuration.`, } -// initCmd initializes a new environment configuration -var initCmd = &cobra.Command{ +// configInitCmd initializes a new environment configuration +var configInitCmd = &cobra.Command{ Use: "init", Short: "Initialize a new environment configuration", Run: func(cmd *cobra.Command, args []string) { @@ -355,7 +355,7 @@ func init() { rootCmd.AddCommand(configCmd) // Adding subcommands to configCmd - configCmd.AddCommand(initCmd) + configCmd.AddCommand(configInitCmd) configCmd.AddCommand(envCmd) configCmd.AddCommand(showCmd) diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000..a1a0720 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,167 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cmd + +import ( + "fmt" + "log" + "net/url" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/pterm/pterm" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v2" +) + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init", + Short: "Initialize a new environment configuration", + Long: `Initialize a new environment configuration for cfctl by specifying a URL.`, + Run: func(cmd *cobra.Command, args []string) { + // Retrieve flags + environment, _ := cmd.Flags().GetString("environment") + urlStr, _ := cmd.Flags().GetString("url") + + // Ensure URL has a scheme; default to "https" if missing + if !strings.HasPrefix(urlStr, "http://") && !strings.HasPrefix(urlStr, "https://") { + urlStr = "https://" + urlStr + } + + // Parse environment name from URL + envName, err := parseEnvNameFromURL(urlStr) + if err != nil { + pterm.Error.WithShowLineNumber(false).Println("Invalid URL format:", err) + return + } + + // If environment name is provided, override the parsed name + if environment != "" { + envName = environment + } + + // Ensure environments directory exists + configPath := filepath.Join(getInitConfigDir(), "config.yaml") + + // Load existing config if it exists + viper.SetConfigFile(configPath) + _ = viper.ReadInConfig() + + // Add or update the environment entry in viper + viper.Set(fmt.Sprintf("environments.%s.url", envName), urlStr) + + // Set the default environment to the new envName + viper.Set("environment", envName) + + // Serialize config data with 2-space indentation + configData := viper.AllSettings() + yamlData, err := yaml.Marshal(configData) + if err != nil { + pterm.Error.WithShowLineNumber(false).Println("Failed to encode YAML data:", err) + return + } + + // Write the serialized YAML to file with 2-space indentation + file, err := os.Create(configPath) + if err != nil { + pterm.Error.WithShowLineNumber(false).Println("Failed to write to config.yaml:", err) + return + } + defer file.Close() + + if _, err := file.Write(yamlData); err != nil { + pterm.Error.WithShowLineNumber(false).Println("Failed to write YAML data to file:", err) + return + } + + pterm.Success.WithShowLineNumber(false).Printfln("Environment '%s' successfully initialized and set as the current environment in config.yaml", envName) + + // After successfully writing to config.yaml, create the environment-specific YAML file + envFilePath := filepath.Join(getInitConfigDir(), "environments", fmt.Sprintf("%s.yaml", envName)) + + // Ensure the environments directory exists + environmentsDir := filepath.Dir(envFilePath) + if _, err := os.Stat(environmentsDir); os.IsNotExist(err) { + os.MkdirAll(environmentsDir, os.ModePerm) + } + + // Create a blank environment-specific file if it doesn't exist + if _, err := os.Stat(envFilePath); os.IsNotExist(err) { + file, err := os.Create(envFilePath) + if err != nil { + pterm.Error.WithShowLineNumber(false).Println("Failed to create environment file:", err) + return + } + defer file.Close() + pterm.Success.WithShowLineNumber(false).Printfln("Created environment-specific file: %s", envFilePath) + } else { + pterm.Info.WithShowLineNumber(false).Printfln("Environment file already exists: %s", envFilePath) + } + }, +} + +// parseEnvNameFromURL parses environment name from the given URL and validates based on URL structure +func parseEnvNameFromURL(urlStr string) (string, error) { + parsedURL, err := url.Parse(urlStr) + if err != nil { + return "", err + } + hostname := parsedURL.Hostname() + + // Check for `prd` environment pattern + if strings.HasSuffix(hostname, "spaceone.megazone.io") { + re := regexp.MustCompile(`^(.*?)\.spaceone`) + matches := re.FindStringSubmatch(hostname) + if len(matches) == 2 { + return fmt.Sprintf("prd-%s", matches[1]), nil + } + } + + // Check for `dev` environment pattern + if strings.HasSuffix(hostname, "console.dev.spaceone.dev") { + re := regexp.MustCompile(`(.*)\.console\.dev\.spaceone\.dev`) + matches := re.FindStringSubmatch(hostname) + if len(matches) == 2 { + return fmt.Sprintf("dev-%s", matches[1]), nil + } + pterm.Error.WithShowLineNumber(false).Println("Invalid URL format for dev environment. Expected format: '.console.dev.spaceone.dev'") + return "", fmt.Errorf("invalid dev URL format") + } + + // Check for `stg` environment pattern + if strings.HasSuffix(hostname, "console.stg.spaceone.dev") { + re := regexp.MustCompile(`(.*)\.console\.stg\.spaceone\.dev`) + matches := re.FindStringSubmatch(hostname) + if len(matches) == 2 { + return fmt.Sprintf("stg-%s", matches[1]), nil + } + pterm.Error.WithShowLineNumber(false).Println("Invalid URL format for stg environment. Expected format: '.console.stg.spaceone.dev'") + return "", fmt.Errorf("invalid stg URL format") + } + + return "", fmt.Errorf("URL does not match any known environment patterns") +} + +func init() { + rootCmd.AddCommand(initCmd) + + // Define flags for the init command + initCmd.Flags().StringP("environment", "e", "", "Override environment name") + initCmd.Flags().StringP("url", "u", "", "URL for the environment") + initCmd.MarkFlagRequired("url") // Ensure URL is mandatory +} + +// getInitConfigDir returns the directory where config files are stored +func getInitConfigDir() string { + home, err := os.UserHomeDir() + if err != nil { + pterm.Error.WithShowLineNumber(false).Println("Unable to find home directory:", err) + log.Fatal(err) + } + return filepath.Join(home, ".spaceone") +} diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..e69de29