diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b0b578f..422c52d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -25,7 +25,7 @@ jobs: go-version-file: 'go.mod' - name: lint run: | - go install honnef.co/go/tools/cmd/staticcheck@latest + go install honnef.co/go/tools/cmd/staticcheck@v0.8.0-rc.1 staticcheck ./... - name: vet run: | diff --git a/Dockerfile b/Dockerfile index b4b9421..a70e1ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.25 AS builder +FROM golang:1.27 AS builder WORKDIR /go/src/github.com/whywaita/myshoes diff --git a/go.mod b/go.mod index a9f0674..5d741cd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/whywaita/myshoes -go 1.25 +go 1.27 require ( github.com/bradleyfalzon/ghinstallation/v2 v2.17.0 @@ -16,7 +16,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.23.2 github.com/r3labs/diff/v2 v2.15.1 - github.com/satori/go.uuid v1.2.0 goji.io v2.0.2+incompatible golang.org/x/oauth2 v0.32.0 golang.org/x/sync v0.18.0 diff --git a/go.sum b/go.sum index 0ae1540..a71cc14 100644 --- a/go.sum +++ b/go.sum @@ -145,8 +145,6 @@ github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/datastore/interface.go b/pkg/datastore/interface.go index 39b0ccc..534e2d3 100644 --- a/pkg/datastore/interface.go +++ b/pkg/datastore/interface.go @@ -9,8 +9,6 @@ import ( "strings" "time" - uuid "github.com/satori/go.uuid" - "github.com/whywaita/myshoes/pkg/gh" "github.com/whywaita/myshoes/pkg/logger" ) @@ -29,27 +27,27 @@ var ( // Datastore is persistent storage type Datastore interface { CreateTarget(ctx context.Context, target Target) error - GetTarget(ctx context.Context, id uuid.UUID) (*Target, error) + GetTarget(ctx context.Context, id UUID) (*Target, error) GetTargetByScope(ctx context.Context, scope string) (*Target, error) ListTargets(ctx context.Context) ([]Target, error) - DeleteTarget(ctx context.Context, id uuid.UUID) error + DeleteTarget(ctx context.Context, id UUID) error // Deprecated: Use datastore.UpdateTargetStatus. - UpdateTargetStatus(ctx context.Context, targetID uuid.UUID, newStatus TargetStatus, description string) error - UpdateToken(ctx context.Context, targetID uuid.UUID, newToken string, newExpiredAt time.Time) error + UpdateTargetStatus(ctx context.Context, targetID UUID, newStatus TargetStatus, description string) error + UpdateToken(ctx context.Context, targetID UUID, newToken string, newExpiredAt time.Time) error - UpdateTargetParam(ctx context.Context, targetID uuid.UUID, newResourceType ResourceType, newProviderURL sql.NullString) error + UpdateTargetParam(ctx context.Context, targetID UUID, newResourceType ResourceType, newProviderURL sql.NullString) error EnqueueJob(ctx context.Context, job Job) error ListJobs(ctx context.Context) ([]Job, error) - DeleteJob(ctx context.Context, id uuid.UUID) error + DeleteJob(ctx context.Context, id UUID) error CreateRunner(ctx context.Context, runner Runner) error ListRunners(ctx context.Context) ([]Runner, error) - ListRunnersByTargetID(ctx context.Context, targetID uuid.UUID) ([]Runner, error) + ListRunnersByTargetID(ctx context.Context, targetID UUID) ([]Runner, error) ListRunnersLogBySince(ctx context.Context, since time.Time) ([]Runner, error) - GetRunner(ctx context.Context, id uuid.UUID) (*Runner, error) - DeleteRunner(ctx context.Context, id uuid.UUID, deletedAt time.Time, reason RunnerStatus) error + GetRunner(ctx context.Context, id UUID) (*Runner, error) + DeleteRunner(ctx context.Context, id UUID, deletedAt time.Time, reason RunnerStatus) error // Lock GetLock(ctx context.Context) error @@ -58,8 +56,8 @@ type Datastore interface { // Target is a target repository that will add auto-scaling runner. type Target struct { - UUID uuid.UUID `db:"uuid" json:"id"` - Scope string `db:"scope" json:"scope"` // repo (:owner/:repo) or org (:organization) + UUID UUID `db:"uuid" json:"id"` + Scope string `db:"scope" json:"scope"` // repo (:owner/:repo) or org (:organization) // deprecated GitHubToken string `db:"github_token" json:"github_token"` TokenExpiredAt time.Time `db:"token_expired_at" json:"token_expired_at"` @@ -107,7 +105,7 @@ func ListTargets(ctx context.Context, ds Datastore) ([]Target, error) { } // UpdateTargetStatus update datastore -func UpdateTargetStatus(ctx context.Context, ds Datastore, targetID uuid.UUID, newStatus TargetStatus, description string) error { +func UpdateTargetStatus(ctx context.Context, ds Datastore, targetID UUID, newStatus TargetStatus, description string) error { target, err := ds.GetTarget(ctx, targetID) if err != nil { return fmt.Errorf("failed to get target: %w", err) @@ -170,11 +168,11 @@ const ( // Job is a runner job type Job struct { - UUID uuid.UUID `db:"uuid"` + UUID UUID `db:"uuid"` GHEDomain sql.NullString `db:"ghe_domain"` Repository string `db:"repository"` // repo (:owner/:repo) CheckEventJSON string `db:"check_event"` - TargetID uuid.UUID `db:"target_id"` + TargetID UUID `db:"target_id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } @@ -198,10 +196,10 @@ func (j *Job) RepoURL() string { // Runner is a runner type Runner struct { - UUID uuid.UUID `db:"runner_id"` + UUID UUID `db:"runner_id"` ShoesType string `db:"shoes_type"` IPAddress string `db:"ip_address"` - TargetID uuid.UUID `db:"target_id"` + TargetID UUID `db:"target_id"` CloudID string `db:"cloud_id"` Deleted bool `db:"deleted"` Status RunnerStatus `db:"status"` diff --git a/pkg/datastore/memory/memory.go b/pkg/datastore/memory/memory.go index 497d585..ba70ca5 100644 --- a/pkg/datastore/memory/memory.go +++ b/pkg/datastore/memory/memory.go @@ -7,25 +7,23 @@ import ( "sync" "time" - uuid "github.com/satori/go.uuid" - "github.com/whywaita/myshoes/pkg/datastore" ) // Memory is implement datastore on-memory type Memory struct { mu *sync.RWMutex - targets map[uuid.UUID]datastore.Target - jobs map[uuid.UUID]datastore.Job - runners map[uuid.UUID]datastore.Runner + targets map[datastore.UUID]datastore.Target + jobs map[datastore.UUID]datastore.Job + runners map[datastore.UUID]datastore.Runner } // New create map func New() (*Memory, error) { m := &sync.RWMutex{} - t := map[uuid.UUID]datastore.Target{} - j := map[uuid.UUID]datastore.Job{} - r := map[uuid.UUID]datastore.Runner{} + t := map[datastore.UUID]datastore.Target{} + j := map[datastore.UUID]datastore.Job{} + r := map[datastore.UUID]datastore.Runner{} return &Memory{ mu: m, @@ -45,7 +43,7 @@ func (m *Memory) CreateTarget(ctx context.Context, target datastore.Target) erro } // GetTarget get a target -func (m *Memory) GetTarget(ctx context.Context, id uuid.UUID) (*datastore.Target, error) { +func (m *Memory) GetTarget(ctx context.Context, id datastore.UUID) (*datastore.Target, error) { m.mu.RLock() defer m.mu.RUnlock() @@ -87,7 +85,7 @@ func (m *Memory) ListTargets(ctx context.Context) ([]datastore.Target, error) { } // DeleteTarget delete a target -func (m *Memory) DeleteTarget(ctx context.Context, id uuid.UUID) error { +func (m *Memory) DeleteTarget(ctx context.Context, id datastore.UUID) error { m.mu.Lock() defer m.mu.Unlock() @@ -96,7 +94,7 @@ func (m *Memory) DeleteTarget(ctx context.Context, id uuid.UUID) error { } // UpdateTargetStatus update status in target -func (m *Memory) UpdateTargetStatus(ctx context.Context, targetID uuid.UUID, newStatus datastore.TargetStatus, description string) error { +func (m *Memory) UpdateTargetStatus(ctx context.Context, targetID datastore.UUID, newStatus datastore.TargetStatus, description string) error { m.mu.Lock() defer m.mu.Unlock() @@ -119,7 +117,7 @@ func (m *Memory) UpdateTargetStatus(ctx context.Context, targetID uuid.UUID, new } // UpdateToken update token in target -func (m *Memory) UpdateToken(ctx context.Context, targetID uuid.UUID, newToken string, newExpiredAt time.Time) error { +func (m *Memory) UpdateToken(ctx context.Context, targetID datastore.UUID, newToken string, newExpiredAt time.Time) error { m.mu.Lock() defer m.mu.Unlock() @@ -135,7 +133,7 @@ func (m *Memory) UpdateToken(ctx context.Context, targetID uuid.UUID, newToken s } // UpdateTargetParam update parameter of target -func (m *Memory) UpdateTargetParam(ctx context.Context, targetID uuid.UUID, newResourceType datastore.ResourceType, newProviderURL string) error { +func (m *Memory) UpdateTargetParam(ctx context.Context, targetID datastore.UUID, newResourceType datastore.ResourceType, newProviderURL string) error { m.mu.Lock() defer m.mu.Unlock() @@ -176,7 +174,7 @@ func (m *Memory) ListJobs(ctx context.Context) ([]datastore.Job, error) { } // DeleteJob delete a job -func (m *Memory) DeleteJob(ctx context.Context, id uuid.UUID) error { +func (m *Memory) DeleteJob(ctx context.Context, id datastore.UUID) error { m.mu.Lock() defer m.mu.Unlock() @@ -208,13 +206,13 @@ func (m *Memory) ListRunners(ctx context.Context) ([]datastore.Runner, error) { } // ListRunnersByTargetID get a not deleted runners that has target_id -func (m *Memory) ListRunnersByTargetID(ctx context.Context, targetID uuid.UUID) ([]datastore.Runner, error) { +func (m *Memory) ListRunnersByTargetID(ctx context.Context, targetID datastore.UUID) ([]datastore.Runner, error) { m.mu.Lock() defer m.mu.Unlock() var runners []datastore.Runner for _, r := range m.runners { - if uuid.Equal(r.TargetID, targetID) { + if r.TargetID == targetID { runners = append(runners, r) } } @@ -238,7 +236,7 @@ func (m *Memory) ListRunnersLogBySince(ctx context.Context, since time.Time) ([] } // GetRunner get a runner -func (m *Memory) GetRunner(ctx context.Context, id uuid.UUID) (*datastore.Runner, error) { +func (m *Memory) GetRunner(ctx context.Context, id datastore.UUID) (*datastore.Runner, error) { m.mu.Lock() defer m.mu.Unlock() @@ -251,7 +249,7 @@ func (m *Memory) GetRunner(ctx context.Context, id uuid.UUID) (*datastore.Runner } // DeleteRunner delete a runner -func (m *Memory) DeleteRunner(ctx context.Context, id uuid.UUID, deletedAt time.Time, reason datastore.RunnerStatus) error { +func (m *Memory) DeleteRunner(ctx context.Context, id datastore.UUID, deletedAt time.Time, reason datastore.RunnerStatus) error { m.mu.Lock() defer m.mu.Unlock() diff --git a/pkg/datastore/mysql/job.go b/pkg/datastore/mysql/job.go index 58d757d..5af4bd5 100644 --- a/pkg/datastore/mysql/job.go +++ b/pkg/datastore/mysql/job.go @@ -6,14 +6,13 @@ import ( "errors" "fmt" - uuid "github.com/satori/go.uuid" "github.com/whywaita/myshoes/pkg/datastore" ) // EnqueueJob add a job func (m *MySQL) EnqueueJob(ctx context.Context, job datastore.Job) error { query := `INSERT INTO jobs(uuid, ghe_domain, repository, check_event, target_id) VALUES (?, ?, ?, ?, ?)` - if _, err := m.Conn.ExecContext(ctx, query, job.UUID, job.GHEDomain, job.Repository, job.CheckEventJSON, job.TargetID.String()); err != nil { + if _, err := m.Conn.ExecContext(ctx, query, job.UUID, job.GHEDomain, job.Repository, job.CheckEventJSON, job.TargetID); err != nil { return fmt.Errorf("failed to execute INSERT query: %w", err) } @@ -43,9 +42,9 @@ func (m *MySQL) ListJobs(ctx context.Context) ([]datastore.Job, error) { } // DeleteJob delete a job -func (m *MySQL) DeleteJob(ctx context.Context, id uuid.UUID) error { +func (m *MySQL) DeleteJob(ctx context.Context, id datastore.UUID) error { query := `DELETE FROM jobs WHERE uuid = ?` - if _, err := m.Conn.ExecContext(ctx, query, id.String()); err != nil { + if _, err := m.Conn.ExecContext(ctx, query, id); err != nil { return fmt.Errorf("failed to execute DELETE query: %w", err) } diff --git a/pkg/datastore/mysql/job_test.go b/pkg/datastore/mysql/job_test.go index d98c60f..7bae6f0 100644 --- a/pkg/datastore/mysql/job_test.go +++ b/pkg/datastore/mysql/job_test.go @@ -10,13 +10,13 @@ import ( "github.com/google/go-cmp/cmp" "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" + "uuid" "github.com/whywaita/myshoes/internal/testutils" "github.com/whywaita/myshoes/pkg/datastore" ) -var testJobID = uuid.FromStringOrNil("1b4e5b7a-e3c1-4829-9cfd-eac4183f2c95") +var testJobID = datastore.UUID{UUID: uuid.MustParse("1b4e5b7a-e3c1-4829-9cfd-eac4183f2c95")} func TestMySQL_EnqueueJob(t *testing.T) { testDatastore, teardown := testutils.GetTestDatastore() @@ -174,7 +174,7 @@ func TestMySQL_DeleteJob(t *testing.T) { } tests := []struct { - input uuid.UUID + input datastore.UUID want *datastore.Job err bool }{ @@ -206,14 +206,14 @@ func TestMySQL_DeleteJob(t *testing.T) { } } -func getJobFromSQL(testDB *sqlx.DB, id uuid.UUID) (*datastore.Job, error) { +func getJobFromSQL(testDB *sqlx.DB, id datastore.UUID) (*datastore.Job, error) { var j datastore.Job query := `SELECT uuid, ghe_domain, repository, check_event, target_id FROM jobs WHERE uuid = ?` stmt, err := testDB.Preparex(query) if err != nil { return nil, fmt.Errorf("failed to prepare: %w", err) } - err = stmt.Get(&j, id) + err = stmt.Get(&j, id.String()) if err != nil { return nil, fmt.Errorf("failed to get job: %w", err) } diff --git a/pkg/datastore/mysql/runner.go b/pkg/datastore/mysql/runner.go index b6761f4..81ff9a3 100644 --- a/pkg/datastore/mysql/runner.go +++ b/pkg/datastore/mysql/runner.go @@ -7,7 +7,6 @@ import ( "fmt" "time" - uuid "github.com/satori/go.uuid" "github.com/whywaita/myshoes/pkg/datastore" ) @@ -16,19 +15,19 @@ func (m *MySQL) CreateRunner(ctx context.Context, runner datastore.Runner) error tx := m.Conn.MustBegin() queryRunner := `INSERT INTO runners(uuid) VALUES (?)` - if _, err := tx.ExecContext(ctx, queryRunner, runner.UUID.String()); err != nil { + if _, err := tx.ExecContext(ctx, queryRunner, runner.UUID); 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, resource_type, runner_user, repository_url, request_webhook, provider_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - if _, err := tx.ExecContext(ctx, queryDetail, runner.UUID.String(), runner.ShoesType, runner.IPAddress, runner.TargetID.String(), runner.CloudID, runner.ResourceType, runner.RunnerUser, runner.RepositoryURL, runner.RequestWebhook, runner.ProviderURL); err != nil { + if _, err := tx.ExecContext(ctx, queryDetail, runner.UUID, runner.ShoesType, runner.IPAddress, runner.TargetID, runner.CloudID, runner.ResourceType, runner.RunnerUser, runner.RepositoryURL, runner.RequestWebhook, runner.ProviderURL); 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 { + if _, err := tx.ExecContext(ctx, queryRunning, runner.UUID); err != nil { tx.Rollback() return fmt.Errorf("failed to execute INSERT query runners_running: %w", err) } @@ -58,7 +57,7 @@ func (m *MySQL) ListRunners(ctx context.Context) ([]datastore.Runner, error) { } // ListRunnersByTargetID get a not deleted runners that has target_id -func (m *MySQL) ListRunnersByTargetID(ctx context.Context, targetID uuid.UUID) ([]datastore.Runner, error) { +func (m *MySQL) ListRunnersByTargetID(ctx context.Context, targetID datastore.UUID) ([]datastore.Runner, error) { var runners []datastore.Runner query := `SELECT runner.runner_id, detail.shoes_type, detail.ip_address, detail.target_id, detail.cloud_id, detail.created_at, detail.updated_at, detail.resource_type, detail.repository_url, detail.request_webhook, detail.runner_user, detail.provider_url FROM runners_running AS runner JOIN runner_detail AS detail ON runner.runner_id = detail.runner_id WHERE detail.target_id = ?` @@ -92,11 +91,11 @@ func (m *MySQL) ListRunnersLogBySince(ctx context.Context, since time.Time) ([]d } // GetRunner get a runner -func (m *MySQL) GetRunner(ctx context.Context, id uuid.UUID) (*datastore.Runner, error) { +func (m *MySQL) GetRunner(ctx context.Context, id datastore.UUID) (*datastore.Runner, error) { var r datastore.Runner query := `SELECT runner_id, shoes_type, ip_address, target_id, cloud_id, created_at, updated_at, resource_type, repository_url, request_webhook, runner_user, provider_url FROM runner_detail WHERE runner_id = ?` - if err := m.Conn.GetContext(ctx, &r, query, id.String()); err != nil { + if err := m.Conn.GetContext(ctx, &r, query, id); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, datastore.ErrNotFound } @@ -108,17 +107,17 @@ 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 { +func (m *MySQL) DeleteRunner(ctx context.Context, id datastore.UUID, deletedAt time.Time, reason datastore.RunnerStatus) error { tx := m.Conn.MustBegin() queryDelete := `DELETE FROM runners_running WHERE runner_id = ?` - if _, err := tx.ExecContext(ctx, queryDelete, id.String()); err != nil { + if _, err := tx.ExecContext(ctx, queryDelete, id); 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 { + if _, err := tx.ExecContext(ctx, queryInsert, id, reason); err != nil { tx.Rollback() return fmt.Errorf("failed to execute INSERT query: %w", err) } diff --git a/pkg/datastore/mysql/runner_test.go b/pkg/datastore/mysql/runner_test.go index d796aa3..677b3f0 100644 --- a/pkg/datastore/mysql/runner_test.go +++ b/pkg/datastore/mysql/runner_test.go @@ -11,12 +11,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" "github.com/whywaita/myshoes/internal/testutils" "github.com/whywaita/myshoes/pkg/datastore" + "uuid" ) -var testRunnerID = uuid.FromStringOrNil("7943e412-c0ae-4068-ab24-3e71a13fbe53") +var testRunnerID = datastore.UUID{UUID: uuid.MustParse("7943e412-c0ae-4068-ab24-3e71a13fbe53")} func TestMySQL_CreateRunner(t *testing.T) { testDatastore, teardown := testutils.GetTestDatastore() @@ -218,14 +218,14 @@ func TestMySQL_ListRunnersNotReturnDeleted(t *testing.T) { RepositoryURL: "https://github.com/octocat/Hello-World", RequestWebhook: "{}", } - input.UUID = uuid.FromStringOrNil(fmt.Sprintf(u, i)) + input.UUID = datastore.UUID{UUID: uuid.MustParse(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") + err := testDatastore.DeleteRunner(context.Background(), datastore.UUID{UUID: uuid.MustParse(fmt.Sprintf(u, 0))}, time.Now(), "deleted") if err != nil { t.Fatalf("failed to delete runner: %+v", err) } @@ -250,7 +250,7 @@ func TestMySQL_ListRunnersNotReturnDeleted(t *testing.T) { RepositoryURL: "https://github.com/octocat/Hello-World", RequestWebhook: "{}", } - r.UUID = uuid.FromStringOrNil(fmt.Sprintf(u, i)) + r.UUID = datastore.UUID{UUID: uuid.MustParse(fmt.Sprintf(u, i))} want = append(want, r) } @@ -285,7 +285,7 @@ func TestMySQL_ListRunnersLogBySince(t *testing.T) { RepositoryURL: "https://github.com/octocat/Hello-World", RequestWebhook: "{}", } - input.UUID = uuid.FromStringOrNil(fmt.Sprintf(u, i)) + input.UUID = datastore.UUID{UUID: uuid.MustParse(fmt.Sprintf(u, i))} err := testDatastore.CreateRunner(context.Background(), input) if err != nil { t.Fatalf("failed to create runner: %+v", err) @@ -314,7 +314,7 @@ func TestMySQL_ListRunnersLogBySince(t *testing.T) { RepositoryURL: "https://github.com/octocat/Hello-World", RequestWebhook: "{}", } - r.UUID = uuid.FromStringOrNil(fmt.Sprintf(u, i)) + r.UUID = datastore.UUID{UUID: uuid.MustParse(fmt.Sprintf(u, i))} want = append(want, r) } @@ -350,7 +350,7 @@ func TestMySQL_GetRunner(t *testing.T) { } tests := []struct { - input uuid.UUID + input datastore.UUID want *datastore.Runner err bool }{ @@ -423,7 +423,7 @@ func TestMySQL_DeleteRunner(t *testing.T) { } tests := []struct { - input uuid.UUID + input datastore.UUID want *datastore.Runner err bool }{ @@ -462,21 +462,21 @@ func TestMySQL_DeleteRunner(t *testing.T) { } } -func getRunnerFromSQL(testDB *sqlx.DB, id uuid.UUID) (*datastore.Runner, error) { +func getRunnerFromSQL(testDB *sqlx.DB, id datastore.UUID) (*datastore.Runner, error) { var r datastore.Runner query := `SELECT runner_id, shoes_type, ip_address, target_id, cloud_id, created_at, updated_at, resource_type, repository_url, request_webhook, runner_user, provider_url 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) + err = stmt.Get(&r, id.String()) 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) { +func getRunningRunnerFromSQL(testDB *sqlx.DB, id datastore.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, detail.resource_type, detail.repository_url, detail.request_webhook FROM runner_detail AS detail JOIN runnesr_running AS running ON detail.runner_id = running.runner_id WHERE detail.runner_id = ?` @@ -484,14 +484,14 @@ FROM runner_detail AS detail JOIN runnesr_running AS running ON detail.runner_id if err != nil { return nil, fmt.Errorf("failed to prepare: %w", err) } - err = stmt.Get(&r, id) + err = stmt.Get(&r, id.String()) 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) { +func getDeletedRunnerFromSQL(testDB *sqlx.DB, id datastore.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, detail.resource_type, detail.repository_url, detail.request_webhook FROM runner_detail AS detail JOIN runners_deleted AS deleted ON detail.runner_id = deleted.runner_id WHERE detail.runner_id = ?` @@ -499,7 +499,7 @@ FROM runner_detail AS detail JOIN runners_deleted AS deleted ON detail.runner_id if err != nil { return nil, fmt.Errorf("failed to prepare: %w", err) } - err = stmt.Get(&r, id) + err = stmt.Get(&r, id.String()) if err != nil { return nil, fmt.Errorf("failed to get runner: %w", err) } diff --git a/pkg/datastore/mysql/target.go b/pkg/datastore/mysql/target.go index 4a0a5a2..9d81de5 100644 --- a/pkg/datastore/mysql/target.go +++ b/pkg/datastore/mysql/target.go @@ -7,7 +7,6 @@ import ( "fmt" "time" - uuid "github.com/satori/go.uuid" "github.com/whywaita/myshoes/pkg/datastore" ) @@ -34,10 +33,10 @@ func (m *MySQL) CreateTarget(ctx context.Context, target datastore.Target) error } // GetTarget get a target -func (m *MySQL) GetTarget(ctx context.Context, id uuid.UUID) (*datastore.Target, error) { +func (m *MySQL) GetTarget(ctx context.Context, id datastore.UUID) (*datastore.Target, error) { var t datastore.Target query := `SELECT uuid, scope, github_token, token_expired_at, resource_type, provider_url, status, status_description, created_at, updated_at FROM targets WHERE uuid = ?` - if err := m.Conn.GetContext(ctx, &t, query, id.String()); err != nil { + if err := m.Conn.GetContext(ctx, &t, query, id); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, datastore.ErrNotFound } @@ -75,9 +74,9 @@ func (m *MySQL) ListTargets(ctx context.Context) ([]datastore.Target, error) { } // DeleteTarget delete a target -func (m *MySQL) DeleteTarget(ctx context.Context, id uuid.UUID) error { +func (m *MySQL) DeleteTarget(ctx context.Context, id datastore.UUID) error { query := `UPDATE targets SET status = "deleted" WHERE uuid = ?` - if _, err := m.Conn.ExecContext(ctx, query, id.String()); err != nil { + if _, err := m.Conn.ExecContext(ctx, query, id); err != nil { return fmt.Errorf("failed to execute DELETE query: %w", err) } @@ -85,9 +84,9 @@ func (m *MySQL) DeleteTarget(ctx context.Context, id uuid.UUID) error { } // UpdateTargetStatus update status in target -func (m *MySQL) UpdateTargetStatus(ctx context.Context, targetID uuid.UUID, newStatus datastore.TargetStatus, description string) error { +func (m *MySQL) UpdateTargetStatus(ctx context.Context, targetID datastore.UUID, newStatus datastore.TargetStatus, description string) error { query := `UPDATE targets SET status = ?, status_description = ? WHERE uuid = ?` - if _, err := m.Conn.ExecContext(ctx, query, newStatus, description, targetID.String()); err != nil { + if _, err := m.Conn.ExecContext(ctx, query, newStatus, description, targetID); err != nil { return fmt.Errorf("failed to execute UPDATE query: %w", err) } @@ -95,9 +94,9 @@ func (m *MySQL) UpdateTargetStatus(ctx context.Context, targetID uuid.UUID, newS } // UpdateToken update token in target -func (m *MySQL) UpdateToken(ctx context.Context, targetID uuid.UUID, newToken string, newExpiredAt time.Time) error { +func (m *MySQL) UpdateToken(ctx context.Context, targetID datastore.UUID, newToken string, newExpiredAt time.Time) error { query := `UPDATE targets SET github_token = ?, token_expired_at = ? WHERE uuid = ?` - if _, err := m.Conn.ExecContext(ctx, query, newToken, newExpiredAt, targetID.String()); err != nil { + if _, err := m.Conn.ExecContext(ctx, query, newToken, newExpiredAt, targetID); err != nil { return fmt.Errorf("failed to execute UPDATE query: %w", err) } @@ -105,9 +104,9 @@ func (m *MySQL) UpdateToken(ctx context.Context, targetID uuid.UUID, newToken st } // UpdateTargetParam update parameter of target -func (m *MySQL) UpdateTargetParam(ctx context.Context, targetID uuid.UUID, newResourceType datastore.ResourceType, newProviderURL sql.NullString) error { +func (m *MySQL) UpdateTargetParam(ctx context.Context, targetID datastore.UUID, newResourceType datastore.ResourceType, newProviderURL sql.NullString) error { query := `UPDATE targets SET resource_type = ?, provider_url = ? WHERE uuid = ?` - if _, err := m.Conn.ExecContext(ctx, query, newResourceType, newProviderURL, targetID.String()); err != nil { + if _, err := m.Conn.ExecContext(ctx, query, newResourceType, newProviderURL, targetID); err != nil { return fmt.Errorf("failed to execute UPDATE query: %w", err) } diff --git a/pkg/datastore/mysql/target_test.go b/pkg/datastore/mysql/target_test.go index f828b0b..cdea04b 100644 --- a/pkg/datastore/mysql/target_test.go +++ b/pkg/datastore/mysql/target_test.go @@ -11,15 +11,15 @@ import ( "github.com/google/go-cmp/cmp" "github.com/jmoiron/sqlx" - uuid "github.com/satori/go.uuid" + "uuid" "github.com/whywaita/myshoes/internal/testutils" "github.com/whywaita/myshoes/pkg/datastore" ) -var testTargetID = uuid.FromStringOrNil("8a72d42c-372c-4e0d-9c6a-4304d44af137") -var testTargetID2 = uuid.FromStringOrNil("d14ccfea-b123-4ada-974e-bbff0937e9c7") -var testTargetID3 = uuid.FromStringOrNil("5c1816ff-4813-46b0-b1ba-30d135a2f3f5") +var testTargetID = datastore.UUID{UUID: uuid.MustParse("8a72d42c-372c-4e0d-9c6a-4304d44af137")} +var testTargetID2 = datastore.UUID{UUID: uuid.MustParse("d14ccfea-b123-4ada-974e-bbff0937e9c7")} +var testTargetID3 = datastore.UUID{UUID: uuid.MustParse("5c1816ff-4813-46b0-b1ba-30d135a2f3f5")} var testScopeOrg = "octocat" var testScopeRepo = "octocat/hello-world" var testScopeRepo2 = "octocat/hello-world2" @@ -170,7 +170,7 @@ func TestMySQL_GetTarget(t *testing.T) { } tests := []struct { - input uuid.UUID + input datastore.UUID want *datastore.Target err bool }{ @@ -477,7 +477,7 @@ func TestMySQL_DeleteTarget(t *testing.T) { } tests := []struct { - input uuid.UUID + input datastore.UUID want *datastore.Target err bool }{ @@ -581,7 +581,7 @@ func TestMySQL_UpdateStatus(t *testing.T) { } for _, test := range tests { - tID := uuid.NewV4() + tID := datastore.UUID{UUID: uuid.NewV4()} if err := testDatastore.CreateTarget(context.Background(), datastore.Target{ UUID: tID, Scope: testScopeRepo, @@ -606,7 +606,7 @@ func TestMySQL_UpdateStatus(t *testing.T) { t.Fatalf("failed to get target from SQL: %+v", err) } if got != nil { - got.UUID = uuid.UUID{} + got.UUID = datastore.UUID{} got.CreatedAt = time.Time{} got.UpdatedAt = time.Time{} } @@ -683,7 +683,7 @@ func TestMySQL_UpdateToken(t *testing.T) { } for _, test := range tests { - tID := uuid.NewV4() + tID := datastore.UUID{UUID: uuid.NewV4()} if err := testDatastore.CreateTarget(context.Background(), datastore.Target{ UUID: tID, Scope: testScopeRepo, @@ -707,7 +707,7 @@ func TestMySQL_UpdateToken(t *testing.T) { t.Fatalf("failed to get target from SQL: %+v", err) } if got != nil { - got.UUID = uuid.UUID{} + got.UUID = datastore.UUID{} got.CreatedAt = time.Time{} got.UpdatedAt = time.Time{} } @@ -825,7 +825,7 @@ func TestMySQL_UpdateTargetParam(t *testing.T) { } for _, test := range tests { - tID := uuid.NewV4() + tID := datastore.UUID{UUID: uuid.NewV4()} if err := testDatastore.CreateTarget(context.Background(), datastore.Target{ UUID: tID, Scope: testScopeRepo, @@ -849,7 +849,7 @@ func TestMySQL_UpdateTargetParam(t *testing.T) { t.Fatalf("failed to get target from SQL: %+v", err) } if got != nil { - got.UUID = uuid.UUID{} + got.UUID = datastore.UUID{} got.CreatedAt = time.Time{} got.UpdatedAt = time.Time{} got.TokenExpiredAt = time.Time{} @@ -865,14 +865,14 @@ func TestMySQL_UpdateTargetParam(t *testing.T) { } } -func getTargetFromSQL(testDB *sqlx.DB, uuid uuid.UUID) (*datastore.Target, error) { +func getTargetFromSQL(testDB *sqlx.DB, id datastore.UUID) (*datastore.Target, error) { var t datastore.Target query := `SELECT uuid, scope, ghe_domain, github_token, token_expired_at, resource_type, provider_url, status, status_description, created_at, updated_at FROM targets WHERE uuid = ?` stmt, err := testDB.Preparex(query) if err != nil { return nil, fmt.Errorf("failed to prepare: %w", err) } - err = stmt.Get(&t, uuid) + err = stmt.Get(&t, id.String()) if err != nil { return nil, fmt.Errorf("failed to get target: %w", err) } diff --git a/pkg/datastore/uuid.go b/pkg/datastore/uuid.go new file mode 100644 index 0000000..3294407 --- /dev/null +++ b/pkg/datastore/uuid.go @@ -0,0 +1,45 @@ +package datastore + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "uuid" +) + +// UUID wraps the standard library uuid.UUID and adds database/sql support +// (driver.Valuer and sql.Scanner). UUID columns are stored as VARCHAR(36), so +// Valuc returns the string form and Scan parses the string back. +type UUID struct { + uuid.UUID +} + +var ( + _ driver.Valuer = UUID{} + _ sql.Scanner = (*UUID)(nil) +) + +// Value implements driver.Valuer. +func (u UUID) Value() (driver.Value, error) { + return u.String(), nil +} + +// Scan implements sql.Scanner. +func (u *UUID) Scan(src interface{}) error { + var s string + switch v := src.(type) { + case string: + s = v + case []byte: + s = string(v) + default: + return fmt.Errorf("cannot scan %T into datastore.UUID", src) + } + + parsed, err := uuid.Parse(s) + if err != nil { + return fmt.Errorf("failed to parse uuid %q: %w", s, err) + } + u.UUID = parsed + return nil +} diff --git a/pkg/metric/scrape_memory.go b/pkg/metric/scrape_memory.go index d4908e4..b570956 100644 --- a/pkg/metric/scrape_memory.go +++ b/pkg/metric/scrape_memory.go @@ -5,8 +5,6 @@ import ( "fmt" "sync/atomic" - uuid "github.com/satori/go.uuid" - "github.com/prometheus/client_golang/prometheus" "github.com/whywaita/myshoes/pkg/config" "github.com/whywaita/myshoes/pkg/datastore" @@ -146,13 +144,13 @@ func scrapeStarterValues(ch chan<- prometheus.Metric) error { runner.DeleteRetryCount.Range(func(key, value any) bool { ch <- prometheus.MustNewConstMetric( - memoryRunnerDeleteRetryCount, prometheus.GaugeValue, float64(value.(int)), key.(uuid.UUID).String()) + memoryRunnerDeleteRetryCount, prometheus.GaugeValue, float64(value.(int)), key.(datastore.UUID).String()) return true }) starter.AddInstanceRetryCount.Range(func(key, value any) bool { ch <- prometheus.MustNewConstMetric( - memoryRunnerCreateRetryCount, prometheus.GaugeValue, float64(value.(int)), key.(uuid.UUID).String()) + memoryRunnerCreateRetryCount, prometheus.GaugeValue, float64(value.(int)), key.(datastore.UUID).String()) return true }) diff --git a/pkg/runner/util.go b/pkg/runner/util.go index 35ba490..1c1fed2 100644 --- a/pkg/runner/util.go +++ b/pkg/runner/util.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - uuid "github.com/satori/go.uuid" "github.com/whywaita/myshoes/pkg/datastore" + "uuid" ) // ToName convert uuid to runner name @@ -16,7 +16,7 @@ func ToName(u string) string { // ToUUID convert runner name to uuid func ToUUID(name string) (uuid.UUID, error) { u := strings.TrimPrefix(name, "myshoes-") - return uuid.FromString(u) + return uuid.Parse(u) } // ToReason convert status from GitHub to datastore.RunnerStatus diff --git a/pkg/starter/starter.go b/pkg/starter/starter.go index 2c92675..51dbf24 100644 --- a/pkg/starter/starter.go +++ b/pkg/starter/starter.go @@ -18,7 +18,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - uuid "github.com/satori/go.uuid" "github.com/whywaita/myshoes/internal/util" "github.com/whywaita/myshoes/pkg/config" "github.com/whywaita/myshoes/pkg/datastore" @@ -27,6 +26,7 @@ import ( "github.com/whywaita/myshoes/pkg/runner" "github.com/whywaita/myshoes/pkg/shoes" "github.com/whywaita/myshoes/pkg/starter/safety" + "uuid" ) var ( @@ -565,7 +565,7 @@ func enqueueRescueJob(ctx context.Context, workflowJob *github.WorkflowJobEvent, } logger.Logf(false, "rescue pending job: (repo: %s, gh_run_id: %d, gh_job_id: %d)", *repository.HTMLURL, workflowJob.WorkflowJob.GetRunID(), workflowJob.WorkflowJob.GetID()) - jobID := uuid.NewV4() + jobID := datastore.UUID{UUID: uuid.NewV4()} job := datastore.Job{ UUID: jobID, GHEDomain: sql.NullString{ diff --git a/pkg/web/target.go b/pkg/web/target.go index 7c8ea44..e287225 100644 --- a/pkg/web/target.go +++ b/pkg/web/target.go @@ -10,7 +10,7 @@ import ( "time" "github.com/r3labs/diff/v2" - uuid "github.com/satori/go.uuid" + "uuid" "github.com/whywaita/myshoes/pkg/datastore" "github.com/whywaita/myshoes/pkg/gh" @@ -115,7 +115,7 @@ func handleTargetRead(w http.ResponseWriter, r *http.Request, ds datastore.Datas func sanitizeTarget(t datastore.Target) UserTarget { ut := UserTarget{ - UUID: t.UUID, + UUID: t.UUID.UUID, Scope: t.Scope, TokenExpiredAt: t.TokenExpiredAt, ResourceType: t.ResourceType.String(), @@ -219,14 +219,14 @@ func handleTargetDelete(w http.ResponseWriter, r *http.Request, ds datastore.Dat w.WriteHeader(http.StatusNoContent) } -func parseReqTargetID(r *http.Request) (uuid.UUID, error) { +func parseReqTargetID(r *http.Request) (datastore.UUID, error) { targetIDStr := pat.Param(r, "id") - targetID, err := uuid.FromString(targetIDStr) + targetID, err := uuid.Parse(targetIDStr) if err != nil { - return uuid.UUID{}, fmt.Errorf("failed to parse target id: %w", err) + return datastore.UUID{}, fmt.Errorf("failed to parse target id: %w", err) } - return targetID, nil + return datastore.UUID{UUID: targetID}, nil } // ErrorResponse is error response @@ -248,7 +248,7 @@ func validateUpdateTarget(old, new datastore.Target) error { newv := new for _, t := range []*datastore.Target{&oldv, &newv} { - t.UUID = uuid.UUID{} + t.UUID = datastore.UUID{} // can update variables t.ResourceType = datastore.ResourceTypeUnknown diff --git a/pkg/web/target_create.go b/pkg/web/target_create.go index 6ed0353..541e391 100644 --- a/pkg/web/target_create.go +++ b/pkg/web/target_create.go @@ -9,7 +9,7 @@ import ( "net/http" "time" - uuid "github.com/satori/go.uuid" + "uuid" "github.com/whywaita/myshoes/pkg/config" "github.com/whywaita/myshoes/pkg/datastore" @@ -65,7 +65,7 @@ func handleTargetCreate(w http.ResponseWriter, r *http.Request, ds datastore.Dat } target, err := ds.GetTargetByScope(ctx, t.Scope) - var targetUUID uuid.UUID + var targetUUID datastore.UUID switch { case errors.Is(err, datastore.ErrNotFound): @@ -143,8 +143,8 @@ func isValidScopeAndToken(ctx context.Context, scope, githubPersonalToken string return nil } -func createNewTarget(ctx context.Context, input datastore.Target, ds datastore.Datastore) (*uuid.UUID, error) { - input.UUID = uuid.NewV4() +func createNewTarget(ctx context.Context, input datastore.Target, ds datastore.Datastore) (*datastore.UUID, error) { + input.UUID = datastore.UUID{UUID: uuid.NewV4()} now := time.Now().UTC() input.CreatedAt = now input.UpdatedAt = now diff --git a/pkg/web/target_test.go b/pkg/web/target_test.go index 0f2baaa..566e9ea 100644 --- a/pkg/web/target_test.go +++ b/pkg/web/target_test.go @@ -13,7 +13,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-github/v80/github" - uuid "github.com/satori/go.uuid" + "uuid" "github.com/whywaita/myshoes/internal/testutils" "github.com/whywaita/myshoes/pkg/datastore" @@ -216,7 +216,7 @@ func Test_handleTargetCreate_recreated(t *testing.T) { t.Fatalf("must be response statuscode is 201, but got %d: %+v", code, string(content)) } - got, err := testDatastore.GetTarget(context.Background(), u) + got, err := testDatastore.GetTarget(context.Background(), datastore.UUID{UUID: u}) if err != nil { t.Fatalf("failed to get created target: %+v", err) } @@ -277,7 +277,7 @@ func Test_handleTargetCreate_recreated_update(t *testing.T) { t.Fatalf("must be response statuscode is 201, but got %d: %+v", code, string(content)) } - got, err := testDatastore.GetTarget(context.Background(), u) + got, err := testDatastore.GetTarget(context.Background(), datastore.UUID{UUID: u}) if err != nil { t.Fatalf("failed to get created target: %+v", err) } @@ -614,7 +614,7 @@ func Test_handleTargetDelete(t *testing.T) { { input: targetUUID, want: &datastore.Target{ - UUID: targetUUID, + UUID: datastore.UUID{UUID: targetUUID}, Scope: "repo", GitHubToken: testGitHubAppToken, TokenExpiredAt: testTime, @@ -641,7 +641,7 @@ func Test_handleTargetDelete(t *testing.T) { t.Fatalf("must be response statuscode is 204, but got %d: %+v", code, string(content)) } - got, err := testDatastore.GetTarget(context.Background(), test.input) + got, err := testDatastore.GetTarget(context.Background(), datastore.UUID{UUID: test.input}) if err != nil { t.Fatalf("failed to get target from datastore: %+v", err) } diff --git a/pkg/web/webhook.go b/pkg/web/webhook.go index e221eeb..b631a88 100644 --- a/pkg/web/webhook.go +++ b/pkg/web/webhook.go @@ -11,7 +11,7 @@ import ( "time" "github.com/google/go-github/v80/github" - uuid "github.com/satori/go.uuid" + "uuid" "github.com/whywaita/myshoes/pkg/config" "github.com/whywaita/myshoes/pkg/datastore" @@ -167,7 +167,7 @@ func processCheckRun(ctx context.Context, ds datastore.Datastore, repoName, repo return nil } - jobID := uuid.NewV4() + jobID := datastore.UUID{UUID: uuid.NewV4()} j := datastore.Job{ UUID: jobID, GHEDomain: sql.NullString{