mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Cloud migrations: create route to fetch cloud migration token (#88176)
* Cloud migration: create route to fetch cloud migration token * implement gcomStub.ListTokens * fix swagger for POST /cloudmigration/migration * fix swagger for POST /cloudmigration/migration * fix swagger for POST /cloudmigration/migration
This commit is contained in:
parent
b640921dfd
commit
83543c6b12
@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@ -47,10 +48,46 @@ func (cma *CloudMigrationAPI) registerEndpoints() {
|
||||
cloudMigrationRoute.Post("/migration/:uid/run", routing.Wrap(cma.RunMigration))
|
||||
cloudMigrationRoute.Get("/migration/:uid/run", routing.Wrap(cma.GetMigrationRunList))
|
||||
cloudMigrationRoute.Get("/migration/run/:runUID", routing.Wrap(cma.GetMigrationRun))
|
||||
cloudMigrationRoute.Get("/token", routing.Wrap(cma.GetToken))
|
||||
cloudMigrationRoute.Post("/token", routing.Wrap(cma.CreateToken))
|
||||
}, middleware.ReqOrgAdmin)
|
||||
}
|
||||
|
||||
// swagger:route GET /cloudmigration/token migrations getCloudMigrationToken
|
||||
//
|
||||
// Fetch the cloud migration token if it exists.
|
||||
//
|
||||
// Responses:
|
||||
// 200: cloudMigrationGetTokenResponse
|
||||
// 401: unauthorisedError
|
||||
// 404: notFoundError
|
||||
// 403: forbiddenError
|
||||
// 500: internalServerError
|
||||
func (cma *CloudMigrationAPI) GetToken(c *contextmodel.ReqContext) response.Response {
|
||||
ctx, span := cma.tracer.Start(c.Req.Context(), "MigrationAPI.GetToken")
|
||||
defer span.End()
|
||||
|
||||
logger := cma.log.FromContext(ctx)
|
||||
|
||||
token, err := cma.cloudMigrationService.GetToken(ctx)
|
||||
if err != nil {
|
||||
if !errors.Is(err, cloudmigration.ErrTokenNotFound) {
|
||||
logger.Error("fetching cloud migration access token", "err", err.Error())
|
||||
}
|
||||
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "fetching cloud migration access token", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, GetAccessTokenResponseDTO{
|
||||
ID: token.ID,
|
||||
DisplayName: token.DisplayName,
|
||||
ExpiresAt: token.ExpiresAt,
|
||||
FirstUsedAt: token.FirstUsedAt,
|
||||
LastUsedAt: token.LastUsedAt,
|
||||
CreatedAt: token.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:route POST /cloudmigration/token migrations createCloudMigrationToken
|
||||
//
|
||||
// Create gcom access token.
|
||||
@ -310,6 +347,13 @@ type CloudMigrationListResponse struct {
|
||||
Body cloudmigration.CloudMigrationListResponse
|
||||
}
|
||||
|
||||
// swagger:parameters createMigration
|
||||
type CreateMigration struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body cloudmigration.CloudMigrationRequest
|
||||
}
|
||||
|
||||
// swagger:response cloudMigrationResponse
|
||||
type CloudMigrationResponse struct {
|
||||
// in: body
|
||||
@ -322,6 +366,25 @@ type CloudMigrationRunListResponse struct {
|
||||
Body cloudmigration.CloudMigrationRunList
|
||||
}
|
||||
|
||||
// swagger:parameters getCloudMigrationToken
|
||||
type GetCloudMigrationToken struct {
|
||||
}
|
||||
|
||||
// swagger:response cloudMigrationGetTokenResponse
|
||||
type CloudMigrationGetTokenResponse struct {
|
||||
// in: body
|
||||
Body GetAccessTokenResponseDTO
|
||||
}
|
||||
|
||||
type GetAccessTokenResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"displayName"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
FirstUsedAt string `json:"firstUsedAt"`
|
||||
LastUsedAt string `json:"lastUsedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
// swagger:response cloudMigrationCreateTokenResponse
|
||||
type CloudMigrationCreateTokenResponse struct {
|
||||
// in: body
|
||||
|
@ -2,10 +2,16 @@ package cloudmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/gcom"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
// Returns the cloud migration token if it exists.
|
||||
GetToken(context.Context) (gcom.TokenView, error)
|
||||
// Creates a cloud migration token.
|
||||
CreateToken(context.Context) (CreateAccessTokenResponse, error)
|
||||
// Sends a request to CMS to test the token.
|
||||
ValidateToken(context.Context, CloudMigration) error
|
||||
|
||||
CreateMigration(context.Context, CloudMigrationRequest) (*CloudMigrationResponse, error)
|
||||
|
@ -102,7 +102,7 @@ func ProvideService(
|
||||
s.gcomService = gcom.New(gcom.Config{ApiURL: cfg.GrafanaComAPIURL, Token: cfg.CloudMigration.GcomAPIToken})
|
||||
} else {
|
||||
s.cmsClient = cmsclient.NewInMemoryClient()
|
||||
s.gcomService = &gcomStub{map[string]gcom.AccessPolicy{}}
|
||||
s.gcomService = &gcomStub{policies: map[string]gcom.AccessPolicy{}, token: nil}
|
||||
s.cfg.StackID = "12345"
|
||||
}
|
||||
|
||||
@ -113,6 +113,47 @@ func ProvideService(
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "CloudMigrationService.GetToken")
|
||||
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 gcom.TokenView{}, fmt.Errorf("fetching instance by id: id=%s %w", s.cfg.StackID, err)
|
||||
}
|
||||
|
||||
logger.Info("instance found", "slug", instance.Slug)
|
||||
|
||||
accessPolicyName := fmt.Sprintf("%s-%s", cloudMigrationAccessPolicyNamePrefix, s.cfg.StackID)
|
||||
accessTokenName := fmt.Sprintf("%s-%s", cloudMigrationTokenNamePrefix, s.cfg.StackID)
|
||||
|
||||
timeoutCtx, cancel = context.WithTimeout(ctx, s.cfg.CloudMigration.ListTokensTimeout)
|
||||
defer cancel()
|
||||
tokens, err := s.gcomService.ListTokens(timeoutCtx, gcom.ListTokenParams{
|
||||
RequestID: requestID,
|
||||
Region: instance.RegionSlug,
|
||||
AccessPolicyName: accessPolicyName,
|
||||
TokenName: accessTokenName})
|
||||
if err != nil {
|
||||
return gcom.TokenView{}, fmt.Errorf("listing tokens: %w", err)
|
||||
}
|
||||
logger.Info("found access tokens", "num_tokens", len(tokens))
|
||||
|
||||
for _, token := range tokens {
|
||||
if token.Name == accessTokenName {
|
||||
logger.Info("found existing cloud migration token", "tokenID", token.ID, "accessPolicyID", token.AccessPolicyID)
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("cloud migration token not found")
|
||||
return gcom.TokenView{}, cloudmigration.ErrTokenNotFound
|
||||
}
|
||||
|
||||
func (s *Service) CreateToken(ctx context.Context) (cloudmigration.CreateAccessTokenResponse, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "CloudMigrationService.CreateToken")
|
||||
defer span.End()
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
"github.com/grafana/grafana/pkg/services/gcom"
|
||||
)
|
||||
|
||||
// NoopServiceImpl Define the Service Implementation.
|
||||
@ -11,6 +12,10 @@ type NoopServiceImpl struct{}
|
||||
|
||||
var _ cloudmigration.Service = (*NoopServiceImpl)(nil)
|
||||
|
||||
func (s *NoopServiceImpl) GetToken(ctx context.Context) (gcom.TokenView, error) {
|
||||
return gcom.TokenView{}, cloudmigration.ErrFeatureDisabledError
|
||||
}
|
||||
|
||||
func (s *NoopServiceImpl) CreateToken(ctx context.Context) (cloudmigration.CreateAccessTokenResponse, error) {
|
||||
return cloudmigration.CreateAccessTokenResponse{}, cloudmigration.ErrFeatureDisabledError
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
)
|
||||
|
||||
type gcomStub struct {
|
||||
// The cloud migration token created by this stub.
|
||||
token *gcom.TokenView
|
||||
policies map[string]gcom.AccessPolicy
|
||||
}
|
||||
|
||||
@ -49,6 +51,14 @@ func (client *gcomStub) ListAccessPolicies(ctx context.Context, params gcom.List
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) ListTokens(ctx context.Context, params gcom.ListTokenParams) ([]gcom.TokenView, error) {
|
||||
if client.token == nil {
|
||||
return []gcom.TokenView{}, nil
|
||||
}
|
||||
|
||||
return []gcom.TokenView{*client.token}, nil
|
||||
}
|
||||
|
||||
func (client *gcomStub) CreateToken(ctx context.Context, params gcom.CreateTokenParams, payload gcom.CreateTokenPayload) (gcom.Token, error) {
|
||||
token := gcom.Token{
|
||||
ID: fmt.Sprintf("random-token-%s", util.GenerateShortUID()),
|
||||
@ -56,5 +66,11 @@ func (client *gcomStub) CreateToken(ctx context.Context, params gcom.CreateToken
|
||||
AccessPolicyID: payload.AccessPolicyID,
|
||||
Token: fmt.Sprintf("completely_fake_token_%s", util.GenerateShortUID()),
|
||||
}
|
||||
client.token = &gcom.TokenView{
|
||||
ID: token.ID,
|
||||
Name: token.Name,
|
||||
AccessPolicyID: token.AccessPolicyID,
|
||||
DisplayName: token.Name,
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ var (
|
||||
ErrMigrationNotFound = errutil.NotFound("cloudmigrations.migrationNotFound").Errorf("Migration not found")
|
||||
ErrMigrationRunNotFound = errutil.NotFound("cloudmigrations.migrationRunNotFound").Errorf("Migration run not found")
|
||||
ErrMigrationNotDeleted = errutil.Internal("cloudmigrations.migrationNotDeleted").Errorf("Migration not deleted")
|
||||
ErrTokenNotFound = errutil.NotFound("cloudmigrations.tokenNotFound").Errorf("Token not found")
|
||||
)
|
||||
|
||||
// CloudMigration api dtos
|
||||
|
@ -20,6 +20,7 @@ type Service interface {
|
||||
CreateAccessPolicy(ctx context.Context, params CreateAccessPolicyParams, payload CreateAccessPolicyPayload) (AccessPolicy, error)
|
||||
ListAccessPolicies(ctx context.Context, params ListAccessPoliciesParams) ([]AccessPolicy, error)
|
||||
DeleteAccessPolicy(ctx context.Context, params DeleteAccessPolicyParams) (bool, error)
|
||||
ListTokens(ctx context.Context, params ListTokenParams) ([]TokenView, error)
|
||||
CreateToken(ctx context.Context, params CreateTokenParams, payload CreateTokenPayload) (Token, error)
|
||||
}
|
||||
|
||||
@ -73,6 +74,13 @@ type DeleteAccessPolicyParams struct {
|
||||
Region string
|
||||
}
|
||||
|
||||
type ListTokenParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
AccessPolicyName string
|
||||
TokenName string
|
||||
}
|
||||
|
||||
type CreateTokenParams struct {
|
||||
RequestID string
|
||||
Region string
|
||||
@ -85,6 +93,7 @@ type CreateTokenPayload struct {
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
// The token returned by gcom api when a token gets created.
|
||||
type Token struct {
|
||||
ID string `json:"id"`
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
@ -92,6 +101,22 @@ type Token struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// The token returned by gcom api for a GET token request.
|
||||
type TokenView struct {
|
||||
ID string `json:"id"`
|
||||
AccessPolicyID string `json:"accessPolicyId"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
FirstUsedAt string `json:"firstUsedAt"`
|
||||
LastUsedAt string `json:"lastUsedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
type listTokensResponse struct {
|
||||
Items []TokenView `json:"items"`
|
||||
}
|
||||
|
||||
type GcomClient struct {
|
||||
log log.Logger
|
||||
cfg Config
|
||||
@ -281,6 +306,50 @@ func (client *GcomClient) ListAccessPolicies(ctx context.Context, params ListAcc
|
||||
return responseBody.Items, nil
|
||||
}
|
||||
|
||||
func (client *GcomClient) ListTokens(ctx context.Context, params ListTokenParams) ([]TokenView, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building gcom tokens url: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("region", params.Region)
|
||||
query.Set("accessPolicyName", params.AccessPolicyName)
|
||||
query.Set("name", params.TokenName)
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
request.Header.Set("x-request-id", params.RequestID)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.cfg.Token))
|
||||
|
||||
response, err := client.httpClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sending http request to list access tokens: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := response.Body.Close(); err != nil {
|
||||
client.log.Error("closing http response body", "err", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return nil, fmt.Errorf("unexpected response when fetching access tokens: code=%d body=%s", response.StatusCode, body)
|
||||
}
|
||||
|
||||
var body listTokensResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&body); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
return body.Items, nil
|
||||
}
|
||||
func (client *GcomClient) CreateToken(ctx context.Context, params CreateTokenParams, payload CreateTokenPayload) (Token, error) {
|
||||
endpoint, err := url.JoinPath(client.cfg.ApiURL, "/v1/tokens")
|
||||
if err != nil {
|
||||
|
@ -11,6 +11,7 @@ type CloudMigrationSettings struct {
|
||||
CreateAccessPolicyTimeout time.Duration
|
||||
FetchAccessPolicyTimeout time.Duration
|
||||
DeleteAccessPolicyTimeout time.Duration
|
||||
ListTokensTimeout time.Duration
|
||||
CreateTokenTimeout time.Duration
|
||||
TokenExpiresAfter time.Duration
|
||||
|
||||
@ -25,6 +26,7 @@ func (cfg *Cfg) readCloudMigrationSettings() {
|
||||
cfg.CloudMigration.CreateAccessPolicyTimeout = cloudMigration.Key("create_access_policy_timeout").MustDuration(5 * time.Second)
|
||||
cfg.CloudMigration.FetchAccessPolicyTimeout = cloudMigration.Key("fetch_access_policy_timeout").MustDuration(5 * time.Second)
|
||||
cfg.CloudMigration.DeleteAccessPolicyTimeout = cloudMigration.Key("delete_access_policy_timeout").MustDuration(5 * time.Second)
|
||||
cfg.CloudMigration.ListTokensTimeout = cloudMigration.Key("list_tokens_timeout").MustDuration(5 * time.Second)
|
||||
cfg.CloudMigration.CreateTokenTimeout = cloudMigration.Key("create_token_timeout").MustDuration(5 * time.Second)
|
||||
cfg.CloudMigration.TokenExpiresAfter = cloudMigration.Key("token_expires_after").MustDuration(7 * 24 * time.Hour)
|
||||
cfg.CloudMigration.IsDeveloperMode = cloudMigration.Key("developer_mode").MustBool(false)
|
||||
|
@ -2322,6 +2322,16 @@
|
||||
],
|
||||
"summary": "Create a migration.",
|
||||
"operationId": "createMigration",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CloudMigrationRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/cloudMigrationResponse"
|
||||
@ -2494,6 +2504,30 @@
|
||||
}
|
||||
},
|
||||
"/cloudmigration/token": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"migrations"
|
||||
],
|
||||
"summary": "Fetch the cloud migration token if it exists.",
|
||||
"operationId": "getCloudMigrationToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/cloudMigrationGetTokenResponse"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/responses/unauthorisedError"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbiddenError"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFoundError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/responses/internalServerError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"migrations"
|
||||
@ -13207,6 +13241,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CloudMigrationRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authToken": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CloudMigrationResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -15075,6 +15117,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"GetAccessTokenResponseDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"expiresAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"firstUsedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastUsedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GetAnnotationTagsResponse": {
|
||||
"type": "object",
|
||||
"title": "GetAnnotationTagsResponse is a response struct for FindTagsResult.",
|
||||
@ -22162,6 +22227,12 @@
|
||||
"$ref": "#/definitions/CreateAccessTokenResponseDTO"
|
||||
}
|
||||
},
|
||||
"cloudMigrationGetTokenResponse": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/GetAccessTokenResponseDTO"
|
||||
}
|
||||
},
|
||||
"cloudMigrationListResponse": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
|
@ -183,6 +183,16 @@
|
||||
},
|
||||
"description": "(empty)"
|
||||
},
|
||||
"cloudMigrationGetTokenResponse": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GetAccessTokenResponseDTO"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "(empty)"
|
||||
},
|
||||
"cloudMigrationListResponse": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
@ -3656,6 +3666,14 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"CloudMigrationRequest": {
|
||||
"properties": {
|
||||
"authToken": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"CloudMigrationResponse": {
|
||||
"properties": {
|
||||
"created": {
|
||||
@ -5524,6 +5542,29 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"GetAccessTokenResponseDTO": {
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"expiresAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"firstUsedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastUsedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"GetAnnotationTagsResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
@ -14995,6 +15036,17 @@
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createMigration",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CloudMigrationRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-originalParamName": "Body"
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/cloudMigrationResponse"
|
||||
@ -15181,6 +15233,30 @@
|
||||
}
|
||||
},
|
||||
"/cloudmigration/token": {
|
||||
"get": {
|
||||
"operationId": "getCloudMigrationToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/cloudMigrationGetTokenResponse"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/unauthorisedError"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/components/responses/forbiddenError"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/components/responses/notFoundError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/internalServerError"
|
||||
}
|
||||
},
|
||||
"summary": "Fetch the cloud migration token if it exists.",
|
||||
"tags": [
|
||||
"migrations"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createCloudMigrationToken",
|
||||
"responses": {
|
||||
|
Loading…
Reference in New Issue
Block a user