Skip to content

Commit

Permalink
Merge pull request #23951 from r-vasquez/change-expiry
Browse files Browse the repository at this point in the history
[DEVEX-36] rpk: change expiry check for free_trial
  • Loading branch information
r-vasquez authored Oct 30, 2024
2 parents cc888d0 + 663291e commit 8211880
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 17 deletions.
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ use_repo(
"com_github_hashicorp_go_multierror",
"com_github_kballard_go_shellquote",
"com_github_kr_pretty",
"com_github_kr_text",
"com_github_lestrrat_go_jwx",
"com_github_linkedin_goavro_v2",
"com_github_lorenzosaino_go_sysctl",
Expand Down
1 change: 1 addition & 0 deletions src/go/rpk/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ require (
github.com/hamba/avro/v2 v2.25.2
github.com/hashicorp/go-multierror v1.1.1
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kr/text v0.2.0
github.com/lestrrat-go/jwx v1.2.30
github.com/linkedin/goavro/v2 v2.13.0
github.com/lorenzosaino/go-sysctl v0.3.1
Expand Down
1 change: 1 addition & 0 deletions src/go/rpk/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
Expand Down
2 changes: 2 additions & 0 deletions src/go/rpk/pkg/adminapi/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ go_library(
"//src/go/rpk/pkg/config",
"//src/go/rpk/pkg/oauth",
"//src/go/rpk/pkg/oauth/providers/auth0",
"@com_github_kr_text//:text",
"@com_github_moby_term//:term",
"@com_github_redpanda_data_common_go_rpadmin//:rpadmin",
"@com_github_spf13_afero//:afero",
"@org_uber_go_zap//:zap",
Expand Down
45 changes: 37 additions & 8 deletions src/go/rpk/pkg/adminapi/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ import (
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/kr/text"
mTerm "github.com/moby/term"

"go.uber.org/zap"

"github.com/redpanda-data/common-go/rpadmin"
Expand Down Expand Up @@ -137,22 +141,31 @@ func licenseFeatureChecks(ctx context.Context, fs afero.Fs, cl *rpadmin.AdminAPI
// violation. (we only save successful responses).
// 2. LicenseStatus was last checked more than 1 hour ago.
if p.LicenseCheck == nil || p.LicenseCheck != nil && time.Unix(p.LicenseCheck.LastUpdate, 0).Add(1*time.Hour).Before(time.Now()) {
resp, err := cl.GetEnterpriseFeatures(ctx)
featResp, err := cl.GetEnterpriseFeatures(ctx)
if err != nil {
zap.L().Sugar().Warnf("unable to check licensed enterprise features in the cluster: %v", err)
return ""
}
info, err := cl.GetLicenseInfo(ctx)
if err != nil {
zap.L().Sugar().Warnf("unable to check license information: %v", err)
return ""
}
// We don't write a profile if the config doesn't exist.
y, exists := p.ActualConfig()
var licenseCheck *config.LicenseStatusCache
if resp.Violation {
var features []string
for _, f := range resp.Features {
if f.Enabled {
features = append(features, f.Name)
}
var enabledFeatures []string
for _, f := range featResp.Features {
if f.Enabled {
enabledFeatures = append(enabledFeatures, f.Name)
}
msg = fmt.Sprintf("\nWARNING: The following Enterprise features are being used in your Redpanda cluster: %v. These features require a license. To get a license, contact us at https://www.redpanda.com/contact. For more information, see https://docs.redpanda.com/current/get-started/licenses/#redpanda-enterprise-edition\n", features)
}
isTrialCheck := isTrialAboutToExpire(info, enabledFeatures)

if featResp.Violation {
msg = fmt.Sprintf("\nWARNING: The following Enterprise features are being used in your Redpanda cluster: %v. These features require a license. To get a license, contact us at https://www.redpanda.com/contact. For more information, see https://docs.redpanda.com/current/get-started/licenses/#redpanda-enterprise-edition\n", enabledFeatures)
} else if isTrialCheck {
msg = fmt.Sprintf("\nWARNING: your TRIAL license is about to expire. The following Enterprise features are being used in your Redpanda cluster: %v. These features require a license. To get a license, contact us at https://www.redpanda.com/contact. For more information, see https://docs.redpanda.com/current/get-started/licenses/#redpanda-enterprise-edition\n", enabledFeatures)
} else {
licenseCheck = &config.LicenseStatusCache{
LastUpdate: time.Now().Unix(),
Expand All @@ -168,5 +181,21 @@ func licenseFeatureChecks(ctx context.Context, fs afero.Fs, cl *rpadmin.AdminAPI
}
}
}
if ws, err := mTerm.GetWinsize(0); err == nil {
// text.Wrap removes the newline from the text. We add it back.
msg = "\n" + text.Wrap(msg, int(ws.Width)) + "\n"
}
return msg
}

// isTrialAboutToExpire returns true if we have a loaded free_trial license that
// expires in less than 15 days, and we have enterprise features enabled.
func isTrialAboutToExpire(info rpadmin.License, enabledFeatures []string) bool {
if len(enabledFeatures) > 0 && info.Loaded && strings.EqualFold(info.Properties.Type, "free_trial") {
ut := time.Unix(info.Properties.Expires, 0)
daysLeft := int(time.Until(ut).Hours() / 24)

return daysLeft < 15 && !ut.Before(time.Now())
}
return false
}
42 changes: 36 additions & 6 deletions src/go/rpk/pkg/adminapi/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package adminapi

import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -16,7 +17,7 @@ func Test_licenseFeatureChecks(t *testing.T) {
tests := []struct {
name string
prof *config.RpkProfile
responseCase string // See the mapLicenseResponses below.
responseCase string // See the mapLicenseFeatureResponses below.
expContain string
withErr bool
checkCache func(t *testing.T, before int64, after int64)
Expand All @@ -27,6 +28,18 @@ func Test_licenseFeatureChecks(t *testing.T) {
responseCase: "ok",
expContain: "",
},
{
name: "free_trial about to expire, no features",
prof: &config.RpkProfile{},
responseCase: "ok-free",
expContain: "",
},
{
name: "free_trial about to expire, with features",
prof: &config.RpkProfile{},
responseCase: "ok-features",
expContain: "WARNING: your TRIAL license is about to expire",
},
{
name: "license ok, cache valid",
prof: &config.RpkProfile{LicenseCheck: &config.LicenseStatusCache{LastUpdate: time.Now().Add(20 * time.Minute).Unix()}},
Expand Down Expand Up @@ -144,16 +157,33 @@ type response struct {
body string
}

var mapLicenseResponses = map[string]response{
"ok": {http.StatusOK, `{"license_status": "valid", "violation": false}`},
var mapLicenseFeatureResponses = map[string]response{
"ok": {http.StatusOK, `{"license_status": "valid", "violation": false, "features": [{"name": "fips", "enabled": true},{"name": "partition_auto_balancing_continuous", "enabled": false}]}`},
"inViolation": {http.StatusOK, `{"license_status": "expired", "violation": true, "features": [{"name": "partition_auto_balancing_continuous", "enabled": true}]}`},
"failedRequest": {http.StatusBadRequest, ""},
"ok-free": {http.StatusOK, `{"license_status": "valid", "violation": false}`},
"ok-features": {http.StatusOK, `{"license_status": "valid", "violation": false, "features": [{"name": "partition_auto_balancing_continuous", "enabled": true}]}`},
}

var mapLicenseInfoResponses = map[string]response{
"ok": {http.StatusOK, fmt.Sprintf(`{"loaded": true, "license": {"type": "enterprise", "expires": %d}}`, time.Now().Add(60*24*time.Hour).Unix())},
"inViolation": {http.StatusOK, fmt.Sprintf(`{"loaded": true, "license": {"type": "enterprise", "expires": %d}}`, time.Now().Add(60*24*time.Hour).Unix())},
"failedRequest": {http.StatusBadRequest, ""},
"ok-free": {http.StatusOK, fmt.Sprintf(`{"loaded": true, "license": {"type": "free_trial", "expires": %d}}`, time.Now().Add(24*time.Hour).Unix())}, // expires in 1 day.
"ok-features": {http.StatusOK, fmt.Sprintf(`{"loaded": true, "license": {"type": "free_trial", "expires": %d}}`, time.Now().Add(24*time.Hour).Unix())},
}

func licenseHandler(respCase string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
resp := mapLicenseResponses[respCase]
w.WriteHeader(resp.status)
w.Write([]byte(resp.body))
fmt.Println(r.URL.Path)
if r.URL.Path == "/v1/features/enterprise" {
resp := mapLicenseFeatureResponses[respCase]
w.WriteHeader(resp.status)
w.Write([]byte(resp.body))
} else if r.URL.Path == "/v1/features/license" {
resp := mapLicenseInfoResponses[respCase]
w.WriteHeader(resp.status)
w.Write([]byte(resp.body))
}
}
}
11 changes: 8 additions & 3 deletions src/go/rpk/pkg/cli/cluster/license/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package license
import (
"fmt"
"os"
"strings"
"time"

"github.com/redpanda-data/common-go/rpadmin"
Expand Down Expand Up @@ -133,17 +134,21 @@ func printTextLicenseInfo(resp infoResponse) {
if *resp.Expired {
tw.Print("License expired:", *resp.Expired)
}
checkLicenseExpiry(resp.ExpiresUnix)
checkLicenseExpiry(resp.ExpiresUnix, resp.Type)
}
out.Section("LICENSE INFORMATION")
tw.Flush()
}

func checkLicenseExpiry(expiresUnix int64) {
func checkLicenseExpiry(expiresUnix int64, licenseType string) {
ut := time.Unix(expiresUnix, 0)
daysLeft := int(time.Until(ut).Hours() / 24)

if daysLeft < 30 && !ut.Before(time.Now()) {
dayThreshold := 30
if strings.EqualFold(licenseType, "free_trial") {
dayThreshold = 15
}
if daysLeft < dayThreshold && !ut.Before(time.Now()) {
fmt.Fprintf(os.Stderr, "WARNING: your license will expire soon.\n\n")
}
}

0 comments on commit 8211880

Please sign in to comment.