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

Add user config and modify the show command #46

Merged
merged 1 commit into from
Nov 25, 2024
Merged
Changes from all 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
refactor: add user config and modify show command
Signed-off-by: Youngjin Jo <youngjinjo@megazone.com>
  • Loading branch information
yjinjo committed Nov 25, 2024
commit b78a91886b9a06a42f3443ea89d809c3ebd8a91e
186 changes: 166 additions & 20 deletions cmd/other/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
package other

import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
Expand Down Expand Up @@ -272,24 +274,38 @@ var showCmd = &cobra.Command{
Use: "show",
Short: "Display the current cfctl configuration",
Run: func(cmd *cobra.Command, args []string) {
// Assume showCmd operates on the app config
configPath := filepath.Join(GetConfigDir(), "config.yaml")
v := appViper
configDir := GetConfigDir()
appConfigPath := filepath.Join(configDir, "config.yaml")
userConfigPath := filepath.Join(configDir, "cache", "config.yaml")

// Load or create the config file
if err := loadConfig(v, configPath); err != nil {
// Load app configuration
if err := loadConfig(appViper, appConfigPath); err != nil {
pterm.Error.Println(err)
return
}

currentEnv := getCurrentEnvironment(v)
// Load user configuration
if err := loadConfig(userViper, userConfigPath); err != nil {
pterm.Error.Println(err)
return
}

currentEnv := getCurrentEnvironment(appViper)
if currentEnv == "" {
log.Fatal("No environment set in ~/.cfctl/config.yaml")
pterm.Sprintf("No environment set in %s\n", appConfigPath)
return
}

envConfig := v.GetStringMap(fmt.Sprintf("environments.%s", currentEnv))
// Try to get the environment from appViper
envConfig := appViper.GetStringMap(fmt.Sprintf("environments.%s", currentEnv))

// If not found in appViper, try userViper
if len(envConfig) == 0 {
log.Fatalf("Environment '%s' not found in config.yaml", currentEnv)
envConfig = userViper.GetStringMap(fmt.Sprintf("environments.%s", currentEnv))
if len(envConfig) == 0 {
pterm.Error.Printf("Environment '%s' not found in %s or %s\n", currentEnv, appConfigPath, userConfigPath)
return
}
}

output, _ := cmd.Flags().GetString("output")
Expand Down Expand Up @@ -320,17 +336,58 @@ var configEndpointCmd = &cobra.Command{
Long: `Update the endpoint for the current environment based on the specified service.
If the service is not 'identity', the proxy setting will be updated to false.

Available Services:
identity, inventory, plugin, repository, secret, monitoring, config, statistics,
notification, cost_analysis, board, file_manager, dashboard`,
Available Services are fetched dynamically from the backend.`,
Run: func(cmd *cobra.Command, args []string) {
service, _ := cmd.Flags().GetString("service")
if service == "" {
token, err := getToken(appViper)
if err != nil {
pterm.Error.Println("Error retrieving token:", err)
return
}

pterm.Error.Println("Please specify a service using -s or --service.")
pterm.Println()

// Fetch and display available services
baseURL, err := getBaseURL(appViper)
if err != nil {
pterm.Error.Println("Error retrieving base URL:", err)
return
}

services, err := fetchAvailableServices(baseURL, token)
if err != nil {
pterm.Error.Println("Error fetching available services:", err)
return
}

if len(services) == 0 {
pterm.Println("No available services found.")
return
}

pterm.DefaultBox.WithTitle("Available Services").
WithRightPadding(1).WithLeftPadding(1).WithTopPadding(0).WithBottomPadding(0).
Println(strings.Join(availableServices, "\n"))
Println(strings.Join(services, "\n"))
return
}

// Fetch available services to validate the input
baseURL, err := getBaseURL(appViper)
if err != nil {
pterm.Error.Println("Error retrieving base URL:", err)
return
}

token, err := getToken(appViper)
if err != nil {
pterm.Error.Println("Error retrieving token:", err)
return
}

services, err := fetchAvailableServices(baseURL, token)
if err != nil {
pterm.Error.Println("Error fetching available services:", err)
return
}

Expand All @@ -346,9 +403,13 @@ Available Services:
if !isValidService {
pterm.Error.Printf("Invalid service '%s'.\n", service)
pterm.Println()
pterm.DefaultBox.WithTitle("Available Services").
WithRightPadding(1).WithLeftPadding(1).WithTopPadding(0).WithBottomPadding(0).
Println(strings.Join(availableServices, "\n"))
if len(services) > 0 {
pterm.DefaultBox.WithTitle("Available Services").
WithRightPadding(1).WithLeftPadding(1).WithTopPadding(0).WithBottomPadding(0).
Println(strings.Join(services, "\n"))
} else {
pterm.Println("No available services found.")
}
return
}

Expand Down Expand Up @@ -402,6 +463,90 @@ Available Services:
},
}

// fetchAvailableServices retrieves the list of services from the backend API using the provided token.
func fetchAvailableServices(baseURL, token string) ([]string, error) {
// Use constructEndpoint to get the gRPC endpoint
grpcEndpoint, err := constructEndpoint(baseURL)
if err != nil {
return nil, fmt.Errorf("failed to construct gRPC endpoint: %w", err)
}

// Derive the API URL from the gRPC endpoint
// Assuming the API is accessible via HTTPS and follows a similar pattern
// For example, if grpcEndpoint is "grpc+ssl://identity.api.dev.spaceone.dev:443"
// then apiURL would be "https://identity.api.dev.spaceone.dev"
parsedURL, err := url.Parse(grpcEndpoint)
if err != nil {
return nil, fmt.Errorf("failed to parse gRPC endpoint: %w", err)
}

apiURL := fmt.Sprintf("https://%s", parsedURL.Hostname())

// Create a new HTTP request
req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/services", apiURL), nil) // Adjust the path as needed
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}

// Set the Authorization header with the token
req.Header.Set("Authorization", "Bearer "+token)

// Initialize the HTTP client
client := &http.Client{}

// Send the request
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to fetch services: %w", err)
}
defer resp.Body.Close()

// Check for non-200 status codes
if resp.StatusCode != http.StatusOK {
var buf bytes.Buffer
buf.ReadFrom(resp.Body)
return nil, fmt.Errorf("failed to fetch services: %s", buf.String())
}

// Parse the JSON response
var services []string
if err := json.NewDecoder(resp.Body).Decode(&services); err != nil {
return nil, fmt.Errorf("failed to decode services response: %w", err)
}

return services, nil
}

// getBaseURL retrieves the base URL for the current environment from the app configuration.
func getBaseURL(v *viper.Viper) (string, error) {
currentEnv := getCurrentEnvironment(v)
if currentEnv == "" {
return "", fmt.Errorf("no environment is set")
}

baseURL := v.GetString(fmt.Sprintf("environments.%s.endpoint", currentEnv))
if baseURL == "" {
return "", fmt.Errorf("no endpoint found for environment '%s'", currentEnv)
}

return baseURL, nil
}

// getToken retrieves the token for the current environment.
func getToken(v *viper.Viper) (string, error) {
currentEnv := getCurrentEnvironment(v)
if currentEnv == "" {
return "", fmt.Errorf("no environment is set")
}

token := v.GetString(fmt.Sprintf("environments.%s.token", currentEnv))
if token == "" {
return "", fmt.Errorf("no token found for environment '%s'", currentEnv)
}

return token, nil
}

// GetConfigDir returns the directory where config files are stored
func GetConfigDir() string {
home, err := os.UserHomeDir()
Expand Down Expand Up @@ -647,8 +792,9 @@ func constructEndpoint(baseURL string) (string, error) {
prefix = "dev"
case strings.Contains(hostname, ".stg.spaceone.dev"):
prefix = "stg"
case strings.Contains(hostname, ".spaceone.megazone.io"):
prefix = "prd"
// TODO: After set up production
//case strings.Contains(hostname, ".spaceone.megazone.io"):
// prefix = "prd"
default:
return "", fmt.Errorf("unknown environment prefix in URL: %s", hostname)
}
Expand All @@ -660,7 +806,7 @@ func constructEndpoint(baseURL string) (string, error) {
}

// Construct the endpoint dynamically based on the service
newEndpoint := fmt.Sprintf("grpc+ssl://%s.api.%s.spaceone.dev:443", service, prefix)
newEndpoint := fmt.Sprintf("grpc+ssl://identity.api.%s.spaceone.dev:443", prefix)
return newEndpoint, nil
}

Expand Down
Loading