Skip to content

Commit

Permalink
feature: Serve the current license status on the CDS server
Browse files Browse the repository at this point in the history
  • Loading branch information
rg0now committed Feb 7, 2025
1 parent ed7fdca commit a80c89d
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 24 deletions.
19 changes: 15 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.23.4
require (
github.com/go-logr/logr v1.4.2
github.com/go-logr/zapr v1.3.0
github.com/l7mp/stunner v1.0.1-0.20250128211951-d74a5a1130a2
github.com/l7mp/stunner v1.0.1-0.20250207173130-50227810304a
github.com/onsi/ginkgo/v2 v2.21.0
github.com/onsi/gomega v1.35.1
github.com/stretchr/testify v1.10.0
Expand All @@ -26,6 +26,7 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
Expand Down Expand Up @@ -64,19 +65,26 @@ require (
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/oapi-codegen/oapi-codegen/v2 v2.4.0 // indirect
github.com/oapi-codegen/runtime v1.1.1 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/dtls/v3 v3.0.4 // indirect
github.com/pion/logging v0.2.3 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v4 v4.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.61.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
Expand All @@ -88,19 +96,22 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.32.0 // indirect
k8s.io/cli-runtime v0.32.0 // indirect
Expand Down
112 changes: 112 additions & 0 deletions go.sum

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion internal/config/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ func (c *Server) ProcessUpdate(e *event.EventUpdate) error {
e.String())

configs := []cdsserver.Config{}

for _, conf := range e.ConfigQueue {
id := conf.Admin.Name
c.log.V(4).Info("Config update", "generation", e.Generation, "client", id, "config",
Expand All @@ -86,5 +85,7 @@ func (c *Server) ProcessUpdate(e *event.EventUpdate) error {
return err
}

c.Server.UpdateLicenseStatus(e.LicenseStatus)

return nil
}
25 changes: 25 additions & 0 deletions internal/config/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,39 @@ func TestConfigDiscovery(t *testing.T) {
c1s := cStore.Get("ns/gw1")
assert.True(t, c1s.DeepEqual(c1Ok), "config ok")

// both clients should return a nil license status
status, err := cdsc1.LicenseStatus(ctx)
assert.NoError(t, err, "loading status 1 ok")
assert.Equal(t, stnrv1.NewEmptyLicenseStatus(), status, "license 1 ok")

status, err = cdsc2.LicenseStatus(ctx)
assert.NoError(t, err, "loading status 2 ok")
assert.Equal(t, stnrv1.NewEmptyLicenseStatus(), status, "license 2 ok")

log.Info("creating a config for the 2nd client", "id", "ns/gw2")
c2Ok := zeroConfig("ns", "gw2", "realm2")
e = event.NewEventUpdate(0)
e.ConfigQueue = []*stnrv1.StunnerConfig{c1Ok, c2Ok}
licenseStatus := stnrv1.LicenseStatus{
EnabledFeatures: []string{"a", "b", "c"},
SubscriptionType: "test",
ValidUntil: "forever",
LastError: "",
}
e.LicenseStatus = licenseStatus
ch <- e

time.Sleep(50 * time.Millisecond)

// both clients should return the new license status
status, err = cdsc1.LicenseStatus(ctx)
assert.NoError(t, err, "loading status 1 ok")
assert.Equal(t, licenseStatus, status, "license 1 ok")

status, err = cdsc2.LicenseStatus(ctx)
assert.NoError(t, err, "loading status 2 ok")
assert.Equal(t, licenseStatus, status, "license 2 ok")

c1, err = cdsc1.Load()
assert.NoError(t, err, "loading client 1 config ok")
assert.NotNil(t, c1)
Expand Down
26 changes: 15 additions & 11 deletions internal/event/event_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ type UpdateConf struct {
}

type EventUpdate struct {
Type EventType
UpsertQueue UpdateConf
DeleteQueue UpdateConf
ConfigQueue ConfigConf
Generation int
RequestAck bool
Type EventType
UpsertQueue UpdateConf
DeleteQueue UpdateConf
ConfigQueue ConfigConf
LicenseStatus stnrv1.LicenseStatus
Generation int
RequestAck bool
}

// NewEvent returns an empty event
Expand All @@ -54,9 +55,10 @@ func NewEventUpdate(generation int) *EventUpdate {
Deployments: store.NewDeploymentStore(),
DaemonSets: store.NewDaemonSetStore(),
},
ConfigQueue: []*stnrv1.StunnerConfig{},
Generation: generation,
RequestAck: false,
ConfigQueue: []*stnrv1.StunnerConfig{},
LicenseStatus: stnrv1.NewEmptyLicenseStatus(),
Generation: generation,
RequestAck: false,
}
}

Expand All @@ -65,11 +67,11 @@ func (e *EventUpdate) GetType() EventType {
}

func (e *EventUpdate) String() string {
return fmt.Sprintf("%s (gen: %d, ack: %t): upsert-queue: gway-cls: %d, gway: %d, "+
return fmt.Sprintf("%s (gen: %d, ack: %t, license: %s): upsert-queue: gway-cls: %d, gway: %d, "+
"route: %d, routeV1A2: %d, svc: %d, confmap: %d, dp: %d, ds: %d / "+
"delete-queue: gway-cls: %d, gway: %d, route: %d, routeV1A2: %d, "+
"svc: %d, confmap: %d, dp: %d, ds: %d / config-queue: %d",
e.Type.String(), e.Generation, e.RequestAck,
e.Type.String(), e.Generation, e.RequestAck, e.LicenseStatus.String(),
e.UpsertQueue.GatewayClasses.Len(), e.UpsertQueue.Gateways.Len(),
e.UpsertQueue.UDPRoutes.Len(), e.UpsertQueue.UDPRoutesV1A2.Len(),
e.UpsertQueue.Services.Len(), e.UpsertQueue.ConfigMaps.Len(),
Expand Down Expand Up @@ -106,6 +108,8 @@ func (e *EventUpdate) DeepCopy() *EventUpdate {
u.DeleteQueue.Deployments = q.Deployments.DeepCopy()
u.DeleteQueue.DaemonSets = q.DaemonSets.DeepCopy()

u.LicenseStatus = e.LicenseStatus

u.ConfigQueue = make([]*stnrv1.StunnerConfig, len(e.ConfigQueue))
copy(u.ConfigQueue, e.ConfigQueue)

Expand Down
4 changes: 3 additions & 1 deletion internal/licensemanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

var licenseManagerConstructor = NewStubManager

// Manager is a global license manager that encapsulates the license management logics
// Manager is a global license manager that encapsulates the license management logics.
type Manager interface {
// Start runs the license manager.
Start(context.Context) error
Expand All @@ -28,6 +28,8 @@ type Manager interface {
SetOperatorChannel(c chan event.Event)
// LastError returns the last license manager error.
LastError() error
// Status returns the current licensing status.
Status() stnrv1.LicenseStatus
}

func NewManager(key string, logger logr.Logger) Manager {
Expand Down
11 changes: 6 additions & 5 deletions internal/licensemanager/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ type stubMgr struct{}

func NewStubManager(_ string, _ logr.Logger) Manager { return &stubMgr{} }

func (_ *stubMgr) Start(_ context.Context) error { return nil }
func (_ *stubMgr) Validate(_ licensecfg.Feature) bool { return true }
func (_ *stubMgr) SubscriptionType() licensecfg.SubscriptionType {
return licensecfg.SubscriptionTypeFree
}
func (_ *stubMgr) Start(_ context.Context) error { return nil }
func (_ *stubMgr) Validate(_ licensecfg.Feature) bool { return true }
func (_ *stubMgr) Status() stnrv1.LicenseStatus { return stnrv1.NewEmptyLicenseStatus() }
func (_ *stubMgr) LastError() error { return nil }
func (_ *stubMgr) SetOperatorChannel(_ chan event.Event) {}
func (_ *stubMgr) GenerateLicenseConfig() (stnrv1.LicenseConfig, error) {
return stnrv1.LicenseConfig{}, nil
}
func (_ *stubMgr) SubscriptionType() licensecfg.SubscriptionType {
return licensecfg.NewNilSubscriptionType()
}
5 changes: 4 additions & 1 deletion internal/renderer/render_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ func NewRenderContext(r *DefaultRenderer, gc *gwapiv1.GatewayClass) *RenderConte
if gc != nil {
logger = r.log.WithValues("gateway-class", gc.GetName())
}
update := event.NewEventUpdate(r.gen)
update.LicenseStatus = r.licmgr.Status()

return &RenderContext{
update: event.NewEventUpdate(r.gen),
update: update,
gc: gc,
gws: store.NewGatewayStore(),
log: logger,
Expand Down
4 changes: 3 additions & 1 deletion internal/renderer/render_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,11 @@ func (r *DefaultRenderer) renderManagedGateways(e *event.EventRender) {
pipelineCtx.Merge(gcCtx)
}

// updates must be acknowledged to the operator by the updater
u := pipelineCtx.update.DeepCopy()

// updates must be acknowledged to the operator by the updater
u.SetRequestAck(true)

r.operatorCh <- u
}

Expand Down
8 changes: 8 additions & 0 deletions test/managed_mode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"context"
"fmt"
"reflect"
"time"

// "reflect"
Expand Down Expand Up @@ -124,6 +125,13 @@ func testManagedMode() {
"GatewayClass namespace")
})

It("should return a default licensing status", func() {
Eventually(func() bool {
status, err := cdsClient.LicenseStatus(ctx)
return err == nil && reflect.DeepEqual(status, stnrv1.NewEmptyLicenseStatus())
}, timeout, interval).Should(BeTrue())
})

It("should render a STUNner config with exactly 2 listeners", func() {
ctrl.Log.Info("trying to load STUNner config")
Eventually(checkConfig(ch, func(c *stnrv1.StunnerConfig) bool {
Expand Down

0 comments on commit a80c89d

Please sign in to comment.