Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expiration date in resource annotations #1631

Merged
merged 10 commits into from
Feb 7, 2024
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
34 changes: 29 additions & 5 deletions fleetshard/pkg/central/reconciler/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/stackrox/rox/operator/apis/platform/v1alpha1"
"github.com/stackrox/rox/pkg/declarativeconfig"
"github.com/stackrox/rox/pkg/random"
"golang.org/x/exp/maps"
"gopkg.in/yaml.v2"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
Expand Down Expand Up @@ -62,6 +63,7 @@ const (
instanceTypeLabelKey = "rhacs.redhat.com/instance-type"
orgIDLabelKey = "rhacs.redhat.com/org-id"
tenantIDLabelKey = "rhacs.redhat.com/tenant"
centralExpiredAtKey = "rhacs.redhat.com/expired-at"

auditLogNotifierKey = "com.redhat.rhacs.auditLogNotifier"
auditLogNotifierName = "Platform Audit Logs"
Expand Down Expand Up @@ -200,7 +202,10 @@ func (r *CentralReconciler) Reconcile(ctx context.Context, remoteCentral private
namespaceAnnotations := map[string]string{
orgNameAnnotationKey: remoteCentral.Spec.Auth.OwnerOrgName,
}
if err := r.ensureNamespaceExists(remoteCentralNamespace, namespaceLabels, namespaceAnnotations); err != nil {
if remoteCentral.Metadata.ExpiredAt != nil {
namespaceAnnotations[centralExpiredAtKey] = remoteCentral.Metadata.ExpiredAt.Format(time.RFC3339)
}
if err := r.reconcileNamespace(ctx, remoteCentralNamespace, namespaceLabels, namespaceAnnotations); err != nil {
return nil, errors.Wrapf(err, "unable to ensure that namespace %s exists", remoteCentralNamespace)
}

Expand Down Expand Up @@ -309,11 +314,11 @@ func (r *CentralReconciler) applyCentralConfig(remoteCentral *private.ManagedCen
r.applyRoutes(central)
r.applyProxyConfig(central)
r.applyDeclarativeConfig(central)
r.applyAnnotations(central)
r.applyAnnotations(remoteCentral, central)
return nil
}

func (r *CentralReconciler) applyAnnotations(central *v1alpha1.Central) {
func (r *CentralReconciler) applyAnnotations(remoteCentral *private.ManagedCentral, central *v1alpha1.Central) {
if central.Spec.Customize == nil {
central.Spec.Customize = &v1alpha1.CustomizeSpec{}
}
Expand All @@ -322,6 +327,9 @@ func (r *CentralReconciler) applyAnnotations(central *v1alpha1.Central) {
}
central.Spec.Customize.Annotations[envAnnotationKey] = r.environment
central.Spec.Customize.Annotations[clusterNameAnnotationKey] = r.clusterName
if remoteCentral.Metadata.ExpiredAt != nil {
central.Spec.Customize.Annotations[centralExpiredAtKey] = remoteCentral.Metadata.ExpiredAt.Format(time.RFC3339)
}
}

func (r *CentralReconciler) applyDeclarativeConfig(central *v1alpha1.Central) {
Expand Down Expand Up @@ -668,6 +676,13 @@ func (r *CentralReconciler) reconcileCentral(ctx context.Context, remoteCentral
centralExists = false
}

if remoteCentral.Metadata.ExpiredAt != nil {
if central.GetAnnotations() == nil {
central.Annotations = map[string]string{}
}
central.Annotations[centralExpiredAtKey] = remoteCentral.Metadata.ExpiredAt.Format(time.RFC3339)
}

if !centralExists {
if central.GetAnnotations() == nil {
central.Annotations = map[string]string{}
Expand Down Expand Up @@ -1170,15 +1185,24 @@ func (r *CentralReconciler) createTenantNamespace(ctx context.Context, namespace
return nil
}

func (r *CentralReconciler) ensureNamespaceExists(name string, labels map[string]string, annotations map[string]string) error {
func (r *CentralReconciler) reconcileNamespace(ctx context.Context, name string, labels map[string]string, annotations map[string]string) error {
namespace, err := r.getNamespace(name)
if err != nil {
if apiErrors.IsNotFound(err) {
namespace.Annotations = annotations
namespace.Labels = labels
return r.createTenantNamespace(context.Background(), namespace)
return r.createTenantNamespace(ctx, namespace)
}
return fmt.Errorf("getting namespace %s: %w", name, err)
} else if !maps.Equal(labels, namespace.Labels) ||
!maps.Equal(annotations, namespace.Annotations) {
namespace.Annotations = annotations
namespace.Labels = labels
if err = r.client.Update(ctx, namespace, &ctrlClient.UpdateOptions{
FieldManager: "fleetshard-sync",
}); err != nil {
return fmt.Errorf("updating namespace %s: %w", name, err)
}
}
return nil
}
Expand Down
9 changes: 8 additions & 1 deletion fleetshard/pkg/central/reconciler/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2153,11 +2153,18 @@ func TestReconciler_applyAnnotations(t *testing.T) {
},
},
}
r.applyAnnotations(c)
date := time.Date(2024, 01, 01, 0, 0, 0, 0, time.UTC)
rc := &private.ManagedCentral{
Metadata: private.ManagedCentralAllOfMetadata{
ExpiredAt: &date,
},
}
r.applyAnnotations(rc, c)
assert.Equal(t, map[string]string{
"rhacs.redhat.com/environment": "test",
"rhacs.redhat.com/cluster-name": "test",
"foo": "bar",
"rhacs.redhat.com/expired-at": "2024-01-01T00:00:00Z",
}, c.Spec.Customize.Annotations)
}

Expand Down
1 change: 1 addition & 0 deletions internal/dinosaur/pkg/api/admin/private/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ paths:
name: reason
required: true
schema:
format: date-time
type: string
style: form
responses:
Expand Down
3 changes: 2 additions & 1 deletion internal/dinosaur/pkg/api/admin/private/api_default.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/dinosaur/pkg/api/private/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,10 @@ components:
additionalProperties:
type: string
type: object
expired-at:
format: date-time
nullable: true
type: string
ManagedCentral_allOf_spec_auth:
properties:
clientSecret:
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/dinosaur/pkg/presenters/managedcentral.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func (c *ManagedCentralPresenter) presentManagedCentral(gitopsConfig gitops.Conf
},
Internal: from.Internal,
SecretsStored: getSecretNames(from), // pragma: allowlist secret
ExpiredAt: from.ExpiredAt,
},
Spec: private.ManagedCentralAllOfSpec{
Owners: []string{
Expand Down
13 changes: 9 additions & 4 deletions internal/dinosaur/pkg/services/dinosaur.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,14 @@ type dinosaurService struct {
amsClient ocm.AMSClient
iamConfig *iam.IAMConfig
rhSSODynamicClientsAPI *dynamicClientAPI.AcsTenantsApiService
telemetry *Telemetry
}

// NewDinosaurService ...
func NewDinosaurService(connectionFactory *db.ConnectionFactory, clusterService ClusterService,
iamConfig *iam.IAMConfig, dinosaurConfig *config.CentralConfig, dataplaneClusterConfig *config.DataplaneClusterConfig, awsConfig *config.AWSConfig,
quotaServiceFactory QuotaServiceFactory, awsClientFactory aws.ClientFactory,
clusterPlacementStrategy ClusterPlacementStrategy, amsClient ocm.AMSClient) DinosaurService {
clusterPlacementStrategy ClusterPlacementStrategy, amsClient ocm.AMSClient, telemetry *Telemetry) DinosaurService {
return &dinosaurService{
connectionFactory: connectionFactory,
clusterService: clusterService,
Expand All @@ -148,6 +149,7 @@ func NewDinosaurService(connectionFactory *db.ConnectionFactory, clusterService
clusterPlacementStrategy: clusterPlacementStrategy,
amsClient: amsClient,
rhSSODynamicClientsAPI: dynamicclients.NewDynamicClientsAPI(iamConfig.RedhatSSORealm),
telemetry: telemetry,
}
}

Expand Down Expand Up @@ -689,7 +691,7 @@ func (k *dinosaurService) Update(dinosaurRequest *dbapi.CentralRequest) *errors.
if err := dbConn.Updates(dinosaurRequest).Error; err != nil {
return errors.NewWithCause(errors.ErrorGeneral, err, "Failed to update central")
}

k.telemetry.UpdateTenantProperties(dinosaurRequest)
return nil
}

Expand All @@ -702,7 +704,10 @@ func (k *dinosaurService) Updates(dinosaurRequest *dbapi.CentralRequest, fields
if err := dbConn.Updates(fields).Error; err != nil {
return errors.NewWithCause(errors.ErrorGeneral, err, "Failed to update central")
}

// Get all request properties, not only the ones provided with fields.
if dinosaurRequest, svcErr := k.GetByID(dinosaurRequest.ID); svcErr == nil {
k.telemetry.UpdateTenantProperties(dinosaurRequest)
}
return nil
}

Expand Down Expand Up @@ -750,7 +755,7 @@ func (k *dinosaurService) UpdateStatus(id string, status dinosaurConstants.Centr
if err := dbConn.Model(&dbapi.CentralRequest{Meta: api.Meta{ID: id}}).Updates(update).Error; err != nil {
return true, errors.NewWithCause(errors.ErrorGeneral, err, "Failed to update central status")
}

k.telemetry.UpdateTenantProperties(dinosaur)
return true, nil
}

Expand Down
55 changes: 36 additions & 19 deletions internal/dinosaur/pkg/services/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,32 +61,26 @@ func (t *Telemetry) enabled() bool {
return t != nil && t.config != nil && t.config.Enabled()
}

// setTenantProperties emits a group event that captures meta data of the input central instance.
// Adds the token user to the tenant group.
func (t *Telemetry) setTenantProperties(ctx context.Context, central *dbapi.CentralRequest) {
if !t.enabled() {
return
}

user, err := t.auth.getUserFromContext(ctx)
if err != nil {
glog.Error(errors.Wrap(err, "cannot get telemetry user from context claims"))
return
}
// getTenantProperties returns the tenant group properties map.
func (t *Telemetry) getTenantProperties(central *dbapi.CentralRequest) map[string]any {
props := map[string]any{
"Cloud Account": central.CloudAccountID,
"Cloud Provider": central.CloudProvider,
"Instance Type": central.InstanceType,
"Organisation ID": central.OrganisationID,
"Region": central.Region,
"Tenant ID": central.ID,
"Status": central.Status,
}
// Group call will issue a supporting Track event to force group properties
// update.
t.config.Telemeter().Group(props,
telemeter.WithUserID(user),
telemeter.WithGroups(TenantGroupName, central.ID),
)
if central.ExpiredAt != nil {
props["Expired At"] = central.ExpiredAt.UTC().Format(time.RFC3339)
} else {
// An instance may loose its expiration date after quota is granted, so
// we need to reset the group property, hence never report nil time, as
// nil is not a value on Amplitude.
props["Expired At"] = time.Time{}.Format(time.RFC3339)
}
return props
}

// trackCreationRequested emits a track event that signals the creation request of a Central instance.
Expand Down Expand Up @@ -123,7 +117,21 @@ func (t *Telemetry) trackCreationRequested(ctx context.Context, tenantID string,
// RegisterTenant initializes the tenant group with the associated properties
// and issues a following event tracking the central creation request.
func (t *Telemetry) RegisterTenant(ctx context.Context, convCentral *dbapi.CentralRequest, isAdmin bool, err error) {
t.setTenantProperties(ctx, convCentral)
user, err := t.auth.getUserFromContext(ctx)
if err != nil {
glog.Error(errors.Wrap(err, "cannot get telemetry user from context claims"))
return
}

props := t.getTenantProperties(convCentral)
// Adds the token user to the tenant group.
// Group call will issue a supporting Track event to force group properties
// update.
t.config.Telemeter().Group(props,
telemeter.WithUserID(user),
telemeter.WithGroups(TenantGroupName, convCentral.ID),
)

go func() {
// This is to raise the chances for the tenant group properties be
// procesed by Segment:
Expand All @@ -132,6 +140,15 @@ func (t *Telemetry) RegisterTenant(ctx context.Context, convCentral *dbapi.Centr
}()
}

// UpdateTenant updates tenant group properties.
func (t *Telemetry) UpdateTenantProperties(convCentral *dbapi.CentralRequest) {
props := t.getTenantProperties(convCentral)
// Update tenant group properties from the name of fleet-manager backend.
t.config.Telemeter().Group(props,
telemeter.WithGroups(TenantGroupName, convCentral.ID),
)
}

// TrackDeletionRequested emits a track event that signals the deletion request of a Central instance.
func (t *Telemetry) TrackDeletionRequested(ctx context.Context, tenantID string, isAdmin bool, requestErr error) {
if !t.enabled() {
Expand Down
1 change: 1 addition & 0 deletions openapi/fleet-manager-private-admin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ paths:
name: reason
schema:
type: string
format: date-time
required: true
security:
- Bearer: [ ]
Expand Down
4 changes: 4 additions & 0 deletions openapi/fleet-manager-private.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ components:
type: object
additionalProperties:
type: string
expired-at:
type: string
format: date-time
nullable: true
spec:
type: object
properties:
Expand Down
Loading