diff --git a/go.mod b/go.mod index dc0e179098..d3e8e5a451 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/charmbracelet/glamour v0.7.0 github.com/charmbracelet/lipgloss v0.11.0 github.com/client9/gospell v0.0.0-20160306015952-90dfc71015df - github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20250521223017-0e8f6f971b52 + github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20251024191705-f32f6cebfdd1 github.com/confluentinc/ccloud-sdk-go-v2/ai v0.1.0 github.com/confluentinc/ccloud-sdk-go-v2/apikeys v0.4.0 github.com/confluentinc/ccloud-sdk-go-v2/billing v0.3.0 diff --git a/go.sum b/go.sum index d6b9f27d60..1568898f7f 100644 --- a/go.sum +++ b/go.sum @@ -186,8 +186,8 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/compose-spec/compose-go/v2 v2.1.3 h1:bD67uqLuL/XgkAK6ir3xZvNLFPxPScEi1KW7R5esrLE= github.com/compose-spec/compose-go/v2 v2.1.3/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= -github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20250521223017-0e8f6f971b52 h1:19qEGhkbZa5fopKCe0VPIV+Sasby4Pv10z9ZaktwWso= -github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20250521223017-0e8f6f971b52/go.mod h1:62EMf+5uFEt1BJ2q8WMrUoI9VUSxAbDnmZCGRt/MbA0= +github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20251024191705-f32f6cebfdd1 h1:mXLlTii+xqGk9wNtAoPnrJsvR1bwuaEmWD4TuUoTYfw= +github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20251024191705-f32f6cebfdd1/go.mod h1:62EMf+5uFEt1BJ2q8WMrUoI9VUSxAbDnmZCGRt/MbA0= github.com/confluentinc/ccloud-sdk-go-v2/ai v0.1.0 h1:zSF4OQUJXWH2JeAo9rsq13ibk+JFdzITGR8S7cFMpzw= github.com/confluentinc/ccloud-sdk-go-v2/ai v0.1.0/go.mod h1:DoxqzzF3JzvJr3fWkvCiOHFlE0GoYpozWxFZ1Ud9ntA= github.com/confluentinc/ccloud-sdk-go-v2/apikeys v0.4.0 h1:8fWyLwMuy8ec0MVF5Avd54UvbIxhDFhZzanHBVwgxdw= diff --git a/pkg/cmd/run_requirements_test.go b/pkg/cmd/run_requirements_test.go index 97e405231f..5548f4abaf 100644 --- a/pkg/cmd/run_requirements_test.go +++ b/pkg/cmd/run_requirements_test.go @@ -17,6 +17,7 @@ var ( regularOrgContextState = &config.ContextState{Auth: &config.AuthConfig{Organization: testserver.RegularOrg}} endOfFreeTrialSuspendedOrgContextState = &config.ContextState{Auth: &config.AuthConfig{Organization: testserver.SuspendedOrg(ccloudv1.SuspensionEventType_SUSPENSION_EVENT_END_OF_FREE_TRIAL)}} + pauseTrialSuspendedOrgContextState = &config.ContextState{Auth: &config.AuthConfig{Organization: testserver.SuspendedOrg(ccloudv1.SuspensionEventType_SUSPENSION_EVENT_PAUSE_TRIAL)}} normalSuspendedOrgContextState = &config.ContextState{Auth: &config.AuthConfig{Organization: testserver.SuspendedOrg(ccloudv1.SuspensionEventType_SUSPENSION_EVENT_CUSTOMER_INITIATED_ORG_DEACTIVATION)}} cloudCfg = func(contextState *config.ContextState) *config.Config { @@ -89,6 +90,7 @@ func TestErrIfMissingRunRequirement_Error(t *testing.T) { }{ {RequireCloudLogin, onPremCfg, config.RequireCloudLoginErr}, {RequireCloudLogin, cloudCfg(endOfFreeTrialSuspendedOrgContextState), config.RequireCloudLoginFreeTrialEndedOrgUnsuspendedErr}, + {RequireCloudLogin, cloudCfg(pauseTrialSuspendedOrgContextState), config.RequireCloudLoginPauseTrialOrgUnsuspendedErr}, {RequireCloudLogin, cloudCfg(normalSuspendedOrgContextState), config.RequireCloudLoginOrgUnsuspendedErr}, {RequireCloudLoginAllowFreeTrialEnded, onPremCfg, config.RequireCloudLoginErr}, {RequireCloudLoginAllowFreeTrialEnded, cloudCfg(normalSuspendedOrgContextState), config.RequireCloudLoginOrgUnsuspendedErr}, diff --git a/pkg/config/config.go b/pkg/config/config.go index 53c94ce376..6b2be4b527 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -37,6 +37,10 @@ var ( ErrorMsg: "you must unsuspend your organization to use this command", SuggestionsMsg: errors.EndOfFreeTrialSuggestions, } + RequireCloudLoginPauseTrialOrgUnsuspendedErr = &errors.RunRequirementError{ + ErrorMsg: "you must resume your organization to use this command", + SuggestionsMsg: errors.PauseTrialSuggestions, + } RequireCloudLoginOrOnPremErr = &errors.RunRequirementError{ ErrorMsg: "you must log in to use this command", SuggestionsMsg: "Log in with `confluent login`.\n" + signupSuggestion, @@ -667,7 +671,9 @@ func (c *Config) CheckIsCloudLogin() error { } if c.isContextStatePresent() && c.isOrgSuspended() { - if c.isLoginBlockedByOrgSuspension() { + if c.isOrgPauseTrialSuspension() { + return RequireCloudLoginPauseTrialOrgUnsuspendedErr + } else if c.isLoginBlockedByOrgSuspension() { return RequireCloudLoginOrgUnsuspendedErr } else { return RequireCloudLoginFreeTrialEndedOrgUnsuspendedErr @@ -794,6 +800,10 @@ func (c *Config) isLoginBlockedByOrgSuspension() bool { return utils.IsLoginBlockedByOrgSuspension(c.Context().GetSuspensionStatus()) } +func (c *Config) isOrgPauseTrialSuspension() bool { + return utils.IsOrgPauseTrialSuspended(c.Context().GetSuspensionStatus()) +} + // Parse `--context` flag value into config struct // Call ParseFlagsIntoContext which handles environment and cluster flags func (c *Config) ParseFlagsIntoConfig(cmd *cobra.Command) error { diff --git a/pkg/errors/error_message.go b/pkg/errors/error_message.go index 5ec1198112..d2388d8e7f 100644 --- a/pkg/errors/error_message.go +++ b/pkg/errors/error_message.go @@ -10,6 +10,7 @@ const ( ByokKeyNotFoundSuggestions = "Ensure the self-managed key exists and has not been deleted, or register a new key via `confluent byok register`." EndOfFreeTrialErrorMsg = `organization "%s" has been suspended because your free trial has ended` EndOfFreeTrialSuggestions = "To continue using Confluent Cloud, please enter a credit card with `confluent billing payment update` or claim a promo code with `confluent billing promo add`. To enter payment via the UI, please go to https://confluent.cloud/login." + PauseTrialSuggestions = "To continue using Confluent Cloud, resume your paused organization at https://confluent.cloud/login." EnsureCpSixPlusSuggestions = "Ensure that you are running against MDS with CP 6.0+." ExactlyOneSetErrorMsg = "exactly one of %v must be set" ListResourceSuggestions = "List available %s with `%s list`." diff --git a/pkg/utils/org_utils.go b/pkg/utils/org_utils.go index 7479bd82c0..cfa8af7145 100644 --- a/pkg/utils/org_utils.go +++ b/pkg/utils/org_utils.go @@ -14,7 +14,14 @@ func IsOrgEndOfFreeTrialSuspended(suspensionStatus *ccloudv1.SuspensionStatus) b return IsOrgSuspended(suspensionStatus) && eventType == ccloudv1.SuspensionEventType_SUSPENSION_EVENT_END_OF_FREE_TRIAL } +func IsOrgPauseTrialSuspended(suspensionStatus *ccloudv1.SuspensionStatus) bool { + eventType := suspensionStatus.GetEventType() + return IsOrgSuspended(suspensionStatus) && eventType == ccloudv1.SuspensionEventType_SUSPENSION_EVENT_PAUSE_TRIAL +} + func IsLoginBlockedByOrgSuspension(suspensionStatus *ccloudv1.SuspensionStatus) bool { eventType := suspensionStatus.GetEventType() - return IsOrgSuspended(suspensionStatus) && eventType != ccloudv1.SuspensionEventType_SUSPENSION_EVENT_END_OF_FREE_TRIAL + return IsOrgSuspended(suspensionStatus) && + eventType != ccloudv1.SuspensionEventType_SUSPENSION_EVENT_END_OF_FREE_TRIAL && + eventType != ccloudv1.SuspensionEventType_SUSPENSION_EVENT_PAUSE_TRIAL } diff --git a/pkg/utils/org_utils_test.go b/pkg/utils/org_utils_test.go new file mode 100644 index 0000000000..d779d9cfae --- /dev/null +++ b/pkg/utils/org_utils_test.go @@ -0,0 +1,105 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" + + ccloudv1 "github.com/confluentinc/ccloud-sdk-go-v1-public" +) + +func TestIsOrgPauseTrialSuspended(t *testing.T) { + tests := []struct { + name string + suspensionStatus *ccloudv1.SuspensionStatus + expected bool + }{ + { + name: "PAUSE_TRIAL event should be pause trial", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_COMPLETED, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_PAUSE_TRIAL, + }, + expected: true, + }, + { + name: "END_OF_FREE_TRIAL event should not be pause trial", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_COMPLETED, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_END_OF_FREE_TRIAL, + }, + expected: false, + }, + { + name: "CUSTOMER_INITIATED_ORG_DEACTIVATION should not be pause trial", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_COMPLETED, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_CUSTOMER_INITIATED_ORG_DEACTIVATION, + }, + expected: false, + }, + { + name: "Not suspended should not be pause trial", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_UNKNOWN, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_PAUSE_TRIAL, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := IsOrgPauseTrialSuspended(tt.suspensionStatus) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestIsLoginBlockedByOrgSuspension(t *testing.T) { + tests := []struct { + name string + suspensionStatus *ccloudv1.SuspensionStatus + expected bool + }{ + { + name: "PAUSE_TRIAL should not block login", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_COMPLETED, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_PAUSE_TRIAL, + }, + expected: false, + }, + { + name: "END_OF_FREE_TRIAL should not block login", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_COMPLETED, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_END_OF_FREE_TRIAL, + }, + expected: false, + }, + { + name: "CUSTOMER_INITIATED_ORG_DEACTIVATION should block login", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_COMPLETED, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_CUSTOMER_INITIATED_ORG_DEACTIVATION, + }, + expected: true, + }, + { + name: "Not suspended should not block login", + suspensionStatus: &ccloudv1.SuspensionStatus{ + Status: ccloudv1.SuspensionStatusType_SUSPENSION_UNKNOWN, + EventType: ccloudv1.SuspensionEventType_SUSPENSION_EVENT_CUSTOMER_INITIATED_ORG_DEACTIVATION, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := IsLoginBlockedByOrgSuspension(tt.suspensionStatus) + require.Equal(t, tt.expected, result) + }) + } +}