From 61180cc0bf90b0e5d11a64804381eb6421031c1a Mon Sep 17 00:00:00 2001 From: Simon Murray Date: Tue, 3 Sep 2024 09:16:29 +0100 Subject: [PATCH] Add Quota Management Support (Part 1) (#55) Without this, you'll not get very far with Openstack's defaults! This adds in an endpoint that can read and update quotas. These are explciitly scoped to the identity (as that maps to an OpenStack project where quotas reside), and implicitly to the client e.g. unikorn-kubernetes/unikorn-baremetal, so they can independently affect quotas in a shared identity without having to know about one another. This first part adds in the necessary APIs that allow quotas to be created by a client, trigger an identity reconcile and are implicitly deleted on identity deletion. --- charts/region/Chart.yaml | 4 +- .../crds/region.unikorn-cloud.org_quotas.yaml | 85 +++++ .../identity-controller/clusterrole.yaml | 1 + .../region-controller/clusterrole.yaml | 3 + pkg/apis/unikorn/v1alpha1/register.go | 1 + pkg/apis/unikorn/v1alpha1/types.go | 47 +++ .../unikorn/v1alpha1/zz_generated.deepcopy.go | 114 ++++++ pkg/handler/handler.go | 183 ++++++++++ pkg/managers/identity/manager.go | 24 ++ pkg/openapi/client.go | 339 ++++++++++++++++++ pkg/openapi/router.go | 114 ++++++ pkg/openapi/schema.go | 202 ++++++----- pkg/openapi/server.spec.yaml | 93 +++++ pkg/openapi/types.go | 27 ++ 14 files changed, 1137 insertions(+), 100 deletions(-) create mode 100644 charts/region/crds/region.unikorn-cloud.org_quotas.yaml diff --git a/charts/region/Chart.yaml b/charts/region/Chart.yaml index c99fd3e..2ce3208 100644 --- a/charts/region/Chart.yaml +++ b/charts/region/Chart.yaml @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn's Region Controller type: application -version: v0.1.37 -appVersion: v0.1.37 +version: v0.1.38 +appVersion: v0.1.38 icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png diff --git a/charts/region/crds/region.unikorn-cloud.org_quotas.yaml b/charts/region/crds/region.unikorn-cloud.org_quotas.yaml new file mode 100644 index 0000000..45905a9 --- /dev/null +++ b/charts/region/crds/region.unikorn-cloud.org_quotas.yaml @@ -0,0 +1,85 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: quotas.region.unikorn-cloud.org +spec: + group: region.unikorn-cloud.org + names: + categories: + - unikorn + kind: Quota + listKind: QuotaList + plural: quotas + singular: quota + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Quota defines resource limits for identities. + We don't want to be concerned with Hertz and bytes, instead we want to + expose higher level primitives like flavors and how many they are. This + removes a lot of the burden from clients. Where we have to be careful is + with overheads, e.g. a machine implicitly defines CPUs, memory and storage, + but this will also need networks, NICs and other supporting resources. + Quotas are scoped to identities, and also to a specific client, as this avoids + having to worry about IPC and split brain concerns. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + flavors: + description: Flavors is a list of flavors and their count. + items: + properties: + count: + description: |- + Count is the number of instances that are required. + For certain services that can do rolling upgrades, be aware that this + may need a little overhead to cater for that. For example the Kubernetes + service will do a one-in-one-out upgrade of the control plane. + type: integer + id: + description: ID is the flavor ID. + type: string + required: + - count + - id + type: object + type: array + x-kubernetes-list-map-keys: + - id + x-kubernetes-list-type: map + type: object + status: + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} diff --git a/charts/region/templates/identity-controller/clusterrole.yaml b/charts/region/templates/identity-controller/clusterrole.yaml index 4beb737..65b7fb1 100644 --- a/charts/region/templates/identity-controller/clusterrole.yaml +++ b/charts/region/templates/identity-controller/clusterrole.yaml @@ -35,6 +35,7 @@ rules: - apiGroups: - region.unikorn-cloud.org resources: + - quotas - physicalnetworks verbs: - list diff --git a/charts/region/templates/region-controller/clusterrole.yaml b/charts/region/templates/region-controller/clusterrole.yaml index 7b22087..ff3e433 100644 --- a/charts/region/templates/region-controller/clusterrole.yaml +++ b/charts/region/templates/region-controller/clusterrole.yaml @@ -18,11 +18,14 @@ rules: resources: - identities - physicalnetworks + - quotas verbs: - list - watch - create - delete + - patch + - update - apiGroups: - region.unikorn-cloud.org resources: diff --git a/pkg/apis/unikorn/v1alpha1/register.go b/pkg/apis/unikorn/v1alpha1/register.go index 7995729..5406aee 100644 --- a/pkg/apis/unikorn/v1alpha1/register.go +++ b/pkg/apis/unikorn/v1alpha1/register.go @@ -50,6 +50,7 @@ var ( func init() { SchemeBuilder.Register(&Region{}, &RegionList{}) SchemeBuilder.Register(&Identity{}, &IdentityList{}) + SchemeBuilder.Register(&Quota{}, &QuotaList{}) SchemeBuilder.Register(&PhysicalNetwork{}, &PhysicalNetworkList{}) SchemeBuilder.Register(&OpenstackIdentity{}, &OpenstackIdentityList{}) SchemeBuilder.Register(&OpenstackPhysicalNetwork{}, &OpenstackPhysicalNetworkList{}) diff --git a/pkg/apis/unikorn/v1alpha1/types.go b/pkg/apis/unikorn/v1alpha1/types.go index d132b57..15bd35f 100644 --- a/pkg/apis/unikorn/v1alpha1/types.go +++ b/pkg/apis/unikorn/v1alpha1/types.go @@ -466,3 +466,50 @@ type VLANAllocationEntry struct { type VLANAllocationStatus struct { } + +// QuotaList is a typed list of quotas. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type QuotaList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Quota `json:"items"` +} + +// Quota defines resource limits for identities. +// We don't want to be concerned with Hertz and bytes, instead we want to +// expose higher level primitives like flavors and how many they are. This +// removes a lot of the burden from clients. Where we have to be careful is +// with overheads, e.g. a machine implicitly defines CPUs, memory and storage, +// but this will also need networks, NICs and other supporting resources. +// Quotas are scoped to identities, and also to a specific client, as this avoids +// having to worry about IPC and split brain concerns. +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:scope=Namespaced,categories=unikorn +// +kubebuilder:printcolumn:name="age",type="date",JSONPath=".metadata.creationTimestamp" +type Quota struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec QuotaSpec `json:"spec"` + Status QuotaStatus `json:"status,omitempty"` +} + +type QuotaSpec struct { + // Flavors is a list of flavors and their count. + // +listType=map + // +listMapKey=id + Flavors []FlavorQuota `json:"flavors,omitempty"` +} + +type FlavorQuota struct { + // ID is the flavor ID. + ID string `json:"id"` + // Count is the number of instances that are required. + // For certain services that can do rolling upgrades, be aware that this + // may need a little overhead to cater for that. For example the Kubernetes + // service will do a one-in-one-out upgrade of the control plane. + Count int `json:"count"` +} + +type QuotaStatus struct { +} diff --git a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go index 58bc20f..8aefec4 100644 --- a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go @@ -104,6 +104,22 @@ func (in *FlavorMetadata) DeepCopy() *FlavorMetadata { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FlavorQuota) DeepCopyInto(out *FlavorQuota) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlavorQuota. +func (in *FlavorQuota) DeepCopy() *FlavorQuota { + if in == nil { + return nil + } + out := new(FlavorQuota) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FlavorSelector) DeepCopyInto(out *FlavorSelector) { *out = *in @@ -745,6 +761,104 @@ func (in *ProviderNetworks) DeepCopy() *ProviderNetworks { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Quota) DeepCopyInto(out *Quota) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Quota. +func (in *Quota) DeepCopy() *Quota { + if in == nil { + return nil + } + out := new(Quota) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Quota) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *QuotaList) DeepCopyInto(out *QuotaList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Quota, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaList. +func (in *QuotaList) DeepCopy() *QuotaList { + if in == nil { + return nil + } + out := new(QuotaList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *QuotaList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *QuotaSpec) DeepCopyInto(out *QuotaSpec) { + *out = *in + if in.Flavors != nil { + in, out := &in.Flavors, &out.Flavors + *out = make([]FlavorQuota, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaSpec. +func (in *QuotaSpec) DeepCopy() *QuotaSpec { + if in == nil { + return nil + } + out := new(QuotaSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *QuotaStatus) DeepCopyInto(out *QuotaStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuotaStatus. +func (in *QuotaStatus) DeepCopy() *QuotaStatus { + if in == nil { + return nil + } + out := new(QuotaStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Region) DeepCopyInto(out *Region) { *out = *in diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 9ed3ca1..9a19c71 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -49,6 +49,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) type Handler struct { @@ -695,6 +696,188 @@ func (h *Handler) DeleteApiV1OrganizationsOrganizationIDProjectsProjectIDIdentit w.WriteHeader(http.StatusAccepted) } +func (h *Handler) getQuota(ctx context.Context, identity *unikornv1.Identity) (*unikornv1.Quota, error) { + userinfo, err := authorization.UserinfoFromContext(ctx) + if err != nil { + return nil, errors.OAuth2ServerError("unable to get userinfo").WithError(err) + } + + options := &client.ListOptions{ + Namespace: h.namespace, + LabelSelector: labels.SelectorFromSet(map[string]string{ + constants.IdentityLabel: identity.Name, + }), + } + + resources := &unikornv1.QuotaList{} + + if err := h.client.List(ctx, resources, options); err != nil { + return nil, errors.OAuth2ServerError("unable to list quotas").WithError(err) + } + + // Default scoping rule is that you can only see your own quota. + resources.Items = slices.DeleteFunc(resources.Items, func(resource unikornv1.Quota) bool { + return resource.Annotations[coreconstants.CreatorAnnotation] != userinfo.Sub + }) + + if len(resources.Items) == 0 { + //nolint:nilnil + return nil, nil + } + + // TODO: what if there's more than one!! + return &resources.Items[0], nil +} + +func convertFlavorQuotas(in []unikornv1.FlavorQuota) *openapi.FlavorQuotaList { + if len(in) == 0 { + return nil + } + + out := make(openapi.FlavorQuotaList, len(in)) + + for i := range in { + out[i] = openapi.FlavorQuota{ + Id: in[i].ID, + Count: in[i].Count, + } + } + + return &out +} + +func convertQuota(in *unikornv1.Quota) *openapi.QuotasSpec { + out := &openapi.QuotasSpec{ + Flavors: convertFlavorQuotas(in.Spec.Flavors), + } + + return out +} + +func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, identityID openapi.IdentityIDParameter) { + if err := rbac.AllowProjectScope(r.Context(), "quotas", identityapi.Read, organizationID, projectID); err != nil { + errors.HandleError(w, r, err) + return + } + + identity, err := h.getIdentity(r.Context(), identityID) + if err != nil { + errors.HandleError(w, r, err) + return + } + + resource, err := h.getQuota(r.Context(), identity) + if err != nil { + errors.HandleError(w, r, err) + return + } + + if resource == nil { + resource = &unikornv1.Quota{} + } + + util.WriteJSONResponse(w, r, http.StatusOK, convertQuota(resource)) +} + +func generateFlavorQuotas(in *openapi.FlavorQuotaList) []unikornv1.FlavorQuota { + if in == nil || len(*in) == 0 { + return nil + } + + t := *in + + out := make([]unikornv1.FlavorQuota, len(t)) + + for i := range t { + out[i] = unikornv1.FlavorQuota{ + ID: t[i].Id, + Count: t[i].Count, + } + } + + return out +} + +func (h *Handler) generateQuota(ctx context.Context, organizationID, projectID string, identity *unikornv1.Identity, in *openapi.QuotasSpec) (*unikornv1.Quota, error) { + userinfo, err := authorization.UserinfoFromContext(ctx) + if err != nil { + return nil, errors.OAuth2ServerError("unable to get userinfo").WithError(err) + } + + metadata := &coreapi.ResourceWriteMetadata{ + Name: fmt.Sprintf("identity-quota-%s", identity.Name), + } + + resource := &unikornv1.Quota{ + ObjectMeta: conversion.NewObjectMetadata(metadata, h.namespace, userinfo.Sub).WithOrganization(organizationID).WithProject(projectID).WithLabel(constants.RegionLabel, identity.Labels[constants.RegionLabel]).WithLabel(constants.IdentityLabel, identity.Name).Get(), + Spec: unikornv1.QuotaSpec{ + Flavors: generateFlavorQuotas(in.Flavors), + }, + } + + // Ensure the quota is owned by the identity so it is automatically cleaned + // up on identity deletion. + if err := controllerutil.SetOwnerReference(identity, resource, h.client.Scheme()); err != nil { + return nil, err + } + + return resource, nil +} + +func (h *Handler) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, identityID openapi.IdentityIDParameter) { + if err := rbac.AllowProjectScope(r.Context(), "quotas", identityapi.Update, organizationID, projectID); err != nil { + errors.HandleError(w, r, err) + return + } + + request := &openapi.QuotasSpec{} + + if err := util.ReadJSONBody(r, request); err != nil { + errors.HandleError(w, r, err) + return + } + + identity, err := h.getIdentity(r.Context(), identityID) + if err != nil { + errors.HandleError(w, r, err) + return + } + + required, err := h.generateQuota(r.Context(), organizationID, projectID, identity, request) + if err != nil { + errors.HandleError(w, r, err) + return + } + + current, err := h.getQuota(r.Context(), identity) + if err != nil { + errors.HandleError(w, r, err) + return + } + + if current == nil { + if err := h.client.Create(r.Context(), required); err != nil { + errors.HandleError(w, r, errors.OAuth2ServerError("unable to create quota").WithError(err)) + return + } + + w.WriteHeader(http.StatusAccepted) + return + } + + updated := current.DeepCopy() + updated.Labels = required.Labels + updated.Annotations = required.Annotations + updated.Spec = required.Spec + + if err := h.client.Patch(r.Context(), updated, client.MergeFrom(current)); err != nil { + errors.HandleError(w, r, errors.OAuth2ServerError("unable to updated quota").WithError(err)) + return + } + + w.WriteHeader(http.StatusAccepted) +} + func convertExternalNetwork(in providers.ExternalNetwork) openapi.ExternalNetwork { out := openapi.ExternalNetwork{ Id: in.ID, diff --git a/pkg/managers/identity/manager.go b/pkg/managers/identity/manager.go index b3e5bf2..6feba4d 100644 --- a/pkg/managers/identity/manager.go +++ b/pkg/managers/identity/manager.go @@ -17,6 +17,8 @@ limitations under the License. package identity import ( + "context" + coreclient "github.com/unikorn-cloud/core/pkg/client" coremanager "github.com/unikorn-cloud/core/pkg/manager" "github.com/unikorn-cloud/core/pkg/manager/options" @@ -24,6 +26,8 @@ import ( "github.com/unikorn-cloud/region/pkg/constants" "github.com/unikorn-cloud/region/pkg/provisioners/managers/identity" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -53,6 +57,21 @@ func (*Factory) Reconciler(options *options.Options, controllerOptions coremanag return coremanager.NewReconciler(options, controllerOptions, manager, identity.New) } +func mapFromQuotaToIdentity(ctx context.Context, quota *unikornv1.Quota) []reconcile.Request { + if quota.DeletionTimestamp != nil { + return nil + } + + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{ + Namespace: quota.Namespace, + Name: quota.Labels[constants.IdentityLabel], + }, + }, + } +} + // RegisterWatches adds any watches that would trigger a reconcile. func (*Factory) RegisterWatches(manager manager.Manager, controller controller.Controller) error { // Any changes to the identity spec, trigger a reconcile. @@ -60,6 +79,11 @@ func (*Factory) RegisterWatches(manager manager.Manager, controller controller.C return err } + // Any changes to a quota owned by the identity trigger a reconcile. + if err := controller.Watch(source.Kind(manager.GetCache(), &unikornv1.Quota{}, handler.TypedEnqueueRequestsFromMapFunc(mapFromQuotaToIdentity))); err != nil { + return err + } + return nil } diff --git a/pkg/openapi/client.go b/pkg/openapi/client.go index 95ed495..7a52f97 100644 --- a/pkg/openapi/client.go +++ b/pkg/openapi/client.go @@ -118,6 +118,14 @@ type ClientInterface interface { // GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkID request GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkID(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, physicalNetworkID PhysicalNetworkIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas request + GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBody request with any body + PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBody(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, body PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetApiV1OrganizationsOrganizationIDRegions request GetApiV1OrganizationsOrganizationIDRegions(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -251,6 +259,42 @@ func (c *Client) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesI return c.Client.Do(req) } +func (c *Client) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequest(c.Server, organizationID, projectID, identityID) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBody(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequestWithBody(c.Server, organizationID, projectID, identityID, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, body PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequest(c.Server, organizationID, projectID, identityID, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) GetApiV1OrganizationsOrganizationIDRegions(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetApiV1OrganizationsOrganizationIDRegionsRequest(c.Server, organizationID) if err != nil { @@ -688,6 +732,115 @@ func NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityID return req, nil } +// NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequest generates requests for GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas +func NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequest(server string, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organizationID", runtime.ParamLocationPath, organizationID) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "projectID", runtime.ParamLocationPath, projectID) + if err != nil { + return nil, err + } + + var pathParam2 string + + pathParam2, err = runtime.StyleParamWithLocation("simple", false, "identityID", runtime.ParamLocationPath, identityID) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/organizations/%s/projects/%s/identities/%s/quotas", pathParam0, pathParam1, pathParam2) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequest calls the generic PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas builder with application/json body +func NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequest(server string, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, body PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequestWithBody(server, organizationID, projectID, identityID, "application/json", bodyReader) +} + +// NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequestWithBody generates requests for PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas with any type of body +func NewPutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasRequestWithBody(server string, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organizationID", runtime.ParamLocationPath, organizationID) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "projectID", runtime.ParamLocationPath, projectID) + if err != nil { + return nil, err + } + + var pathParam2 string + + pathParam2, err = runtime.StyleParamWithLocation("simple", false, "identityID", runtime.ParamLocationPath, identityID) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/organizations/%s/projects/%s/identities/%s/quotas", pathParam0, pathParam1, pathParam2) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewGetApiV1OrganizationsOrganizationIDRegionsRequest generates requests for GetApiV1OrganizationsOrganizationIDRegions func NewGetApiV1OrganizationsOrganizationIDRegionsRequest(server string, organizationID OrganizationIDParameter) (*http.Request, error) { var err error @@ -916,6 +1069,14 @@ type ClientWithResponsesInterface interface { // GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkIDWithResponse request GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkIDWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, physicalNetworkID PhysicalNetworkIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkIDResponse, error) + // GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse request + GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) + + // PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBodyWithResponse request with any body + PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBodyWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) + + PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, body PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody, reqEditors ...RequestEditorFn) (*PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) + // GetApiV1OrganizationsOrganizationIDRegionsWithResponse request GetApiV1OrganizationsOrganizationIDRegionsWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDRegionsResponse, error) @@ -1136,6 +1297,57 @@ func (r GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityID return 0 } +type GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *QuotasResponse + JSON400 *externalRef0.BadRequestResponse + JSON401 *externalRef0.UnauthorizedResponse + JSON403 *externalRef0.ForbiddenResponse + JSON500 *externalRef0.InternalServerErrorResponse +} + +// Status returns HTTPResponse.Status +func (r GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *externalRef0.BadRequestResponse + JSON401 *externalRef0.UnauthorizedResponse + JSON403 *externalRef0.ForbiddenResponse + JSON500 *externalRef0.InternalServerErrorResponse +} + +// Status returns HTTPResponse.Status +func (r PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type GetApiV1OrganizationsOrganizationIDRegionsResponse struct { Body []byte HTTPResponse *http.Response @@ -1324,6 +1536,32 @@ func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDProjectsProject return ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkIDResponse(rsp) } +// GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse request returning *GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse +func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) { + rsp, err := c.GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(ctx, organizationID, projectID, identityID, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse(rsp) +} + +// PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBodyWithResponse request with arbitrary body returning *PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse +func (c *ClientWithResponses) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBodyWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) { + rsp, err := c.PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithBody(ctx, organizationID, projectID, identityID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse(rsp) +} + +func (c *ClientWithResponses) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, body PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody, reqEditors ...RequestEditorFn) (*PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) { + rsp, err := c.PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(ctx, organizationID, projectID, identityID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse(rsp) +} + // GetApiV1OrganizationsOrganizationIDRegionsWithResponse request returning *GetApiV1OrganizationsOrganizationIDRegionsResponse func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDRegionsWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDRegionsResponse, error) { rsp, err := c.GetApiV1OrganizationsOrganizationIDRegions(ctx, organizationID, reqEditors...) @@ -1785,6 +2023,107 @@ func ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentity return response, nil } +// ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse parses an HTTP response from a GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse call +func ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse(rsp *http.Response) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest QuotasResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest externalRef0.BadRequestResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest externalRef0.UnauthorizedResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest externalRef0.ForbiddenResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest externalRef0.InternalServerErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse parses an HTTP response from a PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasWithResponse call +func ParsePutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse(rsp *http.Response) (*PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest externalRef0.BadRequestResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest externalRef0.UnauthorizedResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest externalRef0.ForbiddenResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest externalRef0.InternalServerErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseGetApiV1OrganizationsOrganizationIDRegionsResponse parses an HTTP response from a GetApiV1OrganizationsOrganizationIDRegionsWithResponse call func ParseGetApiV1OrganizationsOrganizationIDRegionsResponse(rsp *http.Response) (*GetApiV1OrganizationsOrganizationIDRegionsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/pkg/openapi/router.go b/pkg/openapi/router.go index b808b82..1bd107a 100644 --- a/pkg/openapi/router.go +++ b/pkg/openapi/router.go @@ -39,6 +39,12 @@ type ServerInterface interface { // (GET /api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/physicalnetworks/{physicalNetworkID}) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkID(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter, physicalNetworkID PhysicalNetworkIDParameter) + // (GET /api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas) + GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter) + + // (PUT /api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas) + PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter) + // (GET /api/v1/organizations/{organizationID}/regions) GetApiV1OrganizationsOrganizationIDRegions(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) @@ -96,6 +102,16 @@ func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdent w.WriteHeader(http.StatusNotImplemented) } +// (GET /api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas) +func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter) { + w.WriteHeader(http.StatusNotImplemented) +} + +// (PUT /api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas) +func (_ Unimplemented) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter, identityID IdentityIDParameter) { + w.WriteHeader(http.StatusNotImplemented) +} + // (GET /api/v1/organizations/{organizationID}/regions) func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDRegions(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) { w.WriteHeader(http.StatusNotImplemented) @@ -466,6 +482,98 @@ func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDProjectsPr handler.ServeHTTP(w, r.WithContext(ctx)) } +// GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas operation middleware +func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "organizationID" ------------- + var organizationID OrganizationIDParameter + + err = runtime.BindStyledParameterWithLocation("simple", false, "organizationID", runtime.ParamLocationPath, chi.URLParam(r, "organizationID"), &organizationID) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "organizationID", Err: err}) + return + } + + // ------------- Path parameter "projectID" ------------- + var projectID ProjectIDParameter + + err = runtime.BindStyledParameterWithLocation("simple", false, "projectID", runtime.ParamLocationPath, chi.URLParam(r, "projectID"), &projectID) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "projectID", Err: err}) + return + } + + // ------------- Path parameter "identityID" ------------- + var identityID IdentityIDParameter + + err = runtime.BindStyledParameterWithLocation("simple", false, "identityID", runtime.ParamLocationPath, chi.URLParam(r, "identityID"), &identityID) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identityID", Err: err}) + return + } + + ctx = context.WithValue(ctx, Oauth2AuthenticationScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w, r, organizationID, projectID, identityID) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas operation middleware +func (siw *ServerInterfaceWrapper) PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "organizationID" ------------- + var organizationID OrganizationIDParameter + + err = runtime.BindStyledParameterWithLocation("simple", false, "organizationID", runtime.ParamLocationPath, chi.URLParam(r, "organizationID"), &organizationID) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "organizationID", Err: err}) + return + } + + // ------------- Path parameter "projectID" ------------- + var projectID ProjectIDParameter + + err = runtime.BindStyledParameterWithLocation("simple", false, "projectID", runtime.ParamLocationPath, chi.URLParam(r, "projectID"), &projectID) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "projectID", Err: err}) + return + } + + // ------------- Path parameter "identityID" ------------- + var identityID IdentityIDParameter + + err = runtime.BindStyledParameterWithLocation("simple", false, "identityID", runtime.ParamLocationPath, chi.URLParam(r, "identityID"), &identityID) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "identityID", Err: err}) + return + } + + ctx = context.WithValue(ctx, Oauth2AuthenticationScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas(w, r, organizationID, projectID, identityID) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // GetApiV1OrganizationsOrganizationIDRegions operation middleware func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDRegions(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -742,6 +850,12 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/physicalnetworks/{physicalNetworkID}", wrapper.GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksPhysicalNetworkID) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas", wrapper.GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas", wrapper.PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/organizations/{organizationID}/regions", wrapper.GetApiV1OrganizationsOrganizationIDRegions) }) diff --git a/pkg/openapi/schema.go b/pkg/openapi/schema.go index 14af854..7525377 100644 --- a/pkg/openapi/schema.go +++ b/pkg/openapi/schema.go @@ -19,104 +19,110 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/fWfJ9L9fUj37u5/AwAA//+PuvDR0nsAAA==", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/openapi/server.spec.yaml b/pkg/openapi/server.spec.yaml index 4a90a42..b7e4357 100644 --- a/pkg/openapi/server.spec.yaml +++ b/pkg/openapi/server.spec.yaml @@ -177,6 +177,49 @@ paths: $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/notFoundResponse' '500': $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/internalServerErrorResponse' + /api/v1/organizations/{organizationID}/projects/{projectID}/identities/{identityID}/quotas: + description: |- + Provides per-client quota management, to limit the scope within the identity and hide + other clients from each other. Aggregation is handled transparently by the controller. + Please note that quotas cannot be enforced per-client, e.g. client A can accidentally + use client B's allocation. Client scoping is handled via the token subject, so it's + important that callers be using service accounts, not end user tokens. + parameters: + - $ref: '#/components/parameters/organizationIDParameter' + - $ref: '#/components/parameters/projectIDParameter' + - $ref: '#/components/parameters/identityIDParameter' + get: + description: A client-specific set of quotas for the identity. + security: + - oauth2Authentication: [] + responses: + '200': + $ref: '#/components/responses/quotasResponse' + '400': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/badRequestResponse' + '401': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/unauthorizedResponse' + '403': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/forbiddenResponse' + '500': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/internalServerErrorResponse' + put: + description: Set per-client quotas for the identity. + security: + - oauth2Authentication: [] + requestBody: + $ref: '#/components/requestBodies/quotasRequest' + responses: + '202': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/acceptedResponse' + '400': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/badRequestResponse' + '401': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/unauthorizedResponse' + '403': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/forbiddenResponse' + '500': + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/internalServerErrorResponse' /api/v1/organizations/{organizationID}/physicalnetworks: description: |- Manages physical networks. Physical networks are networks that may be required for @@ -303,6 +346,13 @@ components: required: true schema: $ref: '#/components/schemas/kubernetesNameParameter' + clientIDParameter: + name: clientID + in: path + description: The client a resource belongs to. + required: true + schema: + $ref: '#/components/schemas/kubernetesNameParameter' schemas: kubernetesNameParameter: description: A Kubernetes name. Must be a valid DNS containing only lower case characters, numbers or hyphens, start and end with a character or number, and be at most 63 characters in length. @@ -687,6 +737,29 @@ components: type: array items: $ref: '#/components/schemas/externalNetwork' + flavorQuota: + description: A flavor quota. + type: object + required: + - id + - count + properties: + id: + description: The flavor ID. + type: string + count: + description: The number of the required flavor. + type: integer + flavorQuotaList: + description: A list of flavor quotas. + type: array + items: + $ref: '#/components/schemas/flavorQuota' + quotasSpec: + type: object + properties: + flavors: + $ref: '#/components/schemas/flavorQuotaList' requestBodies: identityRequest: description: A request for an identity. @@ -721,6 +794,16 @@ components: prefix: 192.168.0.0/24 dnsNameservers: - 8.8.8.8 + quotasRequest: + description: A request to set quotas. + content: + application/json: + schema: + $ref: '#/components/schemas/quotasSpec' + example: + flavors: + - id: 9a8c6370-4065-4d4a-9da0-7678df40cd9d + count: 8 responses: regionsResponse: description: A list of regions. @@ -887,6 +970,16 @@ components: example: - id: c3f92716-1fdc-4b7b-906e-4fe0154dfcdf name: foo + quotasResponse: + description: A set of quotas for a client. + content: + application/json: + schema: + $ref: '#/components/schemas/quotasSpec' + example: + flavors: + - id: 9a8c6370-4065-4d4a-9da0-7678df40cd9d + count: 8 securitySchemes: oauth2Authentication: description: Operation requires OAuth2 bearer token authentication. diff --git a/pkg/openapi/types.go b/pkg/openapi/types.go index ec139b0..3581c31 100644 --- a/pkg/openapi/types.go +++ b/pkg/openapi/types.go @@ -52,6 +52,18 @@ type Flavor struct { Spec FlavorSpec `json:"spec"` } +// FlavorQuota A flavor quota. +type FlavorQuota struct { + // Count The number of the required flavor. + Count int `json:"count"` + + // Id The flavor ID. + Id string `json:"id"` +} + +// FlavorQuotaList A list of flavor quotas. +type FlavorQuotaList = []FlavorQuota + // FlavorSpec A flavor. type FlavorSpec struct { // Baremetal Whether the flavor is for a dedicated machine. @@ -287,6 +299,12 @@ type PhysicalNetworkWriteSpec struct { // PhysicalNetworksRead A list of physical networks. type PhysicalNetworksRead = []PhysicalNetworkRead +// QuotasSpec defines model for quotasSpec. +type QuotasSpec struct { + // Flavors A list of flavor quotas. + Flavors *FlavorQuotaList `json:"flavors,omitempty"` +} + // RegionFeatures A set of features the region may provide to clients. type RegionFeatures struct { // PhysicalNetworks If set, this indicates that the region supports physical networks and @@ -374,6 +392,9 @@ type PhysicalNetworkResponse = PhysicalNetworkRead // PhysicalNetworksResponse A list of physical networks. type PhysicalNetworksResponse = PhysicalNetworksRead +// QuotasResponse defines model for quotasResponse. +type QuotasResponse = QuotasSpec + // RegionsResponse A list of regions. type RegionsResponse = Regions @@ -383,8 +404,14 @@ type IdentityRequest = IdentityWrite // PhysicalNetworkRequest A physical network request. type PhysicalNetworkRequest = PhysicalNetworkWrite +// QuotasRequest defines model for quotasRequest. +type QuotasRequest = QuotasSpec + // PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesJSONRequestBody defines body for PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities for application/json ContentType. type PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesJSONRequestBody = IdentityWrite // PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksJSONRequestBody defines body for PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworks for application/json ContentType. type PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworksJSONRequestBody = PhysicalNetworkWrite + +// PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody defines body for PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotas for application/json ContentType. +type PutApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDQuotasJSONRequestBody = QuotasSpec