Skip to content
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
40 changes: 30 additions & 10 deletions cmd/common/fetchService.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,19 +427,39 @@ func fetchJSONResponse(config *Config, serviceName string, verb string, resource
}
} else {
if !hasIdentityService {
urlParts := strings.Split(apiEndpoint, "//")
if len(urlParts) != 2 {
return nil, fmt.Errorf("invalid API endpoint format: %s", apiEndpoint)
}
// Handle gRPC+SSL protocol directly
if strings.HasPrefix(config.Environments[config.Environment].Endpoint, "grpc+ssl://") {
endpoint := config.Environments[config.Environment].Endpoint
parts := strings.Split(endpoint, "/")
endpoint = strings.Join(parts[:len(parts)-1], "/")
parts = strings.Split(endpoint, "://")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid endpoint format: %s", endpoint)
}

domainParts := strings.Split(urlParts[1], ".")
if len(domainParts) < 4 {
return nil, fmt.Errorf("invalid domain format in API endpoint: %s", apiEndpoint)
}
hostParts := strings.Split(parts[1], ".")
if len(hostParts) < 4 {
return nil, fmt.Errorf("invalid endpoint format: %s", endpoint)
}

domainParts[0] = convertServiceNameToEndpoint(serviceName)
// Replace service name
hostParts[0] = convertServiceNameToEndpoint(serviceName)
hostPort = strings.Join(hostParts, ".")
} else {
// Original HTTP/HTTPS handling
urlParts := strings.Split(apiEndpoint, "//")
if len(urlParts) != 2 {
return nil, fmt.Errorf("invalid API endpoint format: %s", apiEndpoint)
}

hostPort = strings.Join(domainParts, ".") + ":443"
domainParts := strings.Split(urlParts[1], ".")
if len(domainParts) < 4 {
return nil, fmt.Errorf("invalid domain format in API endpoint: %s", apiEndpoint)
}

domainParts[0] = convertServiceNameToEndpoint(serviceName)
hostPort = strings.Join(domainParts, ".") + ":443"
}
} else {
trimmedEndpoint := strings.TrimPrefix(identityEndpoint, "grpc+ssl://")
parts := strings.Split(trimmedEndpoint, ".")
Expand Down
33 changes: 33 additions & 0 deletions cmd/other/apiResources.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,39 @@ func FetchEndpointsMap(endpoint string) (map[string]string, error) {
}

if !hasIdentityService {
// Handle gRPC+SSL protocol directly
if strings.HasPrefix(endpoint, "grpc+ssl://") {
// Parse the endpoint
parts := strings.Split(endpoint, "/")
endpoint = strings.Join(parts[:len(parts)-1], "/")
parts = strings.Split(endpoint, "://")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid endpoint format: %s", endpoint)
}

hostParts := strings.Split(parts[1], ".")
svc := hostParts[0]
baseDomain := strings.Join(hostParts[1:], ".")

// Configure TLS
tlsConfig := &tls.Config{
InsecureSkipVerify: false,
}
opts := []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
}

//If current service is not identity, modify hostPort to use identity service
if svc != "identity" {
hostPort := fmt.Sprintf("identity.%s", baseDomain)
endpoints, err := invokeGRPCEndpointList(hostPort, opts)
if err != nil {
return nil, fmt.Errorf("failed to get endpoints from gRPC: %v", err)
}
return endpoints, nil
}
}

