Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions approval/api/v1/approval_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type ApprovalSpec struct {
Strategy ApprovalStrategy `json:"strategy"`

// State defines the state of the approval
// +kubebuilder:validation:Enum=Pending;Semigranted;Granted;Rejected;Suspended
// +kubebuilder:validation:Enum=Pending;Semigranted;Granted;Rejected;Suspended;Expired
// +kubebuilder:default=Pending
State ApprovalState `json:"state"`

Expand All @@ -57,7 +57,7 @@ type ApprovalStatus struct {
AvailableTransitions AvailableTransitions `json:"availableTransitions,omitempty"`

// LastState defines the last state of the approval
// +kubebuilder:validation:Enum=Pending;Semigranted;Granted;Rejected;Suspended
// +kubebuilder:validation:Enum=Pending;Semigranted;Granted;Rejected;Suspended;Expired
// +kubebuilder:default=Pending
LastState ApprovalState `json:"lastState,omitempty"`

Expand Down
81 changes: 81 additions & 0 deletions approval/api/v1/approvalexpiration_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2025 Deutsche Telekom IT GmbH
//
// SPDX-License-Identifier: Apache-2.0

package v1

import (
"github.com/telekom/controlplane/common/pkg/types"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ApprovalExpirationSpec defines the desired state of ApprovalExpiration
type ApprovalExpirationSpec struct {
// Approval is a reference to the parent Approval resource
Approval types.ObjectRef `json:"approval"`

// Expiration is the absolute date when the approval expires
Expiration metav1.Time `json:"expiration"`

// WeeklyReminder is the date from which weekly reminders start
WeeklyReminder metav1.Time `json:"weeklyReminder"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we use the reminder logic in common for this? That was implemented for graceful-client-secret-rotation?


// DailyReminder is the date from which daily reminders start
DailyReminder metav1.Time `json:"dailyReminder"`
}

// ApprovalExpirationStatus defines the observed state of ApprovalExpiration
type ApprovalExpirationStatus struct {
// +listType=map
// +listMapKey=type
// +patchStrategy=merge
// +patchMergeKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`

// LastReminder is the timestamp when the last reminder was sent
// +optional
LastReminder *metav1.Time `json:"lastReminder,omitempty"`

// LastNotificationRef is a reference to the last sent reminder notification
// +optional
LastNotificationRef *types.ObjectRef `json:"lastNotificationRef,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// ApprovalExpiration is the Schema for the approvalexpirations API
// +kubebuilder:printcolumn:name="Approval",type="string",JSONPath=".spec.approval.name",description="The parent Approval"
// +kubebuilder:printcolumn:name="Expiration",type="date",JSONPath=".spec.expiration",description="When the approval expires"
type ApprovalExpiration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ApprovalExpirationSpec `json:"spec,omitempty"`
Status ApprovalExpirationStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// ApprovalExpirationList contains a list of ApprovalExpiration
type ApprovalExpirationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ApprovalExpiration `json:"items"`
}

// GetConditions returns the conditions of the ApprovalExpiration
func (ae *ApprovalExpiration) GetConditions() []metav1.Condition {
return ae.Status.Conditions
}

// SetCondition sets the condition of the ApprovalExpiration
func (ae *ApprovalExpiration) SetCondition(condition metav1.Condition) bool {
return meta.SetStatusCondition(&ae.Status.Conditions, condition)
}

func init() {
SchemeBuilder.Register(&ApprovalExpiration{}, &ApprovalExpirationList{})
}
8 changes: 5 additions & 3 deletions approval/api/v1/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,13 @@ func (b *approvalBuilder) Build(ctx context.Context) (finalResult ApprovalResult
// Approval was found
if approvalExists {
log.V(2).Info("Approval exists")
isDenied := b.Approval.Spec.State == v1.ApprovalStateRejected || b.Approval.Spec.State == v1.ApprovalStateSuspended
isDenied := b.Approval.Spec.State == v1.ApprovalStateRejected ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I know that you explained it in the Review but is it really the goal to enforce this? Currently its just purely informational

b.Approval.Spec.State == v1.ApprovalStateSuspended ||
b.Approval.Spec.State == v1.ApprovalStateExpired

if isDenied {
log.V(1).Info("Approval is rejected or suspended and must not be provisioned")
b.Owner.SetCondition(newApprovalGrantedCondition(b.Approval.Spec.State, "Approval has been rejected or suspended"))
log.V(1).Info("Approval is rejected, suspended, or expired and must not be provisioned")
b.Owner.SetCondition(newApprovalGrantedCondition(b.Approval.Spec.State, "Approval has been rejected, suspended, or expired"))
return ApprovalResultDenied, nil
}
}
Expand Down
1 change: 1 addition & 0 deletions approval/api/v1/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (
ApprovalActionDeny ApprovalAction = "Deny"
ApprovalActionSuspend ApprovalAction = "Suspend"
ApprovalActionResume ApprovalAction = "Resume"
ApprovalActionExpire ApprovalAction = "Expire"
)

func (a ApprovalAction) String() string {
Expand Down
108 changes: 108 additions & 0 deletions approval/api/v1/zz_generated.deepcopy.go

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

20 changes: 20 additions & 0 deletions approval/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook"

approvalv1 "github.com/telekom/controlplane/approval/api/v1"
"github.com/telekom/controlplane/approval/internal/config"
"github.com/telekom/controlplane/approval/internal/controller"
webhookv1 "github.com/telekom/controlplane/approval/internal/webhook/v1"
notificationv1 "github.com/telekom/controlplane/notification/api/v1"
Expand Down Expand Up @@ -79,6 +80,17 @@ func main() {

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

// Load expiration configuration
expirationConfig, err := config.LoadExpirationConfig()
if err != nil {
setupLog.Error(err, "unable to load expiration config")
os.Exit(1)
}
setupLog.Info("loaded expiration config",
"expirationPeriodMonths", expirationConfig.ExpirationPeriodMonths,
"weeklyReminderMonths", expirationConfig.LastMonthsWithWeeklyReminder,
"dailyReminderWeeks", expirationConfig.LastWeeksWithDailyReminder)

// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
Expand Down Expand Up @@ -135,6 +147,14 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "ApprovalRequest")
os.Exit(1)
}
if err = (&controller.ApprovalExpirationReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ExpirationConfig: expirationConfig,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ApprovalExpiration")
os.Exit(1)
}
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if err = webhookv1.SetupApprovalRequestWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "ApprovalRequest")
Expand Down
Loading
Loading