Skip to content

Handle enterprise license expiry with grace period#4482

Open
hjiawei wants to merge 2 commits intotigera:masterfrom
hjiawei:relax-lic-expiry
Open

Handle enterprise license expiry with grace period#4482
hjiawei wants to merge 2 commits intotigera:masterfrom
hjiawei:relax-lic-expiry

Conversation

@hjiawei
Copy link
Contributor

@hjiawei hjiawei commented Feb 27, 2026

Description

When an enterprise license expires (after the grace period elapses),
disable logging (fluentd DaemonSet) and metrics scraping (ServiceMonitors)
while keeping the dataplane running. During the grace period, all
components continue running normally with only a log warning. This aligns
with the enterprise changes that relax license validation so expired
licenses are accepted, with individual components handling degraded behavior.

Changes:

  • Add license utility functions in pkg/controller/utils/license.go:

    • ParseGracePeriod: parses the "90d" format grace period string from
      the typed LicenseKey status.gracePeriod field
    • GetLicenseStatus: point-in-time check using a single time.Now() call
      returning Valid, InGracePeriod, or Expired status
    • Move existing license helpers (FetchLicenseKey, IsFeatureActive,
      WaitToAddLicenseKeyWatch) out of utils.go into license.go
  • Monitor controller:

    • Add license watch and fetch with grace period support
    • When expired: set degraded status, move ServiceMonitors to toDelete
    • When in grace period: log warning, keep everything running normally
  • LogCollector controller:

    • Add grace period support to license expiry check
    • When expired: set degraded status, move fluentd DaemonSet to toDelete
    • When in grace period: log warning, keep everything running normally
  • Render layer:

    • Add LicenseExpired flag to monitor Config and FluentdConfiguration
    • Conditionally move ServiceMonitors/DaemonSet to toDelete when expired
  • Bump tigera/api to v0.0.0-20260227222130-df0b9e289a34 which includes
    the GracePeriod field in LicenseKeyStatus

Release Note

TBD

For PR author

  • Tests for change.
  • If changing pkg/apis/, run make gen-files
  • If changing versions, run make gen-versions

For PR reviewers

A note for code reviewers - all pull requests must have the following:

  • Milestone set according to targeted release.
  • Appropriate labels:
    • kind/bug if this is a bugfix.
    • kind/enhancement if this is a a new feature.
    • enterprise if this PR applies to Calico Enterprise only.

@marvin-tigera marvin-tigera added this to the v1.42.0 milestone Feb 27, 2026
@hjiawei hjiawei force-pushed the relax-lic-expiry branch 8 times, most recently from c8361f1 to 5f6f096 Compare February 27, 2026 23:12
@hjiawei hjiawei marked this pull request as ready for review February 27, 2026 23:12
@hjiawei hjiawei requested a review from a team as a code owner February 27, 2026 23:12
@hjiawei hjiawei requested a review from Copilot February 27, 2026 23:14
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements enterprise license expiry handling with a grace period. When a license expires, the monitor controller disables metrics scraping (ServiceMonitors) and the log collector controller disables log forwarding (fluentd DaemonSet), while keeping the dataplane running. During the grace period, all components continue running normally with only a log warning. The PR also includes a minor bug fix to SetDegraded that previously produced malformed messages (trailing : ) when err was nil.

Changes:

  • New pkg/controller/utils/license.go with ParseGracePeriod, GetLicenseStatus, and LicenseStatus type, plus moved existing license helpers from utils.go
  • Monitor controller and render layer updated to watch license, handle expiry/grace period, and conditionally delete ServiceMonitors
  • LogCollector controller and fluentd render layer updated to handle expiry/grace period and conditionally delete the fluentd DaemonSet
  • Dependency bumps: tigera/api (adds GracePeriod field to LicenseKeyStatus), Kubernetes libraries to v0.34.4, and associated build tooling

Reviewed changes

