mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CMS: Create local implementation of cloud migration for dev use (#86637)
* add developer mode property to config * create cms stub * cleanup * implement and wire up gcom stub * fix errors * don't document the flag
This commit is contained in:
parent
ae84d16a6f
commit
45a7f649fe
@ -1839,4 +1839,4 @@ fetch_access_policy_timeout = 5s
|
|||||||
# How long to wait for a request to create to delete an access policy to complete
|
# How long to wait for a request to create to delete an access policy to complete
|
||||||
delete_access_policy_timeout = 5s
|
delete_access_policy_timeout = 5s
|
||||||
# The domain name used to access cms
|
# The domain name used to access cms
|
||||||
domain = grafana-dev.net
|
domain = grafana-dev.net
|
@ -84,7 +84,6 @@ func ProvideService(
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
features: features,
|
features: features,
|
||||||
dsService: dsService,
|
dsService: dsService,
|
||||||
gcomService: gcom.New(gcom.Config{ApiURL: cfg.GrafanaComAPIURL, Token: cfg.CloudMigration.GcomAPIToken}),
|
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
metrics: newMetrics(),
|
metrics: newMetrics(),
|
||||||
secretsService: secretsService,
|
secretsService: secretsService,
|
||||||
@ -93,12 +92,20 @@ func ProvideService(
|
|||||||
}
|
}
|
||||||
s.api = api.RegisterApi(routeRegister, s, tracer)
|
s.api = api.RegisterApi(routeRegister, s, tracer)
|
||||||
|
|
||||||
// get CMS path from the config
|
if !cfg.CloudMigration.IsDeveloperMode {
|
||||||
domain, err := s.parseCloudMigrationConfig()
|
// get CMS path from the config
|
||||||
if err != nil {
|
domain, err := s.parseCloudMigrationConfig()
|
||||||
return nil, fmt.Errorf("config parse error: %w", err)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("config parse error: %w", err)
|
||||||
|
}
|
||||||
|
s.cmsClient = cmsclient.NewCMSClient(domain)
|
||||||
|
|
||||||
|
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.cfg.StackID = "12345"
|
||||||
}
|
}
|
||||||
s.cmsClient = cmsclient.NewCMSClient(domain)
|
|
||||||
|
|
||||||
if err := s.registerMetrics(prom, s.metrics); err != nil {
|
if err := s.registerMetrics(prom, s.metrics); err != nil {
|
||||||
s.log.Warn("error registering prom metrics", "error", err.Error())
|
s.log.Warn("error registering prom metrics", "error", err.Error())
|
||||||
|
60
pkg/services/cloudmigration/cloudmigrationimpl/gcomstub.go
Normal file
60
pkg/services/cloudmigration/cloudmigrationimpl/gcomstub.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package cloudmigrationimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/gcom"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gcomStub struct {
|
||||||
|
policies map[string]gcom.AccessPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *gcomStub) GetInstanceByID(ctx context.Context, requestID string, instanceID string) (gcom.Instance, error) {
|
||||||
|
id, err := strconv.Atoi(instanceID)
|
||||||
|
if err != nil {
|
||||||
|
return gcom.Instance{}, fmt.Errorf("parsing instanceID: %w", err)
|
||||||
|
}
|
||||||
|
return gcom.Instance{
|
||||||
|
ID: id,
|
||||||
|
Slug: "stubinstance",
|
||||||
|
RegionSlug: "fake-region",
|
||||||
|
ClusterSlug: "fake-cluser",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *gcomStub) CreateAccessPolicy(ctx context.Context, params gcom.CreateAccessPolicyParams, payload gcom.CreateAccessPolicyPayload) (gcom.AccessPolicy, error) {
|
||||||
|
randStr := fmt.Sprintf("random-policy-%s", util.GenerateShortUID())
|
||||||
|
policy := gcom.AccessPolicy{
|
||||||
|
ID: randStr,
|
||||||
|
Name: randStr,
|
||||||
|
}
|
||||||
|
client.policies[policy.ID] = policy
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *gcomStub) DeleteAccessPolicy(ctx context.Context, params gcom.DeleteAccessPolicyParams) (bool, error) {
|
||||||
|
delete(client.policies, params.AccessPolicyID)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *gcomStub) ListAccessPolicies(ctx context.Context, params gcom.ListAccessPoliciesParams) ([]gcom.AccessPolicy, error) {
|
||||||
|
items := make([]gcom.AccessPolicy, 0)
|
||||||
|
for _, v := range client.policies {
|
||||||
|
items = append(items, v)
|
||||||
|
}
|
||||||
|
return items, 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()),
|
||||||
|
Name: payload.Name,
|
||||||
|
AccessPolicyID: payload.AccessPolicyID,
|
||||||
|
Token: fmt.Sprintf("completely_fake_token_%s", util.GenerateShortUID()),
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
package cloudmigrationtest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
ExpectedError error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) MigrateDatasources(ctx context.Context, request *cloudmigration.MigrateDatasourcesRequest) (*cloudmigration.MigrateDatasourcesResponse, error) {
|
|
||||||
return nil, cloudmigration.ErrInternalNotImplementedError
|
|
||||||
}
|
|
17
pkg/services/cloudmigration/cmsclient/client.go
Normal file
17
pkg/services/cloudmigration/cmsclient/client.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package cmsclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||||
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
ValidateKey(context.Context, cloudmigration.CloudMigration) error
|
||||||
|
MigrateData(context.Context, cloudmigration.CloudMigration, cloudmigration.MigrateDataRequestDTO) (*cloudmigration.MigrateDataResponseDTO, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const logPrefix = "cloudmigration.cmsclient"
|
||||||
|
|
||||||
|
var ErrMigrationNotDeleted = errutil.Internal("cloudmigrations.developerModeEnabled", errutil.WithPublicMessage("Developer mode enabled"))
|
@ -11,26 +11,20 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client interface {
|
// NewCMSClient returns an implementation of Client that queries CloudMigrationService
|
||||||
ValidateKey(context.Context, cloudmigration.CloudMigration) error
|
|
||||||
MigrateData(context.Context, cloudmigration.CloudMigration, cloudmigration.MigrateDataRequestDTO) (*cloudmigration.MigrateDataResponseDTO, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const logPrefix = "cloudmigration.cmsclient"
|
|
||||||
|
|
||||||
func NewCMSClient(domain string) Client {
|
func NewCMSClient(domain string) Client {
|
||||||
return &clientImpl{
|
return &cmsClientImpl{
|
||||||
domain: domain,
|
domain: domain,
|
||||||
log: log.New(logPrefix),
|
log: log.New(logPrefix),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientImpl struct {
|
type cmsClientImpl struct {
|
||||||
domain string
|
domain string
|
||||||
log *log.ConcreteLogger
|
log *log.ConcreteLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientImpl) ValidateKey(ctx context.Context, cm cloudmigration.CloudMigration) error {
|
func (c *cmsClientImpl) ValidateKey(ctx context.Context, cm cloudmigration.CloudMigration) error {
|
||||||
logger := c.log.FromContext(ctx)
|
logger := c.log.FromContext(ctx)
|
||||||
|
|
||||||
path := fmt.Sprintf("https://cms-%s.%s/cloud-migrations/api/v1/validate-key", cm.ClusterSlug, c.domain)
|
path := fmt.Sprintf("https://cms-%s.%s/cloud-migrations/api/v1/validate-key", cm.ClusterSlug, c.domain)
|
||||||
@ -69,7 +63,7 @@ func (c *clientImpl) ValidateKey(ctx context.Context, cm cloudmigration.CloudMig
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientImpl) MigrateData(ctx context.Context, cm cloudmigration.CloudMigration, request cloudmigration.MigrateDataRequestDTO) (*cloudmigration.MigrateDataResponseDTO, error) {
|
func (c *cmsClientImpl) MigrateData(ctx context.Context, cm cloudmigration.CloudMigration, request cloudmigration.MigrateDataRequestDTO) (*cloudmigration.MigrateDataResponseDTO, error) {
|
||||||
logger := c.log.FromContext(ctx)
|
logger := c.log.FromContext(ctx)
|
||||||
|
|
||||||
path := fmt.Sprintf("https://cms-%s.%s/cloud-migrations/api/v1/migrate-data", cm.ClusterSlug, c.domain)
|
path := fmt.Sprintf("https://cms-%s.%s/cloud-migrations/api/v1/migrate-data", cm.ClusterSlug, c.domain)
|
48
pkg/services/cloudmigration/cmsclient/inmemory_client.go
Normal file
48
pkg/services/cloudmigration/cmsclient/inmemory_client.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package cmsclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewInMemoryClient returns an implementation of Client that returns canned responses
|
||||||
|
func NewInMemoryClient() Client {
|
||||||
|
return &memoryClientImpl{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryClientImpl struct{}
|
||||||
|
|
||||||
|
func (c *memoryClientImpl) ValidateKey(ctx context.Context, cm cloudmigration.CloudMigration) error {
|
||||||
|
// return ErrMigrationNotDeleted
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *memoryClientImpl) MigrateData(
|
||||||
|
ctx context.Context,
|
||||||
|
cm cloudmigration.CloudMigration,
|
||||||
|
request cloudmigration.MigrateDataRequestDTO,
|
||||||
|
) (*cloudmigration.MigrateDataResponseDTO, error) {
|
||||||
|
//return nil, ErrMigrationNotDeleted
|
||||||
|
|
||||||
|
result := cloudmigration.MigrateDataResponseDTO{
|
||||||
|
Items: make([]cloudmigration.MigrateDataResponseItemDTO, len(request.Items)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range request.Items {
|
||||||
|
result.Items[i] = cloudmigration.MigrateDataResponseItemDTO{
|
||||||
|
Type: v.Type,
|
||||||
|
RefID: v.RefID,
|
||||||
|
Status: cloudmigration.ItemStatusOK,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// simulate flakiness on one random item
|
||||||
|
i := rand.Intn(len(result.Items))
|
||||||
|
failedItem := result.Items[i]
|
||||||
|
failedItem.Status, failedItem.Error = cloudmigration.ItemStatusError, "simulated random error"
|
||||||
|
result.Items[i] = failedItem
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
@ -63,7 +63,7 @@ type ListAccessPoliciesParams struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type listAccessPoliciesResponse struct {
|
type ListAccessPoliciesResponse struct {
|
||||||
Items []AccessPolicy `json:"items"`
|
Items []AccessPolicy `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ func (client *GcomClient) ListAccessPolicies(ctx context.Context, params ListAcc
|
|||||||
return nil, fmt.Errorf("unexpected response when listing access policies: code=%d body=%s", response.StatusCode, body)
|
return nil, fmt.Errorf("unexpected response when listing access policies: code=%d body=%s", response.StatusCode, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseBody listAccessPoliciesResponse
|
var responseBody ListAccessPoliciesResponse
|
||||||
if err := json.NewDecoder(response.Body).Decode(&responseBody); err != nil {
|
if err := json.NewDecoder(response.Body).Decode(&responseBody); err != nil {
|
||||||
return responseBody.Items, fmt.Errorf("unmarshaling response body: %w", err)
|
return responseBody.Items, fmt.Errorf("unmarshaling response body: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ type CloudMigrationSettings struct {
|
|||||||
DeleteAccessPolicyTimeout time.Duration
|
DeleteAccessPolicyTimeout time.Duration
|
||||||
CreateTokenTimeout time.Duration
|
CreateTokenTimeout time.Duration
|
||||||
TokenExpiresAfter time.Duration
|
TokenExpiresAfter time.Duration
|
||||||
|
|
||||||
|
IsDeveloperMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Cfg) readCloudMigrationSettings() {
|
func (cfg *Cfg) readCloudMigrationSettings() {
|
||||||
@ -25,4 +27,5 @@ func (cfg *Cfg) readCloudMigrationSettings() {
|
|||||||
cfg.CloudMigration.DeleteAccessPolicyTimeout = cloudMigration.Key("delete_access_policy_timeout").MustDuration(5 * time.Second)
|
cfg.CloudMigration.DeleteAccessPolicyTimeout = cloudMigration.Key("delete_access_policy_timeout").MustDuration(5 * time.Second)
|
||||||
cfg.CloudMigration.CreateTokenTimeout = cloudMigration.Key("create_token_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.TokenExpiresAfter = cloudMigration.Key("token_expires_after").MustDuration(7 * 24 * time.Hour)
|
||||||
|
cfg.CloudMigration.IsDeveloperMode = cloudMigration.Key("developer_mode").MustBool(false)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user