payload := map[string]string{}
jsonPayload, err := json.Marshal(payload)
if err != nil {
Expand Down
31 changes: 29 additions & 2 deletions cmd/other/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,13 @@ func executeUserLogin(currentEnv string) {
scope = "WORKSPACE"
}

// Grant new token using the refresh token
newAccessToken, err := grantToken("", identityEndpoint, hasIdentityService, refreshToken, scope, domainID, workspaceID)
if err != nil {
pterm.Error.Println("Failed to retrieve new access token:", err)
exitWithError()
}

// Create cache directory
envCacheDir := filepath.Join(homeDir, ".cfctl", "cache", currentEnv)
if err := os.MkdirAll(envCacheDir, 0700); err != nil {
Expand All @@ -710,7 +717,7 @@ func executeUserLogin(currentEnv string) {
exitWithError()
}

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

// GetAPIEndpoint fetches the actual API endpoint from the config endpoint
func GetAPIEndpoint(endpoint string) (string, error) {
// Handle gRPC+SSL protocol
if strings.HasPrefix(endpoint, "grpc+ssl://") {
// For gRPC+SSL endpoints, return as is since it's already in the correct format
return endpoint, nil
}

// Remove protocol prefix if exists
endpoint = strings.TrimPrefix(endpoint, "https://")
endpoint = strings.TrimPrefix(endpoint, "http://")
Expand Down Expand Up @@ -759,6 +772,20 @@ func GetAPIEndpoint(endpoint string) (string, error) {

// GetIdentityEndpoint fetches the identity service endpoint from the API endpoint
func GetIdentityEndpoint(apiEndpoint string) (string, bool, error) {
// If the endpoint is already gRPC+SSL
if strings.HasPrefix(apiEndpoint, "grpc+ssl://") {
// Check if it contains 'identity'
containsIdentity := strings.Contains(apiEndpoint, "identity")

// Remove /v1 suffix if present
if idx := strings.Index(apiEndpoint, "/v"); idx != -1 {
apiEndpoint = apiEndpoint[:idx]
}

return apiEndpoint, containsIdentity, nil
}

// Original HTTP/HTTPS handling logic
endpointListURL := fmt.Sprintf("%s/identity/endpoint/list", apiEndpoint)

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

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

exitWithError()
Expand Down
75 changes: 59 additions & 16 deletions cmd/other/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"google.golang.org/grpc/credentials/insecure"
"log"
"net/http"
"net/url"
Expand All @@ -15,6 +14,8 @@ import (
"regexp"
"strings"

"google.golang.org/grpc/credentials/insecure"

"gopkg.in/yaml.v3"

"github.com/jhump/protoreflect/dynamic"
Expand Down Expand Up @@ -53,16 +54,21 @@ var settingInitCmd = &cobra.Command{
cfctl setting init endpoint http://localhost:8080 --app
cfctl setting init endpoint http://localhost:8080 --user
or
cfctl setting init local`,
cfctl setting init static grpc://localhost:50051
cfctl setting init static grpc+ssl://inventory.-`,
}

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

// Check if local environment already exists
envName, err := parseEnvNameFromURL(endpoint)
if err != nil {
pterm.Error.Printf("Failed to parse environment name: %v\n", err)
return
}

// Check if environment already exists
if err := v.ReadInConfig(); err == nil {
environments := v.GetStringMap("environments")
if existingEnv, exists := environments["local"]; exists {
if existingEnv, exists := environments[envName]; exists {
currentConfig, _ := yaml.Marshal(map[string]interface{}{
"environment": "local",
"environment": envName,
"environments": map[string]interface{}{
"local": existingEnv,
envName: existingEnv,
},
})

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

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

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

if response != "y" {
pterm.Info.Println("Operation cancelled. Environment 'local' remains unchanged.")
pterm.Info.Printf("Operation cancelled. Environment '%s' remains unchanged.\n", envName)
return
}
}
}

updateSetting("local", "grpc://localhost:50051", "")
updateSetting(envName, endpoint, "")
pterm.Success.Printf("Successfully initialized direct connection to %s\n", endpoint)
},
}

Expand Down Expand Up @@ -480,6 +493,17 @@ You can either specify a new endpoint URL directly or use the service-based endp
if listFlag {
token, err := getToken(appV)
if err != nil {
if strings.HasSuffix(currentEnv, "-user") {
pterm.DefaultBox.WithTitle("Authentication Required").
WithTitleTopCenter().
WithBoxStyle(pterm.NewStyle(pterm.FgLightCyan)).
WithRightPadding(4).
WithLeftPadding(4).
Println("Please login to SpaceONE Console first.\n" +
"Run the following command to authenticate:\n\n" +
"$ cfctl login")
return
}
pterm.Error.Println("Error retrieving token:", err)
return
}
Expand Down Expand Up @@ -1201,11 +1225,17 @@ func updateGlobalSetting() {
}

func parseEnvNameFromURL(urlStr string) (string, error) {
isGRPC := strings.HasPrefix(urlStr, "grpc://") || strings.HasPrefix(urlStr, "grpc+ssl://")

urlStr = strings.TrimPrefix(urlStr, "https://")
urlStr = strings.TrimPrefix(urlStr, "http://")
urlStr = strings.TrimPrefix(urlStr, "grpc://")
urlStr = strings.TrimPrefix(urlStr, "grpc+ssl://")

if isGRPC {
return "local", nil
}

if strings.Contains(urlStr, "localhost") {
return "local", nil
}
Expand Down Expand Up @@ -1271,6 +1301,19 @@ func updateSetting(envName, endpoint string, envSuffix string) {
envKey := fmt.Sprintf("environments.%s.endpoint", fullEnvName)
v.Set(envKey, endpoint)

// Set proxy based on endpoint type
proxyKey := fmt.Sprintf("environments.%s.proxy", fullEnvName)
if strings.HasPrefix(endpoint, "grpc://") || strings.HasPrefix(endpoint, "grpc+ssl://") {
// Check if endpoint contains 'identity'
if strings.Contains(strings.ToLower(endpoint), "identity") {
v.Set(proxyKey, true)
} else {
v.Set(proxyKey, false)
}
} else {
v.Set(proxyKey, true)
}

// Set additional configurations based on environment type
if envName == "local" {
// Local environment settings (only for pure 'local', not 'local-user' or 'local-app')
Expand Down Expand Up @@ -1378,7 +1421,7 @@ func init() {
SettingCmd.AddCommand(settingEndpointCmd)
SettingCmd.AddCommand(showCmd)
settingInitCmd.AddCommand(settingInitEndpointCmd)
settingInitCmd.AddCommand(settingInitLocalCmd)
settingInitCmd.AddCommand(settingInitStaticCmd)

settingInitEndpointCmd.Flags().Bool("app", false, "Initialize as application configuration")
settingInitEndpointCmd.Flags().Bool("user", false, "Initialize as user-specific configuration")
Expand Down
Loading
Loading