diff --git a/pkg/api/client/operations/get_openstack_metadata_parameters.go b/pkg/api/client/operations/get_openstack_metadata_parameters.go new file mode 100644 index 0000000000..7d15857f44 --- /dev/null +++ b/pkg/api/client/operations/get_openstack_metadata_parameters.go @@ -0,0 +1,114 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + "time" + + "golang.org/x/net/context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + + strfmt "github.com/go-openapi/strfmt" +) + +// NewGetOpenstackMetadataParams creates a new GetOpenstackMetadataParams object +// with the default values initialized. +func NewGetOpenstackMetadataParams() *GetOpenstackMetadataParams { + + return &GetOpenstackMetadataParams{ + + timeout: cr.DefaultTimeout, + } +} + +// NewGetOpenstackMetadataParamsWithTimeout creates a new GetOpenstackMetadataParams object +// with the default values initialized, and the ability to set a timeout on a request +func NewGetOpenstackMetadataParamsWithTimeout(timeout time.Duration) *GetOpenstackMetadataParams { + + return &GetOpenstackMetadataParams{ + + timeout: timeout, + } +} + +// NewGetOpenstackMetadataParamsWithContext creates a new GetOpenstackMetadataParams object +// with the default values initialized, and the ability to set a context for a request +func NewGetOpenstackMetadataParamsWithContext(ctx context.Context) *GetOpenstackMetadataParams { + + return &GetOpenstackMetadataParams{ + + Context: ctx, + } +} + +// NewGetOpenstackMetadataParamsWithHTTPClient creates a new GetOpenstackMetadataParams object +// with the default values initialized, and the ability to set a custom HTTPClient for a request +func NewGetOpenstackMetadataParamsWithHTTPClient(client *http.Client) *GetOpenstackMetadataParams { + + return &GetOpenstackMetadataParams{ + HTTPClient: client, + } +} + +/*GetOpenstackMetadataParams contains all the parameters to send to the API endpoint +for the get openstack metadata operation typically these are written to a http.Request +*/ +type GetOpenstackMetadataParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithTimeout adds the timeout to the get openstack metadata params +func (o *GetOpenstackMetadataParams) WithTimeout(timeout time.Duration) *GetOpenstackMetadataParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get openstack metadata params +func (o *GetOpenstackMetadataParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get openstack metadata params +func (o *GetOpenstackMetadataParams) WithContext(ctx context.Context) *GetOpenstackMetadataParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get openstack metadata params +func (o *GetOpenstackMetadataParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get openstack metadata params +func (o *GetOpenstackMetadataParams) WithHTTPClient(client *http.Client) *GetOpenstackMetadataParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get openstack metadata params +func (o *GetOpenstackMetadataParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *GetOpenstackMetadataParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/api/client/operations/get_openstack_metadata_responses.go b/pkg/api/client/operations/get_openstack_metadata_responses.go new file mode 100644 index 0000000000..04ed3758ed --- /dev/null +++ b/pkg/api/client/operations/get_openstack_metadata_responses.go @@ -0,0 +1,112 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + + strfmt "github.com/go-openapi/strfmt" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// GetOpenstackMetadataReader is a Reader for the GetOpenstackMetadata structure. +type GetOpenstackMetadataReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetOpenstackMetadataReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + + case 200: + result := NewGetOpenstackMetadataOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + + default: + result := NewGetOpenstackMetadataDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetOpenstackMetadataOK creates a GetOpenstackMetadataOK with default headers values +func NewGetOpenstackMetadataOK() *GetOpenstackMetadataOK { + return &GetOpenstackMetadataOK{} +} + +/*GetOpenstackMetadataOK handles this case with default header values. + +OK +*/ +type GetOpenstackMetadataOK struct { + Payload *models.OpenstackMetadata +} + +func (o *GetOpenstackMetadataOK) Error() string { + return fmt.Sprintf("[GET /api/v1/openstack/metadata][%d] getOpenstackMetadataOK %+v", 200, o.Payload) +} + +func (o *GetOpenstackMetadataOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.OpenstackMetadata) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetOpenstackMetadataDefault creates a GetOpenstackMetadataDefault with default headers values +func NewGetOpenstackMetadataDefault(code int) *GetOpenstackMetadataDefault { + return &GetOpenstackMetadataDefault{ + _statusCode: code, + } +} + +/*GetOpenstackMetadataDefault handles this case with default header values. + +Error +*/ +type GetOpenstackMetadataDefault struct { + _statusCode int + + Payload *models.Error +} + +// Code gets the status code for the get openstack metadata default response +func (o *GetOpenstackMetadataDefault) Code() int { + return o._statusCode +} + +func (o *GetOpenstackMetadataDefault) Error() string { + return fmt.Sprintf("[GET /api/v1/openstack/metadata][%d] GetOpenstackMetadata default %+v", o._statusCode, o.Payload) +} + +func (o *GetOpenstackMetadataDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/api/client/operations/operations_client.go b/pkg/api/client/operations/operations_client.go index 42b9d2defa..547ee48859 100644 --- a/pkg/api/client/operations/operations_client.go +++ b/pkg/api/client/operations/operations_client.go @@ -111,6 +111,35 @@ func (a *Client) GetClusterInfo(params *GetClusterInfoParams, authInfo runtime.C } +/* +GetOpenstackMetadata grabs bag of openstack metadata +*/ +func (a *Client) GetOpenstackMetadata(params *GetOpenstackMetadataParams, authInfo runtime.ClientAuthInfoWriter) (*GetOpenstackMetadataOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetOpenstackMetadataParams() + } + + result, err := a.transport.Submit(&runtime.ClientOperation{ + ID: "GetOpenstackMetadata", + Method: "GET", + PathPattern: "/api/v1/openstack/metadata", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetOpenstackMetadataReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + }) + if err != nil { + return nil, err + } + return result.(*GetOpenstackMetadataOK), nil + +} + /* Info gets info about kubernikus */ diff --git a/pkg/api/handlers/get_openstack_metadata.go b/pkg/api/handlers/get_openstack_metadata.go new file mode 100644 index 0000000000..e0ef530c6c --- /dev/null +++ b/pkg/api/handlers/get_openstack_metadata.go @@ -0,0 +1,43 @@ +package handlers + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/sapcc/kubernikus/pkg/api" + "github.com/sapcc/kubernikus/pkg/api/models" + "github.com/sapcc/kubernikus/pkg/api/rest/operations" + "github.com/sapcc/kubernikus/pkg/client/openstack" +) + +func NewGetOpenstackMetadata(rt *api.Runtime) operations.GetOpenstackMetadataHandler { + return &getOpenstackMetadata{rt} +} + +type getOpenstackMetadata struct { + *api.Runtime +} + +func (d *getOpenstackMetadata) Handle(params operations.GetOpenstackMetadataParams, principal *models.Principal) middleware.Responder { + tokenID := params.HTTPRequest.Header.Get("X-Auth-Token") + + authOptions := &tokens.AuthOptions{ + IdentityEndpoint: principal.AuthURL, + TokenID: tokenID, + AllowReauth: true, + Scope: tokens.Scope{ + ProjectID: principal.Account, + }, + } + + client, err := openstack.NewScopedClient(authOptions) + if err != nil { + return NewErrorResponse(&operations.GetOpenstackMetadataDefault{}, 500, err.Error()) + } + + openstackMetadata, err := client.GetMetadata() + if err != nil { + return NewErrorResponse(&operations.GetOpenstackMetadataDefault{}, 500, err.Error()) + } + + return operations.NewGetOpenstackMetadataOK().WithPayload(openstackMetadata) +} diff --git a/pkg/api/models/openstack_metadata.go b/pkg/api/models/openstack_metadata.go new file mode 100644 index 0000000000..f67f57ebe8 --- /dev/null +++ b/pkg/api/models/openstack_metadata.go @@ -0,0 +1,535 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "strconv" + + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" +) + +// OpenstackMetadata openstack metadata +// swagger:model OpenstackMetadata + +type OpenstackMetadata struct { + + // flavors + Flavors []*Flavor `json:"flavors"` + + // key pairs + KeyPairs []*KeyPair `json:"keyPairs"` + + // routers + Routers []*Router `json:"routers"` + + // security groups + SecurityGroups []*SecurityGroup `json:"securityGroups"` +} + +/* polymorph OpenstackMetadata flavors false */ + +/* polymorph OpenstackMetadata keyPairs false */ + +/* polymorph OpenstackMetadata routers false */ + +/* polymorph OpenstackMetadata securityGroups false */ + +// Validate validates this openstack metadata +func (m *OpenstackMetadata) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateFlavors(formats); err != nil { + // prop + res = append(res, err) + } + + if err := m.validateKeyPairs(formats); err != nil { + // prop + res = append(res, err) + } + + if err := m.validateRouters(formats); err != nil { + // prop + res = append(res, err) + } + + if err := m.validateSecurityGroups(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenstackMetadata) validateFlavors(formats strfmt.Registry) error { + + if swag.IsZero(m.Flavors) { // not required + return nil + } + + for i := 0; i < len(m.Flavors); i++ { + + if swag.IsZero(m.Flavors[i]) { // not required + continue + } + + if m.Flavors[i] != nil { + + if err := m.Flavors[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("flavors" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *OpenstackMetadata) validateKeyPairs(formats strfmt.Registry) error { + + if swag.IsZero(m.KeyPairs) { // not required + return nil + } + + for i := 0; i < len(m.KeyPairs); i++ { + + if swag.IsZero(m.KeyPairs[i]) { // not required + continue + } + + if m.KeyPairs[i] != nil { + + if err := m.KeyPairs[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("keyPairs" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *OpenstackMetadata) validateRouters(formats strfmt.Registry) error { + + if swag.IsZero(m.Routers) { // not required + return nil + } + + for i := 0; i < len(m.Routers); i++ { + + if swag.IsZero(m.Routers[i]) { // not required + continue + } + + if m.Routers[i] != nil { + + if err := m.Routers[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("routers" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *OpenstackMetadata) validateSecurityGroups(formats strfmt.Registry) error { + + if swag.IsZero(m.SecurityGroups) { // not required + return nil + } + + for i := 0; i < len(m.SecurityGroups); i++ { + + if swag.IsZero(m.SecurityGroups[i]) { // not required + continue + } + + if m.SecurityGroups[i] != nil { + + if err := m.SecurityGroups[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("securityGroups" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *OpenstackMetadata) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenstackMetadata) UnmarshalBinary(b []byte) error { + var res OpenstackMetadata + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// Flavor flavor +// swagger:model Flavor + +type Flavor struct { + + // id + ID string `json:"id,omitempty"` + + // name + Name string `json:"name,omitempty"` +} + +/* polymorph Flavor id false */ + +/* polymorph Flavor name false */ + +// Validate validates this flavor +func (m *Flavor) Validate(formats strfmt.Registry) error { + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// MarshalBinary interface implementation +func (m *Flavor) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Flavor) UnmarshalBinary(b []byte) error { + var res Flavor + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// KeyPair key pair +// swagger:model KeyPair + +type KeyPair struct { + + // name + Name string `json:"name,omitempty"` +} + +/* polymorph KeyPair name false */ + +// Validate validates this key pair +func (m *KeyPair) Validate(formats strfmt.Registry) error { + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// MarshalBinary interface implementation +func (m *KeyPair) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *KeyPair) UnmarshalBinary(b []byte) error { + var res KeyPair + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// Router router +// swagger:model Router + +type Router struct { + + // id + ID string `json:"id,omitempty"` + + // name + Name string `json:"name,omitempty"` + + // networks + Networks []*Network `json:"networks"` +} + +/* polymorph Router id false */ + +/* polymorph Router name false */ + +/* polymorph Router networks false */ + +// Validate validates this router +func (m *Router) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateNetworks(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Router) validateNetworks(formats strfmt.Registry) error { + + if swag.IsZero(m.Networks) { // not required + return nil + } + + for i := 0; i < len(m.Networks); i++ { + + if swag.IsZero(m.Networks[i]) { // not required + continue + } + + if m.Networks[i] != nil { + + if err := m.Networks[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("networks" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *Router) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Router) UnmarshalBinary(b []byte) error { + var res Router + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// Network network +// swagger:model Network + +type Network struct { + + // id + ID string `json:"id,omitempty"` + + // name + Name string `json:"name,omitempty"` + + // subnets + Subnets []*Subnet `json:"subnets"` +} + +/* polymorph Network id false */ + +/* polymorph Network name false */ + +/* polymorph Network subnets false */ + +// Validate validates this network +func (m *Network) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateSubnets(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Network) validateSubnets(formats strfmt.Registry) error { + + if swag.IsZero(m.Subnets) { // not required + return nil + } + + for i := 0; i < len(m.Subnets); i++ { + + if swag.IsZero(m.Subnets[i]) { // not required + continue + } + + if m.Subnets[i] != nil { + + if err := m.Subnets[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("subnets" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *Network) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Network) UnmarshalBinary(b []byte) error { + var res Network + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// Subnet subnet +// swagger:model Subnet + +type Subnet struct { + + // c ID r + CIDR string `json:"CIDR,omitempty"` + + // id + ID string `json:"id,omitempty"` + + // name + Name string `json:"name,omitempty"` +} + +/* polymorph Subnet CIDR false */ + +/* polymorph Subnet id false */ + +/* polymorph Subnet name false */ + +// Validate validates this subnet +func (m *Subnet) Validate(formats strfmt.Registry) error { + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// MarshalBinary interface implementation +func (m *Subnet) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Subnet) UnmarshalBinary(b []byte) error { + var res Subnet + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// SecurityGroup security group +// swagger:model SecurityGroup + +type SecurityGroup struct { + + // id + ID string `json:"id,omitempty"` + + // name + Name string `json:"name,omitempty"` +} + +/* polymorph SecurityGroup id false */ + +/* polymorph SecurityGroup name false */ + +// Validate validates this security group +func (m *SecurityGroup) Validate(formats strfmt.Registry) error { + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// MarshalBinary interface implementation +func (m *SecurityGroup) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *SecurityGroup) UnmarshalBinary(b []byte) error { + var res SecurityGroup + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/api/rest/configure.go b/pkg/api/rest/configure.go index 88cbf123c4..108c9a2f58 100644 --- a/pkg/api/rest/configure.go +++ b/pkg/api/rest/configure.go @@ -42,6 +42,7 @@ func Configure(api *operations.KubernikusAPI, rt *apipkg.Runtime) { api.UpdateClusterHandler = handlers.NewUpdateCluster(rt) api.GetClusterCredentialsHandler = handlers.NewGetClusterCredentials(rt) api.GetClusterInfoHandler = handlers.NewGetClusterInfo(rt) + api.GetOpenstackMetadataHandler = handlers.NewGetOpenstackMetadata(rt) api.ServerShutdown = func() {} diff --git a/pkg/api/rest/operations/get_openstack_metadata.go b/pkg/api/rest/operations/get_openstack_metadata.go new file mode 100644 index 0000000000..5a979050b3 --- /dev/null +++ b/pkg/api/rest/operations/get_openstack_metadata.go @@ -0,0 +1,73 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + middleware "github.com/go-openapi/runtime/middleware" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// GetOpenstackMetadataHandlerFunc turns a function with the right signature into a get openstack metadata handler +type GetOpenstackMetadataHandlerFunc func(GetOpenstackMetadataParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetOpenstackMetadataHandlerFunc) Handle(params GetOpenstackMetadataParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetOpenstackMetadataHandler interface for that can handle valid get openstack metadata params +type GetOpenstackMetadataHandler interface { + Handle(GetOpenstackMetadataParams, *models.Principal) middleware.Responder +} + +// NewGetOpenstackMetadata creates a new http.Handler for the get openstack metadata operation +func NewGetOpenstackMetadata(ctx *middleware.Context, handler GetOpenstackMetadataHandler) *GetOpenstackMetadata { + return &GetOpenstackMetadata{Context: ctx, Handler: handler} +} + +/*GetOpenstackMetadata swagger:route GET /api/v1/openstack/metadata getOpenstackMetadata + +Grab bag of openstack metadata + +*/ +type GetOpenstackMetadata struct { + Context *middleware.Context + Handler GetOpenstackMetadataHandler +} + +func (o *GetOpenstackMetadata) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewGetOpenstackMetadataParams() + + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + r = aCtx + } + var principal *models.Principal + if uprinc != nil { + principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/api/rest/operations/get_openstack_metadata_parameters.go b/pkg/api/rest/operations/get_openstack_metadata_parameters.go new file mode 100644 index 0000000000..06e7ccae41 --- /dev/null +++ b/pkg/api/rest/operations/get_openstack_metadata_parameters.go @@ -0,0 +1,42 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetOpenstackMetadataParams creates a new GetOpenstackMetadataParams object +// with the default values initialized. +func NewGetOpenstackMetadataParams() GetOpenstackMetadataParams { + var () + return GetOpenstackMetadataParams{} +} + +// GetOpenstackMetadataParams contains all the bound params for the get openstack metadata operation +// typically these are obtained from a http.Request +// +// swagger:parameters GetOpenstackMetadata +type GetOpenstackMetadataParams struct { + + // HTTP Request Object + HTTPRequest *http.Request +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls +func (o *GetOpenstackMetadataParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/api/rest/operations/get_openstack_metadata_responses.go b/pkg/api/rest/operations/get_openstack_metadata_responses.go new file mode 100644 index 0000000000..6a4a2c6a23 --- /dev/null +++ b/pkg/api/rest/operations/get_openstack_metadata_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/sapcc/kubernikus/pkg/api/models" +) + +// GetOpenstackMetadataOKCode is the HTTP code returned for type GetOpenstackMetadataOK +const GetOpenstackMetadataOKCode int = 200 + +/*GetOpenstackMetadataOK OK + +swagger:response getOpenstackMetadataOK +*/ +type GetOpenstackMetadataOK struct { + + /* + In: Body + */ + Payload *models.OpenstackMetadata `json:"body,omitempty"` +} + +// NewGetOpenstackMetadataOK creates GetOpenstackMetadataOK with default headers values +func NewGetOpenstackMetadataOK() *GetOpenstackMetadataOK { + return &GetOpenstackMetadataOK{} +} + +// WithPayload adds the payload to the get openstack metadata o k response +func (o *GetOpenstackMetadataOK) WithPayload(payload *models.OpenstackMetadata) *GetOpenstackMetadataOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get openstack metadata o k response +func (o *GetOpenstackMetadataOK) SetPayload(payload *models.OpenstackMetadata) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetOpenstackMetadataOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*GetOpenstackMetadataDefault Error + +swagger:response getOpenstackMetadataDefault +*/ +type GetOpenstackMetadataDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetOpenstackMetadataDefault creates GetOpenstackMetadataDefault with default headers values +func NewGetOpenstackMetadataDefault(code int) *GetOpenstackMetadataDefault { + if code <= 0 { + code = 500 + } + + return &GetOpenstackMetadataDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the get openstack metadata default response +func (o *GetOpenstackMetadataDefault) WithStatusCode(code int) *GetOpenstackMetadataDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the get openstack metadata default response +func (o *GetOpenstackMetadataDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the get openstack metadata default response +func (o *GetOpenstackMetadataDefault) WithPayload(payload *models.Error) *GetOpenstackMetadataDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get openstack metadata default response +func (o *GetOpenstackMetadataDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetOpenstackMetadataDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/api/rest/operations/get_openstack_metadata_urlbuilder.go b/pkg/api/rest/operations/get_openstack_metadata_urlbuilder.go new file mode 100644 index 0000000000..2bc35d926f --- /dev/null +++ b/pkg/api/rest/operations/get_openstack_metadata_urlbuilder.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetOpenstackMetadataURL generates an URL for the get openstack metadata operation +type GetOpenstackMetadataURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetOpenstackMetadataURL) WithBasePath(bp string) *GetOpenstackMetadataURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetOpenstackMetadataURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetOpenstackMetadataURL) Build() (*url.URL, error) { + var result url.URL + + var _path = "/api/v1/openstack/metadata" + + _basePath := o._basePath + result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetOpenstackMetadataURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetOpenstackMetadataURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetOpenstackMetadataURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetOpenstackMetadataURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetOpenstackMetadataURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetOpenstackMetadataURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/api/rest/operations/kubernikus_api.go b/pkg/api/rest/operations/kubernikus_api.go index c15ef9c2c2..4f07e1fa65 100644 --- a/pkg/api/rest/operations/kubernikus_api.go +++ b/pkg/api/rest/operations/kubernikus_api.go @@ -46,6 +46,9 @@ func NewKubernikusAPI(spec *loads.Document) *KubernikusAPI { GetClusterInfoHandler: GetClusterInfoHandlerFunc(func(params GetClusterInfoParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation GetClusterInfo has not yet been implemented") }), + GetOpenstackMetadataHandler: GetOpenstackMetadataHandlerFunc(func(params GetOpenstackMetadataParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation GetOpenstackMetadata has not yet been implemented") + }), InfoHandler: InfoHandlerFunc(func(params InfoParams) middleware.Responder { return middleware.NotImplemented("operation Info has not yet been implemented") }), @@ -114,6 +117,8 @@ type KubernikusAPI struct { GetClusterCredentialsHandler GetClusterCredentialsHandler // GetClusterInfoHandler sets the operation handler for the get cluster info operation GetClusterInfoHandler GetClusterInfoHandler + // GetOpenstackMetadataHandler sets the operation handler for the get openstack metadata operation + GetOpenstackMetadataHandler GetOpenstackMetadataHandler // InfoHandler sets the operation handler for the info operation InfoHandler InfoHandler // ListAPIVersionsHandler sets the operation handler for the list API versions operation @@ -205,6 +210,10 @@ func (o *KubernikusAPI) Validate() error { unregistered = append(unregistered, "GetClusterInfoHandler") } + if o.GetOpenstackMetadataHandler == nil { + unregistered = append(unregistered, "GetOpenstackMetadataHandler") + } + if o.InfoHandler == nil { unregistered = append(unregistered, "InfoHandler") } @@ -346,6 +355,11 @@ func (o *KubernikusAPI) initHandlerCache() { } o.handlers["GET"]["/api/v1/clusters/{name}/info"] = NewGetClusterInfo(o.context, o.GetClusterInfoHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/api/v1/openstack/metadata"] = NewGetOpenstackMetadata(o.context, o.GetOpenstackMetadataHandler) + if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } diff --git a/pkg/api/spec/embedded_spec.go b/pkg/api/spec/embedded_spec.go index b5c7034dc4..63876abbdd 100644 --- a/pkg/api/spec/embedded_spec.go +++ b/pkg/api/spec/embedded_spec.go @@ -203,6 +203,23 @@ func init() { } ] }, + "/api/v1/openstack/metadata": { + "get": { + "summary": "Grab bag of openstack metadata", + "operationId": "GetOpenstackMetadata", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/OpenstackMetadata" + } + }, + "default": { + "$ref": "#/responses/errorResponse" + } + } + } + }, "/info": { "get": { "summary": "Get info about Kubernikus", @@ -456,6 +473,100 @@ func init() { }, "x-nullable": false }, + "OpenstackMetadata": { + "properties": { + "flavors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "x-go-name": "Flavor" + } + }, + "keyPairs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "x-go-name": "KeyPair" + } + }, + "routers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "networks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "subnets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "CIDR": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "x-go-name": "Subnet" + } + } + }, + "x-go-name": "Network" + } + } + }, + "x-go-name": "Router" + } + }, + "securityGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "x-go-name": "SecurityGroup" + } + } + } + }, "OpenstackSpec": { "type": "object", "properties": { diff --git a/pkg/client/openstack/client.go b/pkg/client/openstack/client.go index 9d9c4bfa20..f83815d761 100644 --- a/pkg/client/openstack/client.go +++ b/pkg/client/openstack/client.go @@ -49,6 +49,7 @@ type client struct { roleNameToID sync.Map } + type Client interface { CreateNode(*kubernikus_v1.Kluster, *models.NodePool, []byte) (string, error) DeleteNode(*kubernikus_v1.Kluster, string) error diff --git a/pkg/client/openstack/metadata.go b/pkg/client/openstack/metadata.go new file mode 100644 index 0000000000..47cb0635c2 --- /dev/null +++ b/pkg/client/openstack/metadata.go @@ -0,0 +1,228 @@ +package openstack + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" + "github.com/gophercloud/gophercloud/pagination" + "github.com/sapcc/kubernikus/pkg/api/models" +) + +type scopedClient struct { + providerClient *gophercloud.ProviderClient + networkClient *gophercloud.ServiceClient + computeClient *gophercloud.ServiceClient + identityClient *gophercloud.ServiceClient +} + +type ScopedClient interface { + GetMetadata() (*models.OpenstackMetadata, error) +} + +func NewScopedClient(authOptions *tokens.AuthOptions) (ScopedClient, error) { + var err error + client := &scopedClient{} + + if client.providerClient, err = openstack.NewClient(authOptions.IdentityEndpoint); err != nil { + return nil, err + } + + if err := openstack.AuthenticateV3(client.providerClient, authOptions, gophercloud.EndpointOpts{}); err != nil { + return nil, err + } + + if client.identityClient, err = openstack.NewIdentityV3(client.providerClient, gophercloud.EndpointOpts{}); err != nil { + return nil, err + } + + if client.computeClient, err = openstack.NewComputeV2(client.providerClient, gophercloud.EndpointOpts{}); err != nil { + return nil, err + } + + if client.networkClient, err = openstack.NewNetworkV2(client.providerClient, gophercloud.EndpointOpts{}); err != nil { + return nil, err + } + + return client, nil +} + +func (c *scopedClient) GetMetadata() (*models.OpenstackMetadata, error) { + var err error + metadata := &models.OpenstackMetadata{} + + if metadata.Routers, err = c.getRouters(); err != nil { + return nil, err + } + + if metadata.KeyPairs, err = c.getKeyPairs(); err != nil { + return nil, err + } + + if metadata.SecurityGroups, err = c.getSecurityGroups(); err != nil { + return nil, err + } + + if metadata.Flavors, err = c.getFlavors(); err != nil { + return nil, err + } + + return metadata, nil +} + +func (c *scopedClient) getRouters() ([]*models.Router, error) { + result := []*models.Router{} + + err := routers.List(c.networkClient, routers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + if routerList, err := routers.ExtractRouters(page); err != nil { + return false, err + } else { + for _, router := range routerList { + result = append(result, &models.Router{ID: router.ID, Name: router.Name}) + } + } + return true, nil + }) + + if err != nil { + return nil, err + } + + for _, router := range result { + if router.Networks, err = c.getNetworks(router); err != nil { + return nil, err + } + } + + return result, nil +} + +func (c *scopedClient) getNetworks(router *models.Router) ([]*models.Network, error) { + result := []*models.Network{} + + networkIDs, err := c.getRouterNetworkIDs(router) + if err != nil { + return nil, err + } + + for _, networkID := range networkIDs { + network, err := networks.Get(c.networkClient, networkID).Extract() + if err != nil { + return nil, err + } + result = append(result, &models.Network{ID: network.ID, Name: network.Name}) + } + + for _, network := range result { + if network.Subnets, err = c.getSubnets(network); err != nil { + return nil, err + } + } + + return result, nil +} + +func (c *scopedClient) getRouterNetworkIDs(router *models.Router) ([]string, error) { + result := []string{} + + err := ports.List(c.networkClient, ports.ListOpts{DeviceID: router.ID, DeviceOwner: "network:router_interface"}).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + for _, port := range portList { + result = append(result, port.NetworkID) + } + return true, nil + }) + + return result, err +} + +func (c *scopedClient) getSubnetIDs(network *models.Network) ([]string, error) { + result, err := networks.Get(c.networkClient, network.ID).Extract() + if err != nil { + return nil, err + } + + return result.Subnets, nil +} + +func (c *scopedClient) getSubnets(network *models.Network) ([]*models.Subnet, error) { + result := []*models.Subnet{} + + subnetIDs, err := c.getSubnetIDs(network) + if err != nil { + return nil, err + } + + for _, subnetID := range subnetIDs { + subnet, err := subnets.Get(c.networkClient, subnetID).Extract() + if err != nil { + return nil, err + } + result = append(result, &models.Subnet{ID: subnet.ID, Name: subnet.Name, CIDR: subnet.CIDR}) + } + + return result, nil +} + +func (c *scopedClient) getKeyPairs() ([]*models.KeyPair, error) { + result := []*models.KeyPair{} + + pager, err := keypairs.List(c.computeClient).AllPages() + if err != nil { + return nil, err + } + + keyList, err := keypairs.ExtractKeyPairs(pager) + if err != nil { + return nil, err + } + + for _, key := range keyList { + result = append(result, &models.KeyPair{Name: key.Name}) + } + + return result, nil +} + +func (c *scopedClient) getSecurityGroups() ([]*models.SecurityGroup, error) { + result := []*models.SecurityGroup{} + + err := secgroups.List(c.computeClient).EachPage(func(page pagination.Page) (bool, error) { + secGroupList, err := secgroups.ExtractSecurityGroups(page) + if err != nil { + return false, err + } + for _, secGroup := range secGroupList { + result = append(result, &models.SecurityGroup{ID: secGroup.ID, Name: secGroup.Name}) + } + return true, nil + }) + + return result, err +} + +func (c *scopedClient) getFlavors() ([]*models.Flavor, error) { + result := []*models.Flavor{} + + err := flavors.ListDetail(c.computeClient, &flavors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + list, err := flavors.ExtractFlavors(page) + if err != nil { + return false, err + } + for _, entry := range list { + result = append(result, &models.Flavor{ID: entry.ID, Name: entry.Name}) + } + return true, nil + }) + + return result, err +} diff --git a/swagger.yml b/swagger.yml index e63d0ca3d0..5b446ed4e3 100644 --- a/swagger.yml +++ b/swagger.yml @@ -40,6 +40,17 @@ paths: description: OK schema: $ref: '#/definitions/ApiVersions' + /api/v1/openstack/metadata: + get: + operationId: GetOpenstackMetadata + summary: Grab bag of openstack metadata + responses: + '200': + description: OK + schema: + $ref: '#/definitions/OpenstackMetadata' + default: + $ref: '#/responses/errorResponse' /api/v1/clusters/: get: operationId: ListClusters @@ -147,9 +158,76 @@ paths: default: $ref: '#/responses/errorResponse' definitions: + OpenstackMetadata: + properties: + flavors: + type: array + items: + type: object + x-go-name: Flavor + properties: + name: + type: string + id: + type: string + securityGroups: + type: array + items: + type: object + x-go-name: SecurityGroup + properties: + name: + type: string + id: + type: string + keyPairs: + type: array + items: + type: object + x-go-name: KeyPair + properties: + name: + type: string + routers: + type: array + items: + type: object + x-go-name: Router + properties: + name: + type: string + id: + type: string + networks: + type: array + items: + type: object + x-go-name: Network + properties: + name: + type: string + id: + type: string + subnets: + type: array + items: + type: object + x-go-name: Subnet + properties: + name: + type: string + id: + type: string + CIDR: + type: string KlusterPhase: type: string - enum: [Pending, Creating, Running, Terminating, Error] + enum: + - Pending + - Creating + - Running + - Terminating + - Error Info: properties: version: @@ -187,7 +265,6 @@ definitions: type: array items: type: string - Kluster: type: object required: @@ -213,16 +290,18 @@ definitions: default: 198.18.128.0/17 x-nullable: false type: string - pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' + pattern: >- + ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ clusterCIDR: description: CIDR Range for Pods in the cluster. Can not be updated. default: 198.19.0.0/16 type: string x-nullable: false - pattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' + pattern: >- + ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ nodePools: type: array - x-omitempty: true #should work with go-swagger > 0.12.0 + x-omitempty: true items: $ref: '#/definitions/NodePool' advertiseAddress: @@ -235,8 +314,6 @@ definitions: x-nullable: false type: string default: cluster.local - #domain: - # type: string name: type: string readOnly: true @@ -253,7 +330,6 @@ definitions: lbSubnetID: x-go-name: LBSubnetID type: string - NodePool: x-nullable: false type: object @@ -270,7 +346,7 @@ definitions: type: integer maximum: 127 minimum: 0 - default: 0 + default: 0 flavor: type: string x-nullable: false @@ -288,7 +364,6 @@ definitions: type: boolean repair: type: boolean - KlusterStatus: readOnly: true x-nullable: false @@ -369,3 +444,4 @@ definitions: description: link to help page explaining the error in more detail type: string format: uri +