From aef5f42344750a87726312e224cc0496073f0073 Mon Sep 17 00:00:00 2001 From: Simon Murray Date: Fri, 23 Aug 2024 12:06:25 +0100 Subject: [PATCH] Add a Physical Network View (#51) --- charts/region/Chart.yaml | 4 +- pkg/handler/handler.go | 38 ++++++- pkg/openapi/client.go | 141 +++++++++++++++++++++++++ pkg/openapi/router.go | 39 +++++++ pkg/openapi/schema.go | 193 ++++++++++++++++++----------------- pkg/openapi/server.spec.yaml | 52 ++++++++++ pkg/openapi/types.go | 8 +- 7 files changed, 375 insertions(+), 100 deletions(-) diff --git a/charts/region/Chart.yaml b/charts/region/Chart.yaml index bb79d1a..7adb049 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.34 -appVersion: v0.1.34 +version: v0.1.35 +appVersion: v0.1.35 icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 01cd676..4fd2b82 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -521,7 +521,7 @@ func (h *Handler) convertPhysicalNetwork(ctx context.Context, in *unikornv1.Phys out := &openapi.PhysicalNetworkRead{ Metadata: conversion.ProjectScopedResourceReadMetadata(in, provisioningStatus), - Spec: &openapi.PhysicalNetworkReadSpec{ + Spec: openapi.PhysicalNetworkReadSpec{ RegionId: in.Labels[constants.RegionLabel], Prefix: in.Spec.Prefix.String(), DnsNameservers: convertIPv4List(in.Spec.DNSNameservers), @@ -550,6 +550,42 @@ func (h *Handler) convertPhysicalNetwork(ctx context.Context, in *unikornv1.Phys return out } +func (h *Handler) convertPhysicalNetworkList(ctx context.Context, in unikornv1.PhysicalNetworkList) openapi.PhysicalNetworksRead { + out := make(openapi.PhysicalNetworksRead, len(in.Items)) + + for i := range in.Items { + out[i] = *h.convertPhysicalNetwork(ctx, &in.Items[i]) + } + + return out +} + +func (h *Handler) GetApiV1OrganizationsOrganizationIDPhysicalnetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter) { + if err := rbac.AllowOrganizationScope(r.Context(), "physicalnetworks", identityapi.Read, organizationID); err != nil { + errors.HandleError(w, r, err) + return + } + + var result unikornv1.PhysicalNetworkList + + options := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{ + coreconstants.OrganizationLabel: organizationID, + }), + } + + if err := h.client.List(r.Context(), &result, options); err != nil { + errors.HandleError(w, r, errors.OAuth2ServerError("unable to list physical networks").WithError(err)) + return + } + + slices.SortStableFunc(result.Items, func(a, b unikornv1.PhysicalNetwork) int { + return cmp.Compare(a.Name, b.Name) + }) + + util.WriteJSONResponse(w, r, http.StatusOK, h.convertPhysicalNetworkList(r.Context(), result)) +} + func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDPhysicalnetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, identityID openapi.IdentityIDParameter) { if err := rbac.AllowProjectScope(r.Context(), "physicalnetworks", identityapi.Create, organizationID, projectID); err != nil { errors.HandleError(w, r, err) diff --git a/pkg/openapi/client.go b/pkg/openapi/client.go index 50a83c3..95ed495 100644 --- a/pkg/openapi/client.go +++ b/pkg/openapi/client.go @@ -93,6 +93,9 @@ type ClientInterface interface { // GetApiV1OrganizationsOrganizationIDIdentities request GetApiV1OrganizationsOrganizationIDIdentities(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetApiV1OrganizationsOrganizationIDPhysicalnetworks request + GetApiV1OrganizationsOrganizationIDPhysicalnetworks(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBody request with any body PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBody(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -140,6 +143,18 @@ func (c *Client) GetApiV1OrganizationsOrganizationIDIdentities(ctx context.Conte return c.Client.Do(req) } +func (c *Client) GetApiV1OrganizationsOrganizationIDPhysicalnetworks(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetApiV1OrganizationsOrganizationIDPhysicalnetworksRequest(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) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBody(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesRequestWithBody(c.Server, organizationID, projectID, contentType, body) if err != nil { @@ -318,6 +333,40 @@ func NewGetApiV1OrganizationsOrganizationIDIdentitiesRequest(server string, orga return req, nil } +// NewGetApiV1OrganizationsOrganizationIDPhysicalnetworksRequest generates requests for GetApiV1OrganizationsOrganizationIDPhysicalnetworks +func NewGetApiV1OrganizationsOrganizationIDPhysicalnetworksRequest(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/physicalnetworks", 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 +} + // NewPostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesRequest calls the generic PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities builder with application/json body func NewPostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesRequest(server string, organizationID OrganizationIDParameter, projectID ProjectIDParameter, body PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -842,6 +891,9 @@ type ClientWithResponsesInterface interface { // GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse request GetApiV1OrganizationsOrganizationIDIdentitiesWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDIdentitiesResponse, error) + // GetApiV1OrganizationsOrganizationIDPhysicalnetworksWithResponse request + GetApiV1OrganizationsOrganizationIDPhysicalnetworksWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse, error) + // PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBodyWithResponse request with any body PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBodyWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse, error) @@ -903,6 +955,32 @@ func (r GetApiV1OrganizationsOrganizationIDIdentitiesResponse) StatusCode() int return 0 } +type GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *PhysicalNetworksResponse + JSON400 *externalRef0.BadRequestResponse + JSON401 *externalRef0.UnauthorizedResponse + JSON403 *externalRef0.ForbiddenResponse + JSON500 *externalRef0.InternalServerErrorResponse +} + +// Status returns HTTPResponse.Status +func (r GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse struct { Body []byte HTTPResponse *http.Response @@ -1167,6 +1245,15 @@ func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDIdentitiesWithR return ParseGetApiV1OrganizationsOrganizationIDIdentitiesResponse(rsp) } +// GetApiV1OrganizationsOrganizationIDPhysicalnetworksWithResponse request returning *GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse +func (c *ClientWithResponses) GetApiV1OrganizationsOrganizationIDPhysicalnetworksWithResponse(ctx context.Context, organizationID OrganizationIDParameter, reqEditors ...RequestEditorFn) (*GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse, error) { + rsp, err := c.GetApiV1OrganizationsOrganizationIDPhysicalnetworks(ctx, organizationID, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse(rsp) +} + // PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBodyWithResponse request with arbitrary body returning *PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse func (c *ClientWithResponses) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBodyWithResponse(ctx context.Context, organizationID OrganizationIDParameter, projectID ProjectIDParameter, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse, error) { rsp, err := c.PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithBody(ctx, organizationID, projectID, contentType, body, reqEditors...) @@ -1327,6 +1414,60 @@ func ParseGetApiV1OrganizationsOrganizationIDIdentitiesResponse(rsp *http.Respon return response, nil } +// ParseGetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse parses an HTTP response from a GetApiV1OrganizationsOrganizationIDPhysicalnetworksWithResponse call +func ParseGetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse(rsp *http.Response) (*GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetApiV1OrganizationsOrganizationIDPhysicalnetworksResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PhysicalNetworksResponse + 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 +} + // ParsePostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse parses an HTTP response from a PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesWithResponse call func ParsePostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse(rsp *http.Response) (*PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/pkg/openapi/router.go b/pkg/openapi/router.go index 55c7081..b808b82 100644 --- a/pkg/openapi/router.go +++ b/pkg/openapi/router.go @@ -18,6 +18,9 @@ type ServerInterface interface { // (GET /api/v1/organizations/{organizationID}/identities) GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) + // (GET /api/v1/organizations/{organizationID}/physicalnetworks) + GetApiV1OrganizationsOrganizationIDPhysicalnetworks(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) + // (POST /api/v1/organizations/{organizationID}/projects/{projectID}/identities) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter) @@ -58,6 +61,11 @@ func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDIdentities(w http.Resp w.WriteHeader(http.StatusNotImplemented) } +// (GET /api/v1/organizations/{organizationID}/physicalnetworks) +func (_ Unimplemented) GetApiV1OrganizationsOrganizationIDPhysicalnetworks(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter) { + w.WriteHeader(http.StatusNotImplemented) +} + // (POST /api/v1/organizations/{organizationID}/projects/{projectID}/identities) func (_ Unimplemented) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities(w http.ResponseWriter, r *http.Request, organizationID OrganizationIDParameter, projectID ProjectIDParameter) { w.WriteHeader(http.StatusNotImplemented) @@ -145,6 +153,34 @@ func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDIdentities handler.ServeHTTP(w, r.WithContext(ctx)) } +// GetApiV1OrganizationsOrganizationIDPhysicalnetworks operation middleware +func (siw *ServerInterfaceWrapper) GetApiV1OrganizationsOrganizationIDPhysicalnetworks(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.GetApiV1OrganizationsOrganizationIDPhysicalnetworks(w, r, organizationID) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities operation middleware func (siw *ServerInterfaceWrapper) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -685,6 +721,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl 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}/physicalnetworks", wrapper.GetApiV1OrganizationsOrganizationIDPhysicalnetworks) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/api/v1/organizations/{organizationID}/projects/{projectID}/identities", wrapper.PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities) }) diff --git a/pkg/openapi/schema.go b/pkg/openapi/schema.go index 004619f..4e8997d 100644 --- a/pkg/openapi/schema.go +++ b/pkg/openapi/schema.go @@ -19,102 +19,103 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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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 6da5c4f..4bb123e 100644 --- a/pkg/openapi/server.spec.yaml +++ b/pkg/openapi/server.spec.yaml @@ -177,6 +177,28 @@ 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}/physicalnetworks: + description: |- + Manages physical networks. Physical networks are networks that may be required for + baremetal node provisioning e.g. a VLAN. Note that only a single provider network is currently + supported per identity, as identities are intended to exist per piece of infrastructure. + parameters: + - $ref: '#/components/parameters/organizationIDParameter' + get: + description: List physical networks. + security: + - oauth2Authentication: [] + responses: + '200': + $ref: '#/components/responses/physicalNetworksResponse' + '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}/identities/{identityID}/physicalnetworks: description: |- Manages physical networks. Physical networks are networks that may be required for @@ -633,11 +655,17 @@ components: type: object required: - metadata + - spec properties: metadata: $ref: 'https://raw.githubusercontent.com/unikorn-cloud/core/main/pkg/openapi/common.spec.yaml#/components/schemas/projectScopedResourceReadMetadata' spec: $ref: '#/components/schemas/physicalNetworkReadSpec' + physicalNetworksRead: + description: A list of physical networks. + type: array + items: + $ref: '#/components/schemas/physicalNetworkRead' externalNetwork: description: An Openstack external network. type: object @@ -821,6 +849,30 @@ components: openstack: vlanId: 1024 networkId: 824e8c4c-5858-4b02-9906-390e34561bf3 + physicalNetworksResponse: + description: A list of physical networks. + content: + application/json: + schema: + $ref: '#/components/schemas/physicalNetworksRead' + example: + - metadata: + id: a64f9269-36e0-4312-b8d1-52d93d569b7b + name: unused + organizationId: 9a8c6370-4065-4d4a-9da0-7678df40cd9d + projectId: e36c058a-8eba-4f5b-91f4-f6ffb983795c + creationTime: 2024-05-31T14:11:00Z + createdBy: john.doe@acme.com + provisioningStatus: provisioned + spec: + regionId: d891dbf0-0a01-4efc-ae3a-5d77f6d3424b + prefix: 192.168.0.0/24 + dnsNameservers: + - 8.8.8.8 + type: openstack + openstack: + vlanId: 1024 + networkId: 824e8c4c-5858-4b02-9906-390e34561bf3 externalNetworksResponse: description: A list of valid external networks. content: diff --git a/pkg/openapi/types.go b/pkg/openapi/types.go index 3044ae0..ed6630d 100644 --- a/pkg/openapi/types.go +++ b/pkg/openapi/types.go @@ -221,7 +221,7 @@ type PhysicalNetworkRead struct { Metadata externalRef0.ProjectScopedResourceReadMetadata `json:"metadata"` // Spec A phyical network's specification. - Spec *PhysicalNetworkReadSpec `json:"spec,omitempty"` + Spec PhysicalNetworkReadSpec `json:"spec"` } // PhysicalNetworkReadSpec A phyical network's specification. @@ -281,6 +281,9 @@ type PhysicalNetworkWriteSpec struct { Tags *TagList `json:"tags,omitempty"` } +// PhysicalNetworksRead A list of physical networks. +type PhysicalNetworksRead = []PhysicalNetworkRead + // 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 @@ -365,6 +368,9 @@ type ImagesResponse = Images // PhysicalNetworkResponse A physical network. type PhysicalNetworkResponse = PhysicalNetworkRead +// PhysicalNetworksResponse A list of physical networks. +type PhysicalNetworksResponse = PhysicalNetworksRead + // RegionsResponse A list of regions. type RegionsResponse = Regions