Cloud migrations: create route to delete token (#88297)

* Cloud migrations: create route to delete token

* gcom.DeleteToken returns ErrTokenNotFound instead of a boolean

* remove unnecessary comment

* make openapi3-gen && yarn run rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts

* gcom stub: implement DeleteToken
This commit is contained in:
Bruno
2024-05-31 10:03:43 -03:00
committed by GitHub
parent 3e872bb77e
commit 33b9544047
11 changed files with 230 additions and 27 deletions
+41
View File
@@ -50,6 +50,7 @@ func (cma *CloudMigrationAPI) registerEndpoints() {
cloudMigrationRoute.Get("/migration/run/:runUID", routing.Wrap(cma.GetMigrationRun))
cloudMigrationRoute.Get("/token", routing.Wrap(cma.GetToken))
cloudMigrationRoute.Post("/token", routing.Wrap(cma.CreateToken))
cloudMigrationRoute.Delete("/token/:uid", routing.Wrap(cma.DeleteToken))
}, middleware.ReqOrgAdmin)
}
@@ -112,6 +113,34 @@ func (cma *CloudMigrationAPI) CreateToken(c *contextmodel.ReqContext) response.R
return response.JSON(http.StatusOK, CreateAccessTokenResponseDTO(resp))
}
// swagger:route DELETE /cloudmigration/token/{uid} migrations deleteCloudMigrationToken
//
// Deletes a cloud migration token.
//
// Responses:
// 204: cloudMigrationDeleteTokenResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
func (cma *CloudMigrationAPI) DeleteToken(c *contextmodel.ReqContext) response.Response {
ctx, span := cma.tracer.Start(c.Req.Context(), "MigrationAPI.DeleteToken")
defer span.End()
logger := cma.log.FromContext(ctx)
uid := web.Params(c.Req)[":uid"]
if err := util.ValidateUID(uid); err != nil {
return response.Error(http.StatusBadRequest, "invalid migration uid", err)
}
if err := cma.cloudMigrationService.DeleteToken(ctx, uid); err != nil {
logger.Error("deleting cloud migration token", "err", err.Error())
return response.ErrOrFallback(http.StatusInternalServerError, "deleting cloud migration token", err)
}
return response.Empty(http.StatusNoContent)
}
// swagger:route GET /cloudmigration/migration migrations getMigrationList
//
// Get a list of all cloud migrations.
@@ -394,3 +423,15 @@ type CloudMigrationCreateTokenResponse struct {
type CreateAccessTokenResponseDTO struct {
Token string `json:"token"`
}
// swagger:parameters deleteCloudMigrationToken
type DeleteCloudMigrationToken struct {
// UID of a cloud migration token
//
// in: path
UID string `json:"uid"`
}
// swagger:response cloudMigrationDeleteTokenResponse
type CloudMigrationDeleteTokenResponse struct {
}
@@ -13,6 +13,7 @@ type Service interface {
CreateToken(context.Context) (CreateAccessTokenResponse, error)
// Sends a request to CMS to test the token.
ValidateToken(context.Context, CloudMigration) error
DeleteToken(ctx context.Context, uid string) error
CreateMigration(context.Context, CloudMigrationRequest) (*CloudMigrationResponse, error)
GetMigration(ctx context.Context, uid string) (*CloudMigration, error)
@@ -4,6 +4,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
@@ -25,6 +26,8 @@ import (
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
// Service Define the cloudmigration.Service Implementation.
@@ -106,8 +109,13 @@ func ProvideService(
s.cfg.StackID = "12345"
}
if err := s.registerMetrics(prom, s.metrics); err != nil {
s.log.Warn("error registering prom metrics", "error", err.Error())
if err := prom.Register(s.metrics); err != nil {
var alreadyRegisterErr prometheus.AlreadyRegisteredError
if errors.As(err, &alreadyRegisterErr) {
s.log.Warn("cloud migration metrics already registered")
} else {
return s, fmt.Errorf("registering cloud migration metrics: %w", err)
}
}
return s, nil
@@ -278,6 +286,35 @@ func (s *Service) ValidateToken(ctx context.Context, cm cloudmigration.CloudMigr
return nil
}
func (s *Service) DeleteToken(ctx context.Context, tokenID string) error {
ctx, span := s.tracer.Start(ctx, "CloudMigrationService.DeleteToken", trace.WithAttributes(attribute.String("tokenID", tokenID)))
defer span.End()
logger := s.log.FromContext(ctx)
requestID := tracing.TraceIDFromContext(ctx, false)
timeoutCtx, cancel := context.WithTimeout(ctx, s.cfg.CloudMigration.FetchInstanceTimeout)
defer cancel()
instance, err := s.gcomService.GetInstanceByID(timeoutCtx, requestID, s.cfg.StackID)
if err != nil {
return fmt.Errorf("fetching instance by id: id=%s %w", s.cfg.StackID, err)
}
logger.Info("found instance", "instanceID", instance.ID)
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.DeleteTokenTimeout)
defer cancel()
if err := s.gcomService.DeleteToken(timeoutCtx, gcom.DeleteTokenParams{
RequestID: tracing.TraceIDFromContext(ctx, false),
Region: instance.RegionSlug,
TokenID: tokenID,
}); err != nil && !errors.Is(err, gcom.ErrTokenNotFound) {
return fmt.Errorf("deleting cloud migration token: tokenID=%s %w", tokenID, err)
}
logger.Info("deleted cloud migration token", "tokenID", tokenID)
s.metrics.accessTokenDeleted.With(prometheus.Labels{"slug": s.cfg.Slug}).Inc()
return nil
}
func (s *Service) GetMigration(ctx context.Context, uid string) (*cloudmigration.CloudMigration, error) {
ctx, span := s.tracer.Start(ctx, "CloudMigrationService.GetMigration")
defer span.End()
@@ -19,6 +19,11 @@ func (s *NoopServiceImpl) GetToken(ctx context.Context) (gcom.TokenView, error)
func (s *NoopServiceImpl) CreateToken(ctx context.Context) (cloudmigration.CreateAccessTokenResponse, error) {
return cloudmigration.CreateAccessTokenResponse{}, cloudmigration.ErrFeatureDisabledError
}
func (s *NoopServiceImpl) DeleteToken(ctx context.Context, uid string) error {
return cloudmigration.ErrFeatureDisabledError
}
func (s *NoopServiceImpl) ValidateToken(ctx context.Context, cm cloudmigration.CloudMigration) error {
return cloudmigration.ErrFeatureDisabledError
}
@@ -74,3 +74,8 @@ func (client *gcomStub) CreateToken(ctx context.Context, params gcom.CreateToken
}
return token, nil
}
func (client *gcomStub) DeleteToken(ctx context.Context, params gcom.DeleteTokenParams) error {
client.token = nil
return nil
}
@@ -1,29 +1,17 @@
package cloudmigrationimpl
import (
"errors"
"fmt"
"github.com/prometheus/client_golang/prometheus"
)
// type Metrics struct {
const (
namespace = "grafana"
subsystem = "cloudmigrations"
)
var PromMetrics = []prometheus.Collector{
prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "datasources_migrated",
Help: "Total amount of data sources migrated",
}, []string{"pdc_converted"}),
}
type Metrics struct {
accessTokenCreated *prometheus.CounterVec
accessTokenDeleted *prometheus.CounterVec
}
func newMetrics() *Metrics {
@@ -34,18 +22,21 @@ func newMetrics() *Metrics {
Name: "access_token_created",
Help: "Total of access tokens created",
}, []string{"slug"}),
accessTokenDeleted: prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "access_token_deleted",
Help: "Total of access tokens deleted",
}, []string{"slug"}),
}
}
func (s *Service) registerMetrics(prom prometheus.Registerer, metrics *Metrics) error {
if err := prom.Register(metrics.accessTokenCreated); err != nil {
var alreadyRegisterErr prometheus.AlreadyRegisteredError
if errors.As(err, &alreadyRegisterErr) {
s.log.Warn("metric already registered", "metric", metrics.accessTokenCreated)
} else {
return fmt.Errorf("registering access token created metric: %w", err)
}
}
func (metrics *Metrics) Collect(ch chan<- prometheus.Metric) {
metrics.accessTokenCreated.Collect(ch)
metrics.accessTokenDeleted.Collect(ch)
}
return nil
func (metrics *Metrics) Describe(ch chan<- *prometheus.Desc) {
metrics.accessTokenCreated.Describe(ch)
metrics.accessTokenDeleted.Describe(ch)
}