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 progress bar and save services_verbs to one file #89

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
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
111 changes: 64 additions & 47 deletions cmd/common/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ func convertServiceNameToEndpoint(serviceName string) string {
}

func BuildVerbResourceMap(serviceName string) (map[string][]string, error) {
// Try to load from cache first
home, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("failed to get home directory: %v", err)
Expand All @@ -44,71 +43,43 @@ func BuildVerbResourceMap(serviceName string) (map[string][]string, error) {
}

if strings.HasPrefix(config.Environment, "local-") {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
return nil, fmt.Errorf("local gRPC server connection failed: %v", err)
}
defer conn.Close()

ctx := context.Background()
refClient := grpcreflect.NewClient(ctx, grpc_reflection_v1alpha.NewServerReflectionClient(conn))
defer refClient.Reset()

services, err := refClient.ListServices()
if err != nil {
return nil, fmt.Errorf("failed to list local services: %v", err)
}

verbResourceMap := make(map[string][]string)
for _, s := range services {
if !strings.Contains(s, fmt.Sprintf(".%s.", serviceName)) {
continue
}

serviceDesc, err := refClient.ResolveService(s)
if err != nil {
continue
}

resourceName := s[strings.LastIndex(s, ".")+1:]
for _, method := range serviceDesc.GetMethods() {
verb := method.GetName()
if resources, ok := verbResourceMap[verb]; ok {
verbResourceMap[verb] = append(resources, resourceName)
} else {
verbResourceMap[verb] = []string{resourceName}
}
}
}

return verbResourceMap, nil
return handleLocalEnvironment(serviceName)
}

cacheDir := filepath.Join(home, ".cfctl", "cache", config.Environment)
cacheFile := filepath.Join(cacheDir, fmt.Sprintf("%s_verbs.yaml", serviceName))
cacheFile := filepath.Join(cacheDir, "verb_resources.yaml")

// Check if cache exists and is fresh (less than 1 hour old)
if info, err := os.Stat(cacheFile); err == nil {
if time.Since(info.ModTime()) < time.Hour {
data, err := os.ReadFile(cacheFile)
if err == nil {
verbResourceMap := make(map[string][]string)
if err := yaml.Unmarshal(data, &verbResourceMap); err == nil {
return verbResourceMap, nil
var allServices map[string]map[string][]string
if err := yaml.Unmarshal(data, &allServices); err == nil {
if verbMap, exists := allServices[serviceName]; exists {
return verbMap, nil
}
}
}
}
}

// Cache miss or expired, fetch from server
verbResourceMap, err := fetchVerbResourceMap(serviceName, config)
if err != nil {
return nil, err
}

// Save to cache
var allServices map[string]map[string][]string
if data, err := os.ReadFile(cacheFile); err == nil {
yaml.Unmarshal(data, &allServices)
}
if allServices == nil {
allServices = make(map[string]map[string][]string)
}

allServices[serviceName] = verbResourceMap

if err := os.MkdirAll(cacheDir, 0755); err == nil {
data, err := yaml.Marshal(verbResourceMap)
data, err := yaml.Marshal(allServices)
if err == nil {
os.WriteFile(cacheFile, data, 0644)
}
Expand All @@ -117,6 +88,52 @@ func BuildVerbResourceMap(serviceName string) (map[string][]string, error) {
return verbResourceMap, nil
}

func handleLocalEnvironment(serviceName string) (map[string][]string, error) {
if serviceName != "plugin" {
return nil, fmt.Errorf("only plugin service is supported in local environment")
}

// local 환경의 plugin 서비스 endpoint 설정
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
return nil, fmt.Errorf("failed to connect to local plugin service: %v", err)
}
defer conn.Close()

ctx := context.Background()
refClient := grpcreflect.NewClient(ctx, grpc_reflection_v1alpha.NewServerReflectionClient(conn))
defer refClient.Reset()

services, err := refClient.ListServices()
if err != nil {
return nil, fmt.Errorf("failed to list local services: %v", err)
}

verbResourceMap := make(map[string][]string)
for _, s := range services {
if !strings.Contains(s, ".plugin.") {
continue
}

serviceDesc, err := refClient.ResolveService(s)
if err != nil {
continue
}

resourceName := s[strings.LastIndex(s, ".")+1:]
for _, method := range serviceDesc.GetMethods() {
verb := method.GetName()
if resources, ok := verbResourceMap[verb]; ok {
verbResourceMap[verb] = append(resources, resourceName)
} else {
verbResourceMap[verb] = []string{resourceName}
}
}
}

return verbResourceMap, nil
}

func fetchVerbResourceMap(serviceName string, config *Config) (map[string][]string, error) {
envConfig := config.Environments[config.Environment]
if envConfig.URL == "" {
Expand Down
129 changes: 48 additions & 81 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cmd

import (
"context"
"fmt"
"gopkg.in/yaml.v3"
"log"
Expand All @@ -10,16 +9,13 @@ import (
"strings"
"time"

"github.com/jhump/protoreflect/grpcreflect"
"github.com/spf13/viper"

"github.com/cloudforet-io/cfctl/cmd/common"
"github.com/cloudforet-io/cfctl/cmd/other"

"github.com/pterm/pterm"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
)

var cfgFile string
Expand Down Expand Up @@ -105,13 +101,9 @@ func init() {

// Determine if the current command is 'setting environment -l'
skipDynamicCommands := false
if len(os.Args) >= 3 && os.Args[1] == "setting" && os.Args[2] == "environment" {
for _, arg := range os.Args[3:] {
if arg == "-l" || arg == "--list" {
skipDynamicCommands = true
break
}
}
if len(os.Args) >= 2 && os.Args[1] == "setting" {
// Skip dynamic commands for all setting related operations
skipDynamicCommands = true
}

if !skipDynamicCommands {
Expand Down Expand Up @@ -239,7 +231,19 @@ func showInitializationGuide(originalErr error) {
}

func addDynamicServiceCommands() error {
// If we already have in-memory cache, use it
config, err := loadConfig()
if err != nil {
return err
}

// For local environment, only add plugin command
if strings.HasPrefix(config.Environment, "local-") {
cmd := createServiceCommand("plugin")
rootCmd.AddCommand(cmd)
return nil
}

// For non-local environments, continue with existing logic...
if cachedEndpointsMap != nil {
for serviceName := range cachedEndpointsMap {
cmd := createServiceCommand(serviceName)
Expand All @@ -248,89 +252,52 @@ func addDynamicServiceCommands() error {
return nil
}

// Load configuration
setting, err := loadConfig()
if err != nil {
return fmt.Errorf("failed to load setting: %v", err)
}

// Handle local environment
if strings.HasPrefix(setting.Environment, "local-") {
// Try connecting to local gRPC server
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(2*time.Second))
if err != nil {
pterm.Error.Printf("Cannot connect to local gRPC server (grpc://localhost:50051)\n")
pterm.Info.Println("Please check if your gRPC server is running")
return fmt.Errorf("local gRPC server connection failed: %v", err)
// Only show progress bar when actually fetching services
if len(os.Args) == 1 || (len(os.Args) > 1 && os.Args[1] != "setting") {
// Create progress bar
progressbar, _ := pterm.DefaultProgressbar.
WithTotal(4).
WithTitle("Initializing services").
Start()

progressbar.UpdateTitle("Preparing endpoint configuration")
endpoint := config.Endpoint
if !strings.Contains(endpoint, "identity") {
parts := strings.Split(endpoint, "://")
if len(parts) == 2 {
hostParts := strings.Split(parts[1], ".")
if len(hostParts) >= 4 {
env := hostParts[2]
endpoint = fmt.Sprintf("grpc+ssl://identity.api.%s.spaceone.dev:443", env)
}
}
}
defer conn.Close()
progressbar.Increment()
time.Sleep(time.Millisecond * 300)

// Create reflection client
ctx := context.Background()
refClient := grpcreflect.NewClient(ctx, grpc_reflection_v1alpha.NewServerReflectionClient(conn))
defer refClient.Reset()

// List all services
services, err := refClient.ListServices()
progressbar.UpdateTitle("Fetching available services")
endpointsMap, err := other.FetchEndpointsMap(endpoint)
if err != nil {
return fmt.Errorf("failed to list local services: %v", err)
return fmt.Errorf("failed to fetch services: %v", err)
}
progressbar.Increment()
time.Sleep(time.Millisecond * 300)

endpointsMap := make(map[string]string)
for _, svc := range services {
if strings.HasPrefix(svc, "spaceone.api.") {
parts := strings.Split(svc, ".")
if len(parts) >= 4 {
serviceName := parts[2]
// Skip core service
if serviceName != "core" {
endpointsMap[serviceName] = "grpc://localhost:50051"
}
}
}
}

// Store in both memory and file cache
progressbar.UpdateTitle("Creating cache for faster subsequent runs")
cachedEndpointsMap = endpointsMap
if err := saveEndpointsCache(endpointsMap); err != nil {
fmt.Fprintf(os.Stderr, "Warning: Failed to cache endpoints: %v\n", err)
}
progressbar.Increment()
time.Sleep(time.Millisecond * 300)

// Create commands for each service
progressbar.UpdateTitle("Registering verbs and resources commands to the cache")
for serviceName := range endpointsMap {
cmd := createServiceCommand(serviceName)
rootCmd.AddCommand(cmd)
}

return nil
}

// Continue with existing logic for non-local environments
endpoint := setting.Endpoint
if !strings.Contains(endpoint, "identity") {
parts := strings.Split(endpoint, "://")
if len(parts) == 2 {
hostParts := strings.Split(parts[1], ".")
if len(hostParts) >= 4 {
env := hostParts[2]
endpoint = fmt.Sprintf("grpc+ssl://identity.api.%s.spaceone.dev:443", env)
}
}
}

endpointsMap, err := other.FetchEndpointsMap(endpoint)
if err != nil {
return fmt.Errorf("failed to fetch services: %v", err)
}

cachedEndpointsMap = endpointsMap
if err := saveEndpointsCache(endpointsMap); err != nil {
fmt.Fprintf(os.Stderr, "Warning: Failed to cache endpoints: %v\n", err)
}

for serviceName := range endpointsMap {
cmd := createServiceCommand(serviceName)
rootCmd.AddCommand(cmd)
progressbar.Increment()
time.Sleep(time.Millisecond * 300)
}

return nil
Expand Down
Loading