Skip to content

Commit 0c4f013

Browse files
authored
Merge pull request #103 from yjinjo/master
Handle grpc connection for local and static mode
2 parents 0626c20 + 5d77104 commit 0c4f013

File tree

5 files changed

+244
-73
lines changed

5 files changed

+244
-73
lines changed

Diff for: cmd/common/fetchService.go

+30-10
Original file line numberDiff line numberDiff line change
@@ -427,19 +427,39 @@ func fetchJSONResponse(config *Config, serviceName string, verb string, resource
427427
}
428428
} else {
429429
if !hasIdentityService {
430-
urlParts := strings.Split(apiEndpoint, "//")
431-
if len(urlParts) != 2 {
432-
return nil, fmt.Errorf("invalid API endpoint format: %s", apiEndpoint)
433-
}
430+
// Handle gRPC+SSL protocol directly
431+
if strings.HasPrefix(config.Environments[config.Environment].Endpoint, "grpc+ssl://") {
432+
endpoint := config.Environments[config.Environment].Endpoint
433+
parts := strings.Split(endpoint, "/")
434+
endpoint = strings.Join(parts[:len(parts)-1], "/")
435+
parts = strings.Split(endpoint, "://")
436+
if len(parts) != 2 {
437+
return nil, fmt.Errorf("invalid endpoint format: %s", endpoint)
438+
}
434439

435-
domainParts := strings.Split(urlParts[1], ".")
436-
if len(domainParts) < 4 {
437-
return nil, fmt.Errorf("invalid domain format in API endpoint: %s", apiEndpoint)
438-
}
440+
hostParts := strings.Split(parts[1], ".")
441+
if len(hostParts) < 4 {
442+
return nil, fmt.Errorf("invalid endpoint format: %s", endpoint)
443+
}
439444

440-
domainParts[0] = convertServiceNameToEndpoint(serviceName)
445+
// Replace service name
446+
hostParts[0] = convertServiceNameToEndpoint(serviceName)
447+
hostPort = strings.Join(hostParts, ".")
448+
} else {
449+
// Original HTTP/HTTPS handling
450+
urlParts := strings.Split(apiEndpoint, "//")
451+
if len(urlParts) != 2 {
452+
return nil, fmt.Errorf("invalid API endpoint format: %s", apiEndpoint)
453+
}
441454

442-
hostPort = strings.Join(domainParts, ".") + ":443"
455+
domainParts := strings.Split(urlParts[1], ".")
456+
if len(domainParts) < 4 {
457+
return nil, fmt.Errorf("invalid domain format in API endpoint: %s", apiEndpoint)
458+
}
459+
460+
domainParts[0] = convertServiceNameToEndpoint(serviceName)
461+
hostPort = strings.Join(domainParts, ".") + ":443"
462+
}
443463
} else {
444464
trimmedEndpoint := strings.TrimPrefix(identityEndpoint, "grpc+ssl://")
445465
parts := strings.Split(trimmedEndpoint, ".")

Diff for: cmd/other/apiResources.go

+33
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,39 @@ func FetchEndpointsMap(endpoint string) (map[string]string, error) {
201201
}
202202

203203
if !hasIdentityService {
204+
// Handle gRPC+SSL protocol directly
205+
if strings.HasPrefix(endpoint, "grpc+ssl://") {
206+
// Parse the endpoint
207+
parts := strings.Split(endpoint, "/")
208+
endpoint = strings.Join(parts[:len(parts)-1], "/")
209+
parts = strings.Split(endpoint, "://")
210+
if len(parts) != 2 {
211+
return nil, fmt.Errorf("invalid endpoint format: %s", endpoint)
212+
}
213+
214+
hostParts := strings.Split(parts[1], ".")
215+
svc := hostParts[0]
216+
baseDomain := strings.Join(hostParts[1:], ".")
217+
218+
// Configure TLS
219+
tlsConfig := &tls.Config{
220+
InsecureSkipVerify: false,
221+
}
222+
opts := []grpc.DialOption{
223+
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
224+
}
225+
226+
//If current service is not identity, modify hostPort to use identity service
227+
if svc != "identity" {
228+
hostPort := fmt.Sprintf("identity.%s", baseDomain)
229+
endpoints, err := invokeGRPCEndpointList(hostPort, opts)
230+
if err != nil {
231+
return nil, fmt.Errorf("failed to get endpoints from gRPC: %v", err)
232+
}
233+
return endpoints, nil
234+
}
235+
}
236+
204237
payload := map[string]string{}
205238
jsonPayload, err := json.Marshal(payload)
206239
if err != nil {

Diff for: cmd/other/login.go

+29-2
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,13 @@ func executeUserLogin(currentEnv string) {
697697
scope = "WORKSPACE"
698698
}
699699

700+
// Grant new token using the refresh token
701+
newAccessToken, err := grantToken("", identityEndpoint, hasIdentityService, refreshToken, scope, domainID, workspaceID)
702+
if err != nil {
703+
pterm.Error.Println("Failed to retrieve new access token:", err)
704+
exitWithError()
705+
}
706+
700707
// Create cache directory
701708
envCacheDir := filepath.Join(homeDir, ".cfctl", "cache", currentEnv)
702709
if err := os.MkdirAll(envCacheDir, 0700); err != nil {
@@ -710,7 +717,7 @@ func executeUserLogin(currentEnv string) {
710717
exitWithError()
711718
}
712719

713-
if err := os.WriteFile(filepath.Join(envCacheDir, "access_token"), []byte(accessToken), 0600); err != nil {
720+
if err := os.WriteFile(filepath.Join(envCacheDir, "access_token"), []byte(newAccessToken), 0600); err != nil {
714721
pterm.Error.Printf("Failed to save access token: %v\n", err)
715722
exitWithError()
716723
}
@@ -721,6 +728,12 @@ func executeUserLogin(currentEnv string) {
721728

722729
// GetAPIEndpoint fetches the actual API endpoint from the config endpoint
723730
func GetAPIEndpoint(endpoint string) (string, error) {
731+
// Handle gRPC+SSL protocol
732+
if strings.HasPrefix(endpoint, "grpc+ssl://") {
733+
// For gRPC+SSL endpoints, return as is since it's already in the correct format
734+
return endpoint, nil
735+
}
736+
724737
// Remove protocol prefix if exists
725738
endpoint = strings.TrimPrefix(endpoint, "https://")
726739
endpoint = strings.TrimPrefix(endpoint, "http://")
@@ -759,6 +772,20 @@ func GetAPIEndpoint(endpoint string) (string, error) {
759772

760773
// GetIdentityEndpoint fetches the identity service endpoint from the API endpoint
761774
func GetIdentityEndpoint(apiEndpoint string) (string, bool, error) {
775+
// If the endpoint is already gRPC+SSL
776+
if strings.HasPrefix(apiEndpoint, "grpc+ssl://") {
777+
// Check if it contains 'identity'
778+
containsIdentity := strings.Contains(apiEndpoint, "identity")
779+
780+
// Remove /v1 suffix if present
781+
if idx := strings.Index(apiEndpoint, "/v"); idx != -1 {
782+
apiEndpoint = apiEndpoint[:idx]
783+
}
784+
785+
return apiEndpoint, containsIdentity, nil
786+
}
787+
788+
// Original HTTP/HTTPS handling logic
762789
endpointListURL := fmt.Sprintf("%s/identity/endpoint/list", apiEndpoint)
763790

764791
payload := map[string]string{}
@@ -1062,7 +1089,7 @@ func loadEnvironmentConfig() {
10621089
"Please enable proxy mode and set identity endpoint first.")
10631090

10641091
pterm.DefaultBox.WithBoxStyle(pterm.NewStyle(pterm.FgCyan)).
1065-
Println("$ cfctl config endpoint -s identity\n" +
1092+
Println("$ cfctl setting endpoint -s identity\n" +
10661093
"$ cfctl login")
10671094

10681095
exitWithError()

Diff for: cmd/other/setting.go

+59-16
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"crypto/tls"
77
"encoding/json"
88
"fmt"
9-
"google.golang.org/grpc/credentials/insecure"
109
"log"
1110
"net/http"
1211
"net/url"
@@ -15,6 +14,8 @@ import (
1514
"regexp"
1615
"strings"
1716

17+
"google.golang.org/grpc/credentials/insecure"
18+
1819
"gopkg.in/yaml.v3"
1920

2021
"github.com/jhump/protoreflect/dynamic"
@@ -53,16 +54,21 @@ var settingInitCmd = &cobra.Command{
5354
cfctl setting init endpoint http://localhost:8080 --app
5455
cfctl setting init endpoint http://localhost:8080 --user
5556
or
56-
cfctl setting init local`,
57+
cfctl setting init static grpc://localhost:50051
58+
cfctl setting init static grpc+ssl://inventory.-`,
5759
}
5860

59-
// settingInitLocalCmd represents the setting init local command
60-
var settingInitLocalCmd = &cobra.Command{
61-
Use: "local",
62-
Short: "Initialize local environment setting",
63-
Long: `Initialize a local environment setting with default configuration.`,
64-
Args: cobra.NoArgs,
61+
// settingInitStaticCmd represents the setting init direct command
62+
var settingInitStaticCmd = &cobra.Command{
63+
Use: "static [endpoint]",
64+
Short: "Initialize static connection to a local or service endpoint",
65+
Long: `Initialize configuration with a static service endpoint.
66+
This is useful for development or when connecting directly to specific service endpoints.`,
67+
Example: ` cfctl setting init static grpc://localhost:50051
68+
cfctl setting init static grpc+ssl://inventory-`,
69+
Args: cobra.ExactArgs(1),
6570
Run: func(cmd *cobra.Command, args []string) {
71+
endpoint := args[0]
6672
settingDir := GetSettingDir()
6773
if err := os.MkdirAll(settingDir, 0755); err != nil {
6874
pterm.Error.Printf("Failed to create setting directory: %v\n", err)
@@ -74,14 +80,20 @@ var settingInitLocalCmd = &cobra.Command{
7480
v.SetConfigFile(mainSettingPath)
7581
v.SetConfigType("yaml")
7682

77-
// Check if local environment already exists
83+
envName, err := parseEnvNameFromURL(endpoint)
84+
if err != nil {
85+
pterm.Error.Printf("Failed to parse environment name: %v\n", err)
86+
return
87+
}
88+
89+
// Check if environment already exists
7890
if err := v.ReadInConfig(); err == nil {
7991
environments := v.GetStringMap("environments")
80-
if existingEnv, exists := environments["local"]; exists {
92+
if existingEnv, exists := environments[envName]; exists {
8193
currentConfig, _ := yaml.Marshal(map[string]interface{}{
82-
"environment": "local",
94+
"environment": envName,
8395
"environments": map[string]interface{}{
84-
"local": existingEnv,
96+
envName: existingEnv,
8597
},
8698
})
8799

@@ -91,7 +103,7 @@ var settingInitLocalCmd = &cobra.Command{
91103
WithLeftPadding(4).
92104
WithBoxStyle(pterm.NewStyle(pterm.FgYellow))
93105

94-
confirmBox.Println("Environment 'local' already exists.\nDo you want to overwrite it?")
106+
confirmBox.Println(fmt.Sprintf("Environment '%s' already exists.\nDo you want to overwrite it?", envName))
95107

96108
pterm.Info.Println("Current configuration:")
97109
fmt.Println(string(currentConfig))
@@ -102,13 +114,14 @@ var settingInitLocalCmd = &cobra.Command{
102114
response = strings.ToLower(strings.TrimSpace(response))
103115

104116
if response != "y" {
105-
pterm.Info.Println("Operation cancelled. Environment 'local' remains unchanged.")
117+
pterm.Info.Printf("Operation cancelled. Environment '%s' remains unchanged.\n", envName)
106118
return
107119
}
108120
}
109121
}
110122

111-
updateSetting("local", "grpc://localhost:50051", "")
123+
updateSetting(envName, endpoint, "")
124+
pterm.Success.Printf("Successfully initialized direct connection to %s\n", endpoint)
112125
},
113126
}
114127

@@ -480,6 +493,17 @@ You can either specify a new endpoint URL directly or use the service-based endp
480493
if listFlag {
481494
token, err := getToken(appV)
482495
if err != nil {
496+
if strings.HasSuffix(currentEnv, "-user") {
497+
pterm.DefaultBox.WithTitle("Authentication Required").
498+
WithTitleTopCenter().
499+
WithBoxStyle(pterm.NewStyle(pterm.FgLightCyan)).
500+
WithRightPadding(4).
501+
WithLeftPadding(4).
502+
Println("Please login to SpaceONE Console first.\n" +
503+
"Run the following command to authenticate:\n\n" +
504+
"$ cfctl login")
505+
return
506+
}
483507
pterm.Error.Println("Error retrieving token:", err)
484508
return
485509
}
@@ -1201,11 +1225,17 @@ func updateGlobalSetting() {
12011225
}
12021226

12031227
func parseEnvNameFromURL(urlStr string) (string, error) {
1228+
isGRPC := strings.HasPrefix(urlStr, "grpc://") || strings.HasPrefix(urlStr, "grpc+ssl://")
1229+
12041230
urlStr = strings.TrimPrefix(urlStr, "https://")
12051231
urlStr = strings.TrimPrefix(urlStr, "http://")
12061232
urlStr = strings.TrimPrefix(urlStr, "grpc://")
12071233
urlStr = strings.TrimPrefix(urlStr, "grpc+ssl://")
12081234

1235+
if isGRPC {
1236+
return "local", nil
1237+
}
1238+
12091239
if strings.Contains(urlStr, "localhost") {
12101240
return "local", nil
12111241
}
@@ -1271,6 +1301,19 @@ func updateSetting(envName, endpoint string, envSuffix string) {
12711301
envKey := fmt.Sprintf("environments.%s.endpoint", fullEnvName)
12721302
v.Set(envKey, endpoint)
12731303

1304+
// Set proxy based on endpoint type
1305+
proxyKey := fmt.Sprintf("environments.%s.proxy", fullEnvName)
1306+
if strings.HasPrefix(endpoint, "grpc://") || strings.HasPrefix(endpoint, "grpc+ssl://") {
1307+
// Check if endpoint contains 'identity'
1308+
if strings.Contains(strings.ToLower(endpoint), "identity") {
1309+
v.Set(proxyKey, true)
1310+
} else {
1311+
v.Set(proxyKey, false)
1312+
}
1313+
} else {
1314+
v.Set(proxyKey, true)
1315+
}
1316+
12741317
// Set additional configurations based on environment type
12751318
if envName == "local" {
12761319
// Local environment settings (only for pure 'local', not 'local-user' or 'local-app')
@@ -1378,7 +1421,7 @@ func init() {
13781421
SettingCmd.AddCommand(settingEndpointCmd)
13791422
SettingCmd.AddCommand(showCmd)
13801423
settingInitCmd.AddCommand(settingInitEndpointCmd)
1381-
settingInitCmd.AddCommand(settingInitLocalCmd)
1424+
settingInitCmd.AddCommand(settingInitStaticCmd)
13821425

13831426
settingInitEndpointCmd.Flags().Bool("app", false, "Initialize as application configuration")
13841427
settingInitEndpointCmd.Flags().Bool("user", false, "Initialize as user-specific configuration")

0 commit comments

Comments
 (0)