CloudMigrations: Implement migrations API (#85348)
* Implement run migration endpoint * Refactor RunMigration method into separate methods * Save migration runs fix lint * Minor changes * Refactor how to use cms endpoint * fix interface * complete merge * add individual items * adds tracing to getMigration * linter * updated swagger definition with the latest changes * CloudMigrations: Implement core API handlers for cloud migrations and migration runs (#85407) * implement delete * add auth token encryption * implement token validation * call token validation during migration creation * implement get migration status * implement list migration runs * fix bug * finish parse domain func * fix urls * fix typo * fix encoding and decoding * remove double decryption * add missing slash * fix id returned by create function * inject missing services * finish implementing (as far as I can tell right now) data migration and response handling * comment out broken test, needs a rewrite * add a few final touches * get dashboard migration to work properly * changed runMigration to a POST * swagger * swagger * swagger --------- Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com> Co-authored-by: Leonard Gram <leo@xlson.com> Co-authored-by: Michael Mandrus <41969079+mmandrus@users.noreply.github.com>
This commit is contained in:
co-authored by
Michael Mandrus
Leonard Gram
Michael Mandrus
parent
89638238e5
commit
b885da09da
@@ -2,8 +2,12 @@ package cloudmigrationimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
@@ -11,27 +15,55 @@ import (
|
||||
)
|
||||
|
||||
type sqlStore struct {
|
||||
db db.DB
|
||||
db db.DB
|
||||
secretsService secrets.Service
|
||||
}
|
||||
|
||||
func (ss *sqlStore) MigrateDatasources(ctx context.Context, request *cloudmigration.MigrateDatasourcesRequest) (*cloudmigration.MigrateDatasourcesResponse, error) {
|
||||
return nil, cloudmigration.ErrInternalNotImplementedError
|
||||
func (ss *sqlStore) GetMigration(ctx context.Context, id int64) (*cloudmigration.CloudMigration, error) {
|
||||
var cm cloudmigration.CloudMigration
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
exist, err := sess.ID(id).Get(&cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return cloudmigration.ErrMigrationNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := ss.decryptToken(ctx, &cm); err != nil {
|
||||
return &cm, err
|
||||
}
|
||||
|
||||
return &cm, err
|
||||
}
|
||||
|
||||
func (ss *sqlStore) CreateMigration(ctx context.Context, migration cloudmigration.CloudMigration) error {
|
||||
func (ss *sqlStore) SaveMigrationRun(ctx context.Context, cmr *cloudmigration.CloudMigrationRun) error {
|
||||
return ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
_, err := sess.Insert(cmr)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *sqlStore) CreateMigration(ctx context.Context, migration cloudmigration.CloudMigration) (*cloudmigration.CloudMigration, error) {
|
||||
if err := ss.encryptToken(ctx, &migration); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
migration.Created = time.Now()
|
||||
migration.Updated = time.Now()
|
||||
_, err := sess.Insert(migration)
|
||||
_, err := sess.Insert(&migration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return &migration, nil
|
||||
}
|
||||
|
||||
func (ss *sqlStore) GetAllCloudMigrations(ctx context.Context) ([]*cloudmigration.CloudMigration, error) {
|
||||
@@ -40,5 +72,98 @@ func (ss *sqlStore) GetAllCloudMigrations(ctx context.Context) ([]*cloudmigratio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(migrations); i++ {
|
||||
m := migrations[i]
|
||||
if err := ss.decryptToken(ctx, m); err != nil {
|
||||
return migrations, err
|
||||
}
|
||||
}
|
||||
|
||||
return migrations, nil
|
||||
}
|
||||
|
||||
func (ss *sqlStore) DeleteMigration(ctx context.Context, id int64) (*cloudmigration.CloudMigration, error) {
|
||||
var c cloudmigration.CloudMigration
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
exist, err := sess.ID(id).Get(&c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return cloudmigration.ErrMigrationNotFound
|
||||
}
|
||||
affected, err := sess.Delete(&cloudmigration.CloudMigration{
|
||||
ID: id,
|
||||
})
|
||||
if affected == 0 {
|
||||
return cloudmigration.ErrMigrationNotDeleted.Errorf("0 affected rows for id %d", id)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return &c, err
|
||||
}
|
||||
|
||||
func (ss *sqlStore) GetMigrationStatus(ctx context.Context, migrationID string, runID string) (*cloudmigration.CloudMigrationRun, error) {
|
||||
id, err := strconv.ParseInt(runID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid runID: %s", runID)
|
||||
}
|
||||
cm := cloudmigration.CloudMigrationRun{
|
||||
ID: id,
|
||||
CloudMigrationUID: migrationID,
|
||||
}
|
||||
err = ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
exist, err := sess.Get(&cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return cloudmigration.ErrMigrationRunNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return &cm, err
|
||||
}
|
||||
|
||||
func (ss *sqlStore) GetMigrationStatusList(ctx context.Context, migrationID string) ([]*cloudmigration.CloudMigrationRun, error) {
|
||||
var runs = make([]*cloudmigration.CloudMigrationRun, 0)
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
return sess.Find(&runs, &cloudmigration.CloudMigrationRun{
|
||||
CloudMigrationUID: migrationID,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return runs, nil
|
||||
}
|
||||
|
||||
func (ss *sqlStore) encryptToken(ctx context.Context, cm *cloudmigration.CloudMigration) error {
|
||||
s, err := ss.secretsService.Encrypt(ctx, []byte(cm.AuthToken), secrets.WithoutScope())
|
||||
if err != nil {
|
||||
return fmt.Errorf("encrypting auth token: %w", err)
|
||||
}
|
||||
|
||||
cm.AuthToken = base64.StdEncoding.EncodeToString(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *sqlStore) decryptToken(ctx context.Context, cm *cloudmigration.CloudMigration) error {
|
||||
decoded, err := base64.StdEncoding.DecodeString(cm.AuthToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("token could not be decoded")
|
||||
}
|
||||
|
||||
t, err := ss.secretsService.Decrypt(ctx, decoded)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypting auth token: %w", err)
|
||||
}
|
||||
cm.AuthToken = string(t)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user