Copilot reviewed 16 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/controller/utils/license.go New file with license status types, parsing, and moved license helpers
pkg/controller/utils/license_test.go Tests for ParseGracePeriod and GetLicenseStatus
pkg/controller/utils/utils.go Removed license helper functions moved to license.go
pkg/controller/monitor/monitor_controller.go Added license watch, expiry check, grace period requeue, and LicenseExpired flag to render config
pkg/controller/monitor/monitor_controller_test.go Tests for license expiry, valid license, and grace period scenarios
pkg/render/monitor/monitor.go Added LicenseExpired flag to Config; ServiceMonitors conditionally moved to toDelete
pkg/render/monitor/monitor_test.go Tests for ServiceMonitor placement based on LicenseExpired flag
pkg/render/fluentd.go Added LicenseExpired flag to FluentdConfiguration; DaemonSet conditionally moved to toDelete
pkg/render/fluentd_test.go Tests for DaemonSet placement based on LicenseExpired flag
pkg/controller/status/status.go Fixed SetDegraded to omit trailing : when err is nil
pkg/controller/status/status_test.go Updated test expectation to match fixed message format
pkg/controller/logcollector/logcollector_controller.go Added grace period check, expiry handling, and LicenseExpired flag to fluentd config
pkg/controller/logcollector/logcollector_controller_test.go Added RemoveDaemonsets mock; added expiry and grace period tests
go.mod, go.sum, api/go.mod, api/go.sum Dependency version bumps
Makefile Build image version bumps consistent with k8s dependency updates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

When an enterprise license expires (after the grace period elapses),
disable logging (fluentd DaemonSet) and metrics scraping (ServiceMonitors)
while keeping the dataplane running. During the grace period, all
components continue running normally with only a log warning. This aligns
with the enterprise changes that relax license validation so expired
licenses are accepted, with individual components handling degraded behavior.

Changes:
- Add license utility functions in pkg/controller/utils/license.go:
  - ParseGracePeriod: parses the "90d" format grace period string from
    the typed LicenseKey status.gracePeriod field
  - GetLicenseStatus: point-in-time check using a single time.Now() call
    returning Valid, InGracePeriod, or Expired status
  - Move existing license helpers (FetchLicenseKey, IsFeatureActive,
    WaitToAddLicenseKeyWatch) out of utils.go into license.go

- Monitor controller:
  - Add license watch and fetch with grace period support
  - When expired: set degraded status, move ServiceMonitors to toDelete
  - When in grace period: log warning, keep everything running normally

- LogCollector controller:
  - Add grace period support to license expiry check
  - When expired: set degraded status, move fluentd DaemonSet to toDelete
  - When in grace period: log warning, keep everything running normally

- Render layer:
  - Add LicenseExpired flag to monitor Config and FluentdConfiguration
  - Conditionally move ServiceMonitors/DaemonSet to toDelete when expired

- Bump tigera/api to v0.0.0-20260227222130-df0b9e289a34 which includes
  the GracePeriod field in LicenseKeyStatus

// Should requeue to re-reconcile when the grace period expires.
Expect(result.RequeueAfter).To(BeNumerically(">", 0))
Expect(result.RequeueAfter).To(BeNumerically("~", 89*24*time.Hour, 1*time.Hour))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL "~" comparator can be used here.

if gracePeriod == "" {
return 0
}
s := strings.TrimSuffix(gracePeriod, "d")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't be able to deal with durations that don't use d as a suffix, I think it is better to solve for that as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bare number without unit d is now treated as invalid grace period.

Comment on lines 46 to 48
Entry("invalid string", "abc", time.Duration(0)),
Entry("negative days", "-5d", time.Duration(0)),
Entry("number without d suffix", "90", 90*24*time.Hour),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these seem invalid to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case has been updated to require the d unit suffix and no longer accepts bare numbers without a unit.

ParseGracePeriod now requires the "d" suffix and rejects bare integers
or other formats. This makes the parser stricter and aligned with the
license controller's output format (e.g. "90d"). Also guards against
negative values and uses strings.CutSuffix for cleaner suffix handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hjiawei hjiawei requested a review from rene-dekker February 28, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants