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: reconcile user qos limits #138

Merged
merged 3 commits into from
Feb 8, 2025
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
6 changes: 6 additions & 0 deletions apis/user/v1alpha1/userqualityofservicelimits_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ type UserQualityOfServiceLimitsParameters struct {
// UserIDSelector selects a user to retrieve its groupId and userId.
// +optional
UserIDSelector *xpv1.Selector `json:"userIdSelector,omitempty"`

// Region in which to apply the quality of service limits. Default region if unspecified.
// +optional
Region string `json:"region,omitempty"`

QOS `json:",inline"`
}

// UserQualityOfServiceLimitsObservation are the observable fields of a UserQualityOfServiceLimits.
Expand Down
1 change: 1 addition & 0 deletions apis/user/v1alpha1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ package userqualityofservicelimits

import (
"context"
"fmt"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/connection"
"github.com/crossplane/crossplane-runtime/pkg/controller"
"github.com/crossplane/crossplane-runtime/pkg/event"
Expand All @@ -34,6 +34,7 @@ import (

"github.com/statnett/provider-cloudian/apis/user/v1alpha1"
apisv1alpha1 "github.com/statnett/provider-cloudian/apis/v1alpha1"
qoslimits "github.com/statnett/provider-cloudian/internal/controller/qualityofservicelimits"
"github.com/statnett/provider-cloudian/internal/features"
"github.com/statnett/provider-cloudian/internal/sdk/cloudian"
)
Expand All @@ -45,6 +46,9 @@ const (
errGetCreds = "cannot get credentials"

errNewClient = "cannot create new Service"
errCreateQOS = "cannot create QOS"
errDeleteQOS = "cannot delete QOS"
errGetQOS = "cannot get QOS"
)

var (
Expand Down Expand Up @@ -142,8 +146,34 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
return managed.ExternalObservation{}, errors.New(errNotUserQualityOfServiceLimits)
}

// These fmt statements should be removed in the real implementation.
fmt.Printf("Observing: %+v", cr)
groupID := cr.Spec.ForProvider.GroupID
if groupID == "" {
return managed.ExternalObservation{}, nil
}
userID := cr.Spec.ForProvider.UserID
if userID == "" {
return managed.ExternalObservation{}, nil
}

user := cloudian.User{
GroupID: groupID,
UserID: userID,
}
qos, err := c.cloudianService.GetQOS(ctx, user, cr.Spec.ForProvider.Region)

if errors.Is(err, cloudian.ErrNotFound) {
return managed.ExternalObservation{ResourceExists: false}, nil
}
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, errGetQOS)
}

cr.SetConditions(xpv1.Available())

expected, err := qoslimits.ToCloudianQOS(cr.Spec.ForProvider.QOS)
if err != nil {
return managed.ExternalObservation{}, err
}

return managed.ExternalObservation{
// Return false when the external resource does not exist. This lets
Expand All @@ -154,7 +184,8 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
// Return false when the external resource exists, but it not up to date
// with the desired managed resource state. This lets the managed
// resource reconciler know that it needs to call Update.
ResourceUpToDate: true,
ResourceUpToDate: expected.Warning.Equal(qos.Warning) &&
expected.Hard.Equal(qos.Hard),

// Return any details that may be required to connect to the external
// resource. These will be stored as the connection secret.
Expand All @@ -168,7 +199,18 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext
return managed.ExternalCreation{}, errors.New(errNotUserQualityOfServiceLimits)
}

fmt.Printf("Creating: %+v", cr)
qos, err := qoslimits.ToCloudianQOS(cr.Spec.ForProvider.QOS)
if err != nil {
return managed.ExternalCreation{}, err
}

user := cloudian.User{
GroupID: cr.Spec.ForProvider.GroupID,
UserID: cr.Spec.ForProvider.UserID,
}
if err := c.cloudianService.SetQOS(ctx, user, cr.Spec.ForProvider.Region, qos); err != nil {
return managed.ExternalCreation{}, errors.Wrap(err, errCreateQOS)
}

return managed.ExternalCreation{
// Optionally return any details that may be required to connect to the
Expand All @@ -183,7 +225,18 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext
return managed.ExternalUpdate{}, errors.New(errNotUserQualityOfServiceLimits)
}

fmt.Printf("Updating: %+v", cr)
qos, err := qoslimits.ToCloudianQOS(cr.Spec.ForProvider.QOS)
if err != nil {
return managed.ExternalUpdate{}, err
}

user := cloudian.User{
GroupID: cr.Spec.ForProvider.GroupID,
UserID: cr.Spec.ForProvider.UserID,
}
if err := c.cloudianService.SetQOS(ctx, user, cr.Spec.ForProvider.Region, qos); err != nil {
return managed.ExternalUpdate{}, errors.Wrap(err, errCreateQOS)
}

return managed.ExternalUpdate{
// Optionally return any details that may be required to connect to the
Expand All @@ -198,7 +251,16 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) (managed.Ext
return managed.ExternalDelete{}, errors.New(errNotUserQualityOfServiceLimits)
}

fmt.Printf("Deleting: %+v", cr)
cr.SetConditions(xpv1.Deleting())

user := cloudian.User{
GroupID: cr.Spec.ForProvider.GroupID,
UserID: cr.Spec.ForProvider.UserID,
}
err := c.cloudianService.DeleteQOS(ctx, user, cr.Spec.ForProvider.Region)
if err != nil && !errors.Is(err, cloudian.ErrNotFound) {
return managed.ExternalDelete{}, errors.Wrap(err, errGetCreds)
}

return managed.ExternalDelete{}, nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,44 @@ spec:
groupId:
description: GroupID of the quality of service limits.
type: string
hard:
description: Hard is the hard limit.
properties:
inboundBytesPerMin:
description: InboundBytesPerMin is the limit for inbound data
per minute in bytes.
nullable: true
pattern: ^(0|((0|[1-9][0-9]*)[KMGT]i))$
type: string
outboundBytesPerMin:
description: OutboundKiBsPerMin is the limit for outbound
data per minute in bytes.
nullable: true
pattern: ^(0|((0|[1-9][0-9]*)[KMGT]i))$
type: string
requestsPerMin:
description: RequestsPerMin is the limit for number of HTTP
requests per minute.
format: int32
nullable: true
type: integer
storageQuotaBytes:
description: StorageQuotaBytes is the limit for total stored
data in bytes.
nullable: true
pattern: ^(0|((0|[1-9][0-9]*)[KMGT]i))$
type: string
storageQuotaCount:
description: StorageQuotaCount is the limit for total number
of objects.
format: int32
nullable: true
type: integer
type: object
region:
description: Region in which to apply the quality of service limits.
Default region if unspecified.
type: string
userId:
description: UserID of the quality of service limits.
type: string
Expand Down Expand Up @@ -158,6 +196,40 @@ spec:
type: string
type: object
type: object
warning:
description: Warning is the soft limit that triggers a warning.
properties:
inboundBytesPerMin:
description: InboundBytesPerMin is the limit for inbound data
per minute in bytes.
nullable: true
pattern: ^(0|((0|[1-9][0-9]*)[KMGT]i))$
type: string
outboundBytesPerMin:
description: OutboundKiBsPerMin is the limit for outbound
data per minute in bytes.
nullable: true
pattern: ^(0|((0|[1-9][0-9]*)[KMGT]i))$
type: string
requestsPerMin:
description: RequestsPerMin is the limit for number of HTTP
requests per minute.
format: int32
nullable: true
type: integer
storageQuotaBytes:
description: StorageQuotaBytes is the limit for total stored
data in bytes.
nullable: true
pattern: ^(0|((0|[1-9][0-9]*)[KMGT]i))$
type: string
storageQuotaCount:
description: StorageQuotaCount is the limit for total number
of objects.
format: int32
nullable: true
type: integer
type: object
type: object
managementPolicies:
default:
Expand Down
Loading