diff --git a/Makefile b/Makefile index 66296f9..6dde3bb 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ GOBIN := $(if $(shell go env GOBIN),$(shell go env GOBIN),$(GOPATH)/bin) FLAGS=-trimpath -ldflags '-X $(MODULE)/pkg/constants.Version=$(VERSION) -X $(MODULE)/pkg/constants.Revision=$(REVISION)' # Defines the linter version. -LINT_VERSION=v1.57.1 +LINT_VERSION=v1.59.1 # Defines the version of the CRD generation tools to use. CONTROLLER_TOOLS_VERSION=v0.14.0 diff --git a/charts/region/Chart.yaml b/charts/region/Chart.yaml index 69d1338..bf7e4ae 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.18 -appVersion: v0.1.18 +version: v0.1.19 +appVersion: v0.1.19 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_identities.yaml b/charts/region/crds/region.unikorn-cloud.org_identities.yaml index edbcfa1..9ee82df 100644 --- a/charts/region/crds/region.unikorn-cloud.org_identities.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_identities.yaml @@ -62,7 +62,7 @@ spec: to "openstack". properties: projectID: - description: ProjectIS is the ID of the project created for the + description: ProjectID is the ID of the project created for the identity. type: string userID: @@ -77,6 +77,24 @@ spec: enum: - openstack type: string + tags: + description: |- + Tags are an abitrary list of key/value pairs that a client + may populate to store metadata for the resource. + items: + description: Tag is an arbirary key/value. + properties: + name: + description: Name of the tag. + type: string + value: + description: Value of the tag. + type: string + required: + - name + - value + type: object + type: array required: - provider type: object diff --git a/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml b/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml new file mode 100644 index 0000000..927c812 --- /dev/null +++ b/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml @@ -0,0 +1,92 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: physicalnetworks.region.unikorn-cloud.org +spec: + group: region.unikorn-cloud.org + names: + categories: + - unikorn + kind: PhysicalNetwork + listKind: PhysicalNetworkList + plural: physicalnetworks + singular: physicalnetwork + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Available")].reason + name: status + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: PhysicalNetwork defines a physical network beloning to an identity. + 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: + providerNetwork: + description: |- + ProviderNetwork is the provider network for port allocation of + virtual machines. + properties: + id: + description: ID is the network ID. + type: string + vlanID: + description: VlanID is the ID if the VLAN for IPAM. + type: integer + required: + - id + - vlanID + type: object + tags: + description: |- + Tags are an abitrary list of key/value pairs that a client + may populate to store metadata for the resource. + items: + description: Tag is an arbirary key/value. + properties: + name: + description: Name of the tag. + type: string + value: + description: Value of the tag. + type: string + required: + - name + - value + type: object + type: array + type: object + status: + type: object + required: + - spec + - status + type: object + served: true + storage: true + subresources: {} diff --git a/go.mod b/go.mod index 8f44a47..4904898 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/oapi-codegen/runtime v1.1.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 - github.com/unikorn-cloud/core v0.1.55 - github.com/unikorn-cloud/identity v0.2.11 + github.com/unikorn-cloud/core v0.1.57 + github.com/unikorn-cloud/identity v0.2.20 go.opentelemetry.io/otel v1.27.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 go.opentelemetry.io/otel/sdk v1.27.0 diff --git a/go.sum b/go.sum index 089db18..c6b312e 100644 --- a/go.sum +++ b/go.sum @@ -136,10 +136,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/unikorn-cloud/core v0.1.55 h1:Oy5r3UBTNWb0qFDcmehLrgBwMx9xCo9x2nOEzNZoYUU= -github.com/unikorn-cloud/core v0.1.55/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw= -github.com/unikorn-cloud/identity v0.2.11 h1:q6mkJ3qTRjwhlvLS9Jv0I4wlJhnsbJZHu2rbNdnXBYk= -github.com/unikorn-cloud/identity v0.2.11/go.mod h1:4KHNdHiIKpKERD0slunDDXhdC59M7eiN+Y1wSfHbQwQ= +github.com/unikorn-cloud/core v0.1.57 h1:/5LjDH/z2C+NZSbfRUNBgP2IRwe/7OfnR7OgDtJAREs= +github.com/unikorn-cloud/core v0.1.57/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw= +github.com/unikorn-cloud/identity v0.2.20 h1:djsq9+F7siK3sUpGpaEM5LnCbCRtL0BIvbkOEAz5YSU= +github.com/unikorn-cloud/identity v0.2.20/go.mod h1:xeFcIJ4yZ7wRQCW8Fs1rL/NvP04LBOiv+Y1KQF3etl4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 138060f..8a02272 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -20,27 +20,28 @@ package handler import ( "cmp" - "context" "encoding/base64" "fmt" "net/http" "slices" "time" - "github.com/unikorn-cloud/core/pkg/authorization/constants" - "github.com/unikorn-cloud/core/pkg/authorization/userinfo" - coreopenapi "github.com/unikorn-cloud/core/pkg/openapi" + coreapi "github.com/unikorn-cloud/core/pkg/openapi" "github.com/unikorn-cloud/core/pkg/server/conversion" "github.com/unikorn-cloud/core/pkg/server/errors" coreutil "github.com/unikorn-cloud/core/pkg/util" - "github.com/unikorn-cloud/identity/pkg/authorization" identityclient "github.com/unikorn-cloud/identity/pkg/client" + identityapi "github.com/unikorn-cloud/identity/pkg/openapi" + "github.com/unikorn-cloud/identity/pkg/rbac" unikornv1 "github.com/unikorn-cloud/region/pkg/apis/unikorn/v1alpha1" + "github.com/unikorn-cloud/region/pkg/constants" "github.com/unikorn-cloud/region/pkg/handler/region" "github.com/unikorn-cloud/region/pkg/openapi" "github.com/unikorn-cloud/region/pkg/providers" "github.com/unikorn-cloud/region/pkg/server/util" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -77,27 +78,8 @@ func (h *Handler) setUncacheable(w http.ResponseWriter) { w.Header().Add("Cache-Control", "no-cache") } -//nolint:unparam -func (h *Handler) checkRBAC(ctx context.Context, organizationID, scope string, permission constants.Permission) error { - identity, err := h.identity.Client(ctx) - if err != nil { - return err - } - - authorizer, err := userinfo.NewAuthorizer(ctx, authorization.NewIdentityACLGetter(identity, organizationID)) - if err != nil { - return errors.HTTPForbidden("operation is not allowed by rbac").WithError(err) - } - - if err := authorizer.Allow(ctx, scope, permission); err != nil { - return errors.HTTPForbidden("operation is not allowed by rbac").WithError(err) - } - - return nil -} - func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter) { - if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -125,7 +107,7 @@ func convertGpuVendor(in providers.GPUVendor) openapi.GpuVendor { func convertFlavor(in providers.Flavor) openapi.Flavor { out := openapi.Flavor{ - Metadata: coreopenapi.StaticResourceMetadata{ + Metadata: coreapi.StaticResourceMetadata{ Id: in.ID, Name: in.Name, }, @@ -150,7 +132,7 @@ func convertFlavor(in providers.Flavor) openapi.Flavor { } func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDFlavors(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) { - if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -193,7 +175,7 @@ func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsReg func convertImage(in providers.Image) openapi.Image { out := openapi.Image{ - Metadata: coreopenapi.StaticResourceMetadata{ + Metadata: coreapi.StaticResourceMetadata{ Id: in.ID, Name: in.Name, CreationTime: in.Created, @@ -211,7 +193,7 @@ func convertImage(in providers.Image) openapi.Image { } func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImages(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) { - if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -266,9 +248,29 @@ func convertTags(in unikornv1.TagList) openapi.TagList { return out } +func regionScopedResourceReadMetadata(in metav1.Object, status coreapi.ResourceProvisioningStatus) coreapi.RegionScopedResourceMetadata { + labels := in.GetLabels() + + temp := conversion.ProjectScopedResourceReadMetadata(in, status) + + out := coreapi.RegionScopedResourceMetadata{ + Id: temp.Id, + Name: temp.Name, + Description: temp.Description, + CreatedBy: temp.CreatedBy, + CreationTime: temp.CreationTime, + ProvisioningStatus: temp.ProvisioningStatus, + OrganizationId: temp.OrganizationId, + ProjectId: temp.ProjectId, + RegionId: labels[constants.RegionLabel], + } + + return out +} + func convertIdentity(identity *unikornv1.Identity, in *providers.CloudConfig) *openapi.IdentityRead { out := &openapi.IdentityRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(identity, coreopenapi.ResourceProvisioningStatusProvisioned), + Metadata: regionScopedResourceReadMetadata(identity, coreapi.ResourceProvisioningStatusProvisioned), } if tags := convertTags(identity.Spec.Tags); tags != nil { @@ -291,8 +293,11 @@ func convertIdentity(identity *unikornv1.Identity, in *providers.CloudConfig) *o return out } +func (h *Handler) GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter) { +} + func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) { - if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Create); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Create, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -322,7 +327,7 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRe func convertPhysicalNetwork(in *unikornv1.PhysicalNetwork) *openapi.PhysicalNetworkRead { out := &openapi.PhysicalNetworkRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(in, coreopenapi.ResourceProvisioningStatusProvisioned), + Metadata: conversion.ProjectScopedResourceReadMetadata(in, coreapi.ResourceProvisioningStatusProvisioned), } if tags := convertTags(in.Spec.Tags); tags != nil { @@ -333,7 +338,7 @@ func convertPhysicalNetwork(in *unikornv1.PhysicalNetwork) *openapi.PhysicalNetw } func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDIdentitiesIdentityIDPhysicalNetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter, identityID openapi.IdentityIDParameter) { - if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Create); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Create, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -380,7 +385,7 @@ func convertExternalNetworks(in providers.ExternalNetworks) openapi.ExternalNetw } func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDExternalnetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) { - if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } diff --git a/pkg/openapi/client.go b/pkg/openapi/client.go index eb795e4..5acef1e 100644 --- a/pkg/openapi/client.go +++ b/pkg/openapi/client.go @@ -90,6 +90,9 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption { // The interface specification for the client above. type ClientInterface interface { + // GetApiV1OrganizationsOrganizationIDIdentities request + GetApiV1OrganizationsOrganizationIDIdentities(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions request GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -113,6 +116,18 @@ type ClientInterface interface { GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImages(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, regionID RegionIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) } +func (c *Client) GetApiV1OrganizationsOrganizationIDIdentities(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetApiV1OrganizationsOrganizationIDIdentitiesRequest(c.Server, organizationID) + 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) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRequest(c.Server, organizationID, projectID) if err != nil { @@ -209,6 +224,40 @@ func (c *Client) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegi return c.Client.Do(req) } +// NewGetApiV1OrganizationsOrganizationIDIdentitiesRequest generates requests for GetApiV1OrganizationsOrganizationIDIdentities +func NewGetApiV1OrganizationsOrganizationIDIdentitiesRequest(server string, organizationID OrganizationIDParameter) (*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 + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/organizations/%s/identities", pathParam0) + 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 +} + // NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRequest generates requests for GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions func NewGetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRequest(server string, organizationID OrganizationIDParameter, projectID ProjectIDParameter) (*http.Request, error) { var err error @@ -566,6 +615,9 @@ func WithBaseURL(baseURL string) ClientOption { // ClientWithResponsesInterface is the interface specification for the client with responses above. type ClientWithResponsesInterface interface { + // GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse request + GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDIdentitiesResponse, error) + // GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsWithResponse request GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse, error) @@ -589,6 +641,32 @@ type ClientWithResponsesInterface interface { GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImagesWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, regionID RegionIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImagesResponse, error) } +type GetApiV1OrganizationsOrganizationIDIdentitiesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *IdentitiesResponse + JSON400 *externalRef0.BadRequestResponse + JSON401 *externalRef0.UnauthorizedResponse + JSON403 *externalRef0.ForbiddenResponse + JSON500 *externalRef0.InternalServerErrorResponse +} + +// Status returns HTTPResponse.Status +func (r GetApiV1OrganizationsOrganizationIDIdentitiesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetApiV1OrganizationsOrganizationIDIdentitiesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse struct { Body []byte HTTPResponse *http.Response @@ -741,6 +819,15 @@ func (r GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImage return 0 } +// GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse request returning *GetApiV1OrganizationsOrganizationIDIdentitiesResponse +func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDIdentitiesResponse, error) { + rsp, err := c.GetApiV1OrganizationsOrganizationIDIdentities(ctx, organizationID, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetApiV1OrganizationsOrganizationIDIdentitiesResponse(rsp) +} + // GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsWithResponse request returning *GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse, error) { rsp, err := c.GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(ctx, organizationID, projectID, reqEditors...) @@ -811,6 +898,60 @@ func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDProjectsProject return ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImagesResponse(rsp) } +// ParseGetApiV1OrganizationsOrganizationIDIdentitiesResponse parses an HTTP response from a GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse call +func ParseGetApiV1OrganizationsOrganizationIDIdentitiesResponse(rsp *http.Response) (*GetApiV1OrganizationsOrganizationIDIdentitiesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetApiV1OrganizationsOrganizationIDIdentitiesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest IdentitiesResponse + 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 +} + // ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse parses an HTTP response from a GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsWithResponse call func ParseGetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse(rsp *http.Response) (*GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/pkg/openapi/router.go b/pkg/openapi/router.go index 7e4c3cb..cc663ef 100644 --- a/pkg/openapi/router.go +++ b/pkg/openapi/router.go @@ -15,6 +15,9 @@ import ( // ServerInterface represents all server handlers. type ServerInterface interface { + // (GET /api/v1/organizations/{organizationID}/identities) + GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) + // (GET /api/v1/organizations/{organizationID}/projects/{projectID}/regions) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter) @@ -38,6 +41,11 @@ type ServerInterface interface { type Unimplemented struct{} +// (GET /api/v1/organizations/{organizationID}/identities) +func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) { + w.WriteHeader(http.StatusNotImplemented) +} + // (GET /api/v1/organizations/{organizationID}/projects/{projectID}/regions) func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter) { w.WriteHeader(http.StatusNotImplemented) @@ -77,6 +85,34 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(http.Handler) http.Handler +// GetApiV1OrganizationsOrganizationIDIdentities operation middleware +func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDIdentities(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 + } + + ctx = context.WithValue(ctx, Oauth2AuthenticationScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetApiV1OrganizationsOrganizationIDIdentities(w, r, organizationID) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions operation middleware func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -466,6 +502,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl ErrorHandlerFunc: options.ErrorHandlerFunc, } + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/api/v1/organizations/{organizationID}/identities", wrapper.GetApiV1OrganizationsOrganizationIDIdentities) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/organizations/{organizationID}/projects/{projectID}/regions", wrapper.GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions) }) diff --git a/pkg/openapi/schema.go b/pkg/openapi/schema.go index 4437ff5..3e6dd1f 100644 --- a/pkg/openapi/schema.go +++ b/pkg/openapi/schema.go @@ -19,87 +19,89 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "AA==", + "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", + "/02JOzZaXQAA", } // 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 f6bdd5f..a899c28 100644 --- a/pkg/openapi/server.spec.yaml +++ b/pkg/openapi/server.spec.yaml @@ -68,6 +68,28 @@ paths: $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/unauthorizedResponse' '500': $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/responses/internalServerErrorResponse' + /api/v1/organizations/{organizationID}/identities: + description: |- + Managed identity services. Identities should be single use e.g. a single cluster instance. + This limits blast radius in the event of a credential leak, or in some cases avoids bugs in + provisioning software. + parameters: + - $ref: '#/components/parameters/organizationIDParameter' + get: + description: Get all identites scoped to an organization. + security: + - oauth2Authentication: [] + responses: + '200': + $ref: '#/components/responses/identitiesResponse' + '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}/projects/{projectID}/regions/{regionID}/identities: description: |- Managed identity services. Identities should be single use e.g. a single cluster instance. @@ -399,9 +421,14 @@ components: - spec properties: metadata: - $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/schemas/projectScopedResourceReadMetadata' + $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/schemas/regionScopedResourceMetadata' spec: $ref: '#/components/schemas/identitySpec' + identitiesRead: + description: A list of provider specific identities. + type: array + items: + $ref: '#/components/schemas/identityRead' physicalNetworkSpec: description: A phyical network's specification. type: object @@ -547,12 +574,35 @@ components: schema: $ref: '#/components/schemas/identityRead' example: - # TODO: metadata for region? metadata: id: a64f9269-36e0-4312-b8d1-52d93d569b7b name: unused organizationId: 9a8c6370-4065-4d4a-9da0-7678df40cd9d projectId: e36c058a-8eba-4f5b-91f4-f6ffb983795c + regionId: d891dbf0-0a01-4efc-ae3a-5d77f6d3424b + creationTime: 2024-05-31T14:11:00Z + createdBy: john.doe@acme.com + provisioningStatus: provisioned + spec: + type: openstack + openstack: + cloud: default + cloudConfig: dGhpcyBpcyBhIHRlc3QK + projectId: eb9c92d937464d14bf87e50fa726380d + userId: a19678a28126497dba24b54c96a064fa + identitiesResponse: + description: A list of identites. + content: + application/json: + schema: + $ref: '#/components/schemas/identitiesRead' + example: + - metadata: + id: a64f9269-36e0-4312-b8d1-52d93d569b7b + name: unused + organizationId: 9a8c6370-4065-4d4a-9da0-7678df40cd9d + projectId: e36c058a-8eba-4f5b-91f4-f6ffb983795c + regionId: d891dbf0-0a01-4efc-ae3a-5d77f6d3424b creationTime: 2024-05-31T14:11:00Z createdBy: john.doe@acme.com provisioningStatus: provisioned diff --git a/pkg/openapi/types.go b/pkg/openapi/types.go index cbc53a8..565e033 100644 --- a/pkg/openapi/types.go +++ b/pkg/openapi/types.go @@ -93,9 +93,12 @@ type GpuSpec struct { // GpuVendor The GPU vendor. type GpuVendor string +// IdentitiesRead A list of provider specific identities. +type IdentitiesRead = []IdentityRead + // IdentityRead A provider specific identity. type IdentityRead struct { - Metadata externalRef0.ProjectScopedResourceReadMetadata `json:"metadata"` + Metadata externalRef0.RegionScopedResourceMetadata `json:"metadata"` // Spec A provider specific identity, while the client can list regions to infer the // type, we don't requires this and return it with the response. That can then @@ -256,6 +259,9 @@ type ExternalNetworksResponse = ExternalNetworks // FlavorsResponse A list of flavors. type FlavorsResponse = Flavors +// IdentitiesResponse A list of provider specific identities. +type IdentitiesResponse = IdentitiesRead + // IdentityResponse A provider specific identity. type IdentityResponse = IdentityRead diff --git a/pkg/providers/openstack/README.md b/pkg/providers/openstack/README.md index 95da5df..2ed7753 100644 --- a/pkg/providers/openstack/README.md +++ b/pkg/providers/openstack/README.md @@ -53,7 +53,7 @@ When we create a `Region` of type `openstack`, it will require a secret that con This can be configured as follows. ```bash -kubectl create secret generic -n unikorn uk-north-1-credentials \ +kubectl create secret generic -n unikorn-region gb-north-1-credentials \ --from-literal=domain-id=${DOMAIN_ID} \ --from-literal=user-id=${USER_ID} \ --from-literal=password=${PASSWORD} @@ -67,16 +67,17 @@ apiVersion: region.unikorn-cloud.org/v1alpha1 kind: Region metadata: # Use "uuidgen -r" to select a random ID, this MUST start with a character a-f. + namespace: unikorn-region name: c7e8492f-c320-4278-8201-48cd38fed38b labels: - unikorn-cloud.org/name: uk-north-1 + unikorn-cloud.org/name: gb-north-1 spec: provider: openstack openstack: - endpoint: https://openstack.uk-north-1.unikorn-cloud.org:5000 + endpoint: https://openstack.gb-north-1.unikorn-cloud.org:5000 serviceAccountSecret: namespace: unikorn - name: uk-north-1-credentials + name: gb-north-1-credentials ``` Cleanup actions. diff --git a/pkg/providers/openstack/provider.go b/pkg/providers/openstack/provider.go index 5cae895..d7b32ca 100644 --- a/pkg/providers/openstack/provider.go +++ b/pkg/providers/openstack/provider.go @@ -545,10 +545,10 @@ func (p *Provider) CreateIdentity(ctx context.Context, organizationID, projectID }, } - objectMeta := conversion.NewObjectMetadata(&request.Metadata, p.region.Namespace) - objectMeta = objectMeta.WithOrganization(organizationID) - objectMeta = objectMeta.WithProject(projectID) - objectMeta = objectMeta.WithLabel(constants.RegionLabel, p.region.Name) + objectMeta := conversion.NewObjectMetadata(&request.Metadata, p.region.Namespace) + objectMeta = objectMeta.WithOrganization(organizationID) + objectMeta = objectMeta.WithProject(projectID) + objectMeta = objectMeta.WithLabel(constants.RegionLabel, p.region.Name) identity := &unikornv1.Identity{ ObjectMeta: objectMeta.Get(ctx), @@ -599,11 +599,11 @@ func (p *Provider) DeleteIdentity(ctx context.Context, identityID string) error // GetIdentity looks up the specified identity resource. func (p *Provider) GetIdentity(ctx context.Context, id string) (*unikornv1.Identity, error) { - out := &unikornv1.Identity{} + out := &unikornv1.Identity{} - if err := p.client.Get(ctx, client.ObjectKey{Namespace: p.region.Namespace, Name: id}, out); err != nil { - return nil, err - } + if err := p.client.Get(ctx, client.ObjectKey{Namespace: p.region.Namespace, Name: id}, out); err != nil { + return nil, err + } return out, nil } diff --git a/pkg/reaper/reaper.go b/pkg/reaper/reaper.go index ccb2bbd..9fca0d2 100644 --- a/pkg/reaper/reaper.go +++ b/pkg/reaper/reaper.go @@ -21,7 +21,9 @@ import ( "errors" "fmt" - "github.com/unikorn-cloud/core/pkg/constants" + coreconstants "github.com/unikorn-cloud/core/pkg/constants" + unikornv1 "github.com/unikorn-cloud/region/pkg/apis/unikorn/v1alpha1" + "github.com/unikorn-cloud/region/pkg/constants" "github.com/unikorn-cloud/region/pkg/handler/region" corev1 "k8s.io/api/core/v1" @@ -70,7 +72,7 @@ func (r *Reaper) Run(ctx context.Context) error { // API access. options := &client.ListOptions{ FieldSelector: fields.SelectorFromSet(fields.Set{ - "reason": constants.IdentityCleanupReadyEventReason, + "reason": coreconstants.IdentityCleanupReadyEventReason, }), } @@ -125,17 +127,18 @@ func (r *Reaper) handleEvent(ctx context.Context, event *corev1.Event) error { log.Info("processing cluster deletion event", "event", event) - regionID, ok := event.Annotations[constants.RegionAnnotation] + identityID, ok := event.Annotations[coreconstants.CloudIdentityAnnotation] if !ok { - return fmt.Errorf("%w: region annotation not present", ErrDataMissing) + return fmt.Errorf("%w: identity annotation not present", ErrDataMissing) } - identityID, ok := event.Annotations[constants.CloudIdentityAnnotation] - if !ok { - return fmt.Errorf("%w: identity annotation not present", ErrDataMissing) + identity := &unikornv1.Identity{} + + if err := r.client.Get(ctx, client.ObjectKey{Namespace: r.namespace, Name: identityID}, identity); err != nil { + return err } - provider, err := region.NewClient(r.client, r.namespace).Provider(ctx, regionID) + provider, err := region.NewClient(r.client, r.namespace).Provider(ctx, identity.Labels[constants.RegionLabel]) if err != nil { return fmt.Errorf("%w: failed to create provider client", err) } diff --git a/pkg/server/server.go b/pkg/server/server.go index 6c157a0..84f4cd3 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -31,12 +31,13 @@ import ( "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/trace" + coreapi "github.com/unikorn-cloud/core/pkg/openapi" "github.com/unikorn-cloud/core/pkg/server/middleware/cors" - openapimiddleware "github.com/unikorn-cloud/core/pkg/server/middleware/openapi" "github.com/unikorn-cloud/core/pkg/server/middleware/opentelemetry" "github.com/unikorn-cloud/core/pkg/server/middleware/timeout" identityclient "github.com/unikorn-cloud/identity/pkg/client" - "github.com/unikorn-cloud/identity/pkg/middleware/authorizer" + openapimiddleware "github.com/unikorn-cloud/identity/pkg/middleware/openapi" + openapimiddlewareremote "github.com/unikorn-cloud/identity/pkg/middleware/openapi/remote" "github.com/unikorn-cloud/region/pkg/constants" "github.com/unikorn-cloud/region/pkg/handler" "github.com/unikorn-cloud/region/pkg/openapi" @@ -128,7 +129,7 @@ func (s *Server) GetServer(client client.Client) (*http.Server, error) { } }() - schema, err := openapimiddleware.NewSchema(openapi.GetSwagger) + schema, err := coreapi.NewSchema(openapi.GetSwagger) if err != nil { return nil, err } @@ -141,7 +142,7 @@ func (s *Server) GetServer(client client.Client) (*http.Server, error) { router.NotFound(http.HandlerFunc(handler.NotFound)) router.MethodNotAllowed(http.HandlerFunc(handler.MethodNotAllowed)) - authorizer := authorizer.NewAuthorizer(client, s.Options.Namespace, &s.IdentityOptions) + authorizer := openapimiddlewareremote.NewAuthorizer(client, s.Options.Namespace, &s.IdentityOptions) // Middleware specified here is applied to all requests post-routing. // NOTE: these are applied in reverse order!!