From badccf453367317e44fbd59ae17f80787e765d61 Mon Sep 17 00:00:00 2001 From: whywaita Date: Wed, 26 May 2021 17:49:56 +0900 Subject: [PATCH] devide runner tables --- pkg/datastore/interface.go | 2 +- pkg/datastore/mysql/runner.go | 50 +++++++++++++--- pkg/datastore/mysql/runner_test.go | 95 ++++++++++++++++++++++++++++-- pkg/datastore/mysql/schema.sql | 27 +++++++-- 4 files changed, 156 insertions(+), 18 deletions(-) diff --git a/pkg/datastore/interface.go b/pkg/datastore/interface.go index d4246ad..1f78478 100644 --- a/pkg/datastore/interface.go +++ b/pkg/datastore/interface.go @@ -140,7 +140,7 @@ type Job struct { // Runner is a runner type Runner struct { - UUID uuid.UUID `db:"uuid"` + UUID uuid.UUID `db:"runner_id"` ShoesType string `db:"shoes_type"` IPAddress string `db:"ip_address"` TargetID uuid.UUID `db:"target_id"` diff --git a/pkg/datastore/mysql/runner.go b/pkg/datastore/mysql/runner.go index 60894b8..d3790b3 100644 --- a/pkg/datastore/mysql/runner.go +++ b/pkg/datastore/mysql/runner.go @@ -13,18 +13,38 @@ import ( // CreateRunner add a runner func (m *MySQL) CreateRunner(ctx context.Context, runner datastore.Runner) error { - query := `INSERT INTO runners(uuid, shoes_type, ip_address, target_id, cloud_id, deleted) VALUES (?, ?, ?, ?, ?, ?)` - if _, err := m.Conn.ExecContext(ctx, query, runner.UUID.String(), runner.ShoesType, runner.IPAddress, runner.TargetID.String(), runner.CloudID, false); err != nil { - return fmt.Errorf("failed to execute INSERT query: %w", err) + tx := m.Conn.MustBegin() + + queryRunner := `INSERT INTO runners(uuid) VALUES (?)` + if _, err := tx.ExecContext(ctx, queryRunner, runner.UUID.String()); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute INSERT query runners: %w", err) + } + + queryDetail := `INSERT INTO runner_detail(runner_id, shoes_type, ip_address, target_id, cloud_id) VALUES (?, ?, ?, ?, ?)` + if _, err := tx.ExecContext(ctx, queryDetail, runner.UUID.String(), runner.ShoesType, runner.IPAddress, runner.TargetID.String(), runner.CloudID); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute INSERT query runner_detail: %w", err) } + queryRunning := `INSERT INTO runners_running(runner_id) VALUES (?)` + if _, err := tx.ExecContext(ctx, queryRunning, runner.UUID.String()); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute INSERT query runners_running: %w", err) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute COMMIT: %w", err) + } return nil } // ListRunners get a not deleted runners func (m *MySQL) ListRunners(ctx context.Context) ([]datastore.Runner, error) { var runners []datastore.Runner - query := `SELECT uuid, shoes_type, ip_address, target_id, cloud_id, deleted, status, created_at, updated_at, deleted_at FROM runners WHERE deleted = false` + query := `SELECT runner.runner_id, detail.shoes_type, detail.ip_address, detail.target_id, detail.cloud_id, detail.created_at, detail.updated_at + FROM runners_running AS runner JOIN runner_detail AS detail ON runner.runner_id = detail.runner_id` err := m.Conn.SelectContext(ctx, &runners, query) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -41,7 +61,7 @@ func (m *MySQL) ListRunners(ctx context.Context) ([]datastore.Runner, error) { func (m *MySQL) GetRunner(ctx context.Context, id uuid.UUID) (*datastore.Runner, error) { var r datastore.Runner - query := `SELECT uuid, shoes_type, ip_address, target_id, cloud_id, deleted, status, created_at, updated_at, deleted_at FROM runners WHERE uuid = ? AND deleted = false` + query := `SELECT runner_id, shoes_type, ip_address, target_id, cloud_id, created_at, updated_at FROM runner_detail WHERE runner_id = ?` if err := m.Conn.GetContext(ctx, &r, query, id.String()); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, datastore.ErrNotFound @@ -55,9 +75,23 @@ func (m *MySQL) GetRunner(ctx context.Context, id uuid.UUID) (*datastore.Runner, // DeleteRunner delete a runner func (m *MySQL) DeleteRunner(ctx context.Context, id uuid.UUID, deletedAt time.Time, reason datastore.RunnerStatus) error { - query := `UPDATE runners SET deleted=1, deleted_at = ?, status = ? WHERE uuid = ? ` - if _, err := m.Conn.ExecContext(ctx, query, deletedAt, reason, id.String()); err != nil { - return fmt.Errorf("failed to execute UPDATE query: %w", err) + tx := m.Conn.MustBegin() + + queryDelete := `DELETE FROM runners_running WHERE runner_id = ?` + if _, err := tx.ExecContext(ctx, queryDelete, id.String()); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute DELETE query: %w", err) + } + + queryInsert := `INSERT INTO runners_deleted(runner_id, reason) VALUES (?, ?)` + if _, err := tx.ExecContext(ctx, queryInsert, id.String(), reason); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute INSERT query: %w", err) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute COMMIT: %w", err) } return nil diff --git a/pkg/datastore/mysql/runner_test.go b/pkg/datastore/mysql/runner_test.go index 36c7af3..68392db 100644 --- a/pkg/datastore/mysql/runner_test.go +++ b/pkg/datastore/mysql/runner_test.go @@ -3,6 +3,7 @@ package mysql_test import ( "context" "database/sql" + "errors" "fmt" "testing" "time" @@ -22,8 +23,6 @@ var testRunner = datastore.Runner{ IPAddress: "", TargetID: testTargetID, CloudID: "mycloud-uuid", - Deleted: false, - Status: datastore.RunnerStatusCreated, } func TestMySQL_CreateRunner(t *testing.T) { @@ -127,6 +126,57 @@ func TestMySQL_ListRunners(t *testing.T) { } } +func TestMySQL_ListRunnersNotReturnDeleted(t *testing.T) { + testDatastore, teardown := testutils.GetTestDatastore() + defer teardown() + + if err := testDatastore.CreateTarget(context.Background(), datastore.Target{ + UUID: testTargetID, + Scope: testScopeRepo, + GitHubPersonalToken: testGitHubPersonalToken, + ResourceType: datastore.ResourceTypeNano, + }); err != nil { + t.Fatalf("failed to create target: %+v", err) + } + + u := "00000000-0000-0000-0000-00000000000%d" + + for i := 0; i < 3; i++ { + input := testRunner + input.UUID = uuid.FromStringOrNil(fmt.Sprintf(u, i)) + err := testDatastore.CreateRunner(context.Background(), input) + if err != nil { + t.Fatalf("failed to create runner: %+v", err) + } + } + + err := testDatastore.DeleteRunner(context.Background(), uuid.FromStringOrNil(fmt.Sprintf(u, 0)), time.Now(), "deleted") + if err != nil { + t.Fatalf("failed to delete runner: %+v", err) + } + + got, err := testDatastore.ListRunners(context.Background()) + if err != nil { + t.Fatalf("failed to get runners: %+v", err) + } + for i := range got { + got[i].CreatedAt = time.Time{} + got[i].UpdatedAt = time.Time{} + } + + var want []datastore.Runner + for i := 1; i < 3; i++ { + r := testRunner + r.UUID = uuid.FromStringOrNil(fmt.Sprintf(u, i)) + want = append(want, r) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + +} + func TestMySQL_GetRunner(t *testing.T) { testDatastore, teardown := testutils.GetTestDatastore() defer teardown() @@ -191,8 +241,6 @@ func TestMySQL_DeleteRunner(t *testing.T) { } deleted := testRunner - deleted.Deleted = true - deleted.Status = datastore.RunnerStatusCompleted tests := []struct { input uuid.UUID @@ -224,12 +272,49 @@ func TestMySQL_DeleteRunner(t *testing.T) { if diff := cmp.Diff(test.want, got); diff != "" { t.Errorf("mismatch (-want +got):\n%s", diff) } + + if _, err := getRunningRunnerFromSQL(testDB, test.input); err == nil || errors.Is(err, sql.ErrNoRows) { + t.Errorf("%s is deleted, but exist in runner_running: %+v", test.input, err) + } + if _, err := getDeletedRunnerFromSQL(testDB, test.input); err != nil { + t.Fatalf("%s is not exist in runners_deleted: %+v", test.input, err) + } } } func getRunnerFromSQL(testDB *sqlx.DB, id uuid.UUID) (*datastore.Runner, error) { var r datastore.Runner - query := `SELECT uuid, shoes_type, ip_address, target_id, cloud_id, deleted, status, created_at, updated_at, deleted_at FROM runners WHERE uuid = ?` + query := `SELECT runner_id, shoes_type, ip_address, target_id, cloud_id, created_at, updated_at FROM runner_detail WHERE runner_id = ?` + stmt, err := testDB.Preparex(query) + if err != nil { + return nil, fmt.Errorf("failed to prepare: %w", err) + } + err = stmt.Get(&r, id) + if err != nil { + return nil, fmt.Errorf("failed to get runner: %w", err) + } + return &r, nil +} + +func getRunningRunnerFromSQL(testDB *sqlx.DB, id uuid.UUID) (*datastore.Runner, error) { + var r datastore.Runner + query := `SELECT detail.runner_id, shoes_type, ip_address, target_id, cloud_id, detail.created_at, updated_at +FROM runner_detail AS detail JOIN runnesr_running AS running ON detail.runner_id = running.runner_id WHERE detail.runner_id = ?` + stmt, err := testDB.Preparex(query) + if err != nil { + return nil, fmt.Errorf("failed to prepare: %w", err) + } + err = stmt.Get(&r, id) + if err != nil { + return nil, fmt.Errorf("failed to get runner: %w", err) + } + return &r, nil +} + +func getDeletedRunnerFromSQL(testDB *sqlx.DB, id uuid.UUID) (*datastore.Runner, error) { + var r datastore.Runner + query := `SELECT detail.runner_id, shoes_type, ip_address, target_id, cloud_id, detail.created_at, updated_at +FROM runner_detail AS detail JOIN runners_deleted AS deleted ON detail.runner_id = deleted.runner_id WHERE detail.runner_id = ?` stmt, err := testDB.Preparex(query) if err != nil { return nil, fmt.Errorf("failed to prepare: %w", err) diff --git a/pkg/datastore/mysql/schema.sql b/pkg/datastore/mysql/schema.sql index 670b33a..0595de1 100644 --- a/pkg/datastore/mysql/schema.sql +++ b/pkg/datastore/mysql/schema.sql @@ -16,17 +16,36 @@ CREATE TABLE `targets` ( CREATE TABLE `runners` ( `uuid` VARCHAR(36) NOT NULL PRIMARY KEY, + `created_at` TIMESTAMP NOT NULL DEFAULT current_timestamp +); + +CREATE TABLE `runner_detail` ( + `runner_id` VARCHAR(36) NOT NULL, `shoes_type` VARCHAR(255) NOT NULL, `ip_address` VARCHAR(255) NOT NULL, `target_id` VARCHAR(36) NOT NULL, `cloud_id` TEXT NOT NULL, - `deleted` bool DEFAULT false NOT NULL, - `status` VARCHAR(255) NOT NULL DEFAULT 'created', `created_at` TIMESTAMP NOT NULL DEFAULT current_timestamp, `updated_at` TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp, - `deleted_at` TIMESTAMP, KEY `fk_runner_target_id` (`target_id`), - CONSTRAINT `runners_ibfk_1` FOREIGN KEY fk_runner_target_id(`target_id`) REFERENCES targets(`uuid`) ON DELETE RESTRICT + CONSTRAINT `runners_ibfk_1` FOREIGN KEY fk_runner_target_id(`target_id`) REFERENCES targets(`uuid`) ON DELETE RESTRICT, + KEY `fk_runner_detail_id` (`runner_id`), + CONSTRAINT `runners_ibfk_2` FOREIGN KEY fk_runner_detail_id(`runner_id`) REFERENCES runners(`uuid`) ON DELETE RESTRICT +); + +CREATE TABLE `runners_running` ( + `runner_id` VARCHAR(36) NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT current_timestamp, + KEY `fk_runner_deleted_id` (`runner_id`), + CONSTRAINT `runners_running_ibfk_1` FOREIGN KEY fk_runner_deleted_id(`runner_id`) REFERENCES runners(`uuid`) ON DELETE CASCADE +); + +CREATE TABLE `runners_deleted` ( + `runner_id` VARCHAR(36) NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT current_timestamp, + `reason` VARCHAR(255) NOT NULL, + KEY `fk_runner_deleted_id` (`runner_id`), + CONSTRAINT `runners_deleted_ibfk_1` FOREIGN KEY fk_runner_deleted_id(`runner_id`) REFERENCES runners(`uuid`) ON DELETE CASCADE ); CREATE TABLE `jobs` (