Semaphore/db/Store.go
Denis Gukov 6fa3e50198
Runners (#1477)
* feat(runners): add migartion

* feat(runners): sql implementation
2023-09-11 01:02:10 +02:00

359 lines
12 KiB
Go

package db
import (
"encoding/json"
"errors"
log "github.com/Sirupsen/logrus"
"reflect"
"strings"
"time"
)
const databaseTimeFormat = "2006-01-02T15:04:05:99Z"
// GetParsedTime returns the timestamp as it will retrieved from the database
// This allows us to create timestamp consistency on return values from create requests
func GetParsedTime(t time.Time) time.Time {
parsedTime, err := time.Parse(databaseTimeFormat, t.Format(databaseTimeFormat))
if err != nil {
log.Error(err)
}
return parsedTime
}
func ObjectToJSON(obj interface{}) *string {
if obj == nil || (reflect.ValueOf(obj).Kind() == reflect.Ptr && reflect.ValueOf(obj).IsNil()) {
return nil
}
bytes, err := json.Marshal(obj)
if err != nil {
return nil
}
str := string(bytes)
return &str
}
type RetrieveQueryParams struct {
Offset int
Count int
SortBy string
SortInverted bool
}
type ObjectReferrer struct {
ID int `json:"id"`
Name string `json:"name"`
}
type ObjectReferrers struct {
Templates []ObjectReferrer `json:"templates"`
Inventories []ObjectReferrer `json:"inventories"`
Repositories []ObjectReferrer `json:"repositories"`
}
// ObjectProps describe database entities.
// It mainly used for NoSQL implementations (currently BoltDB) to preserve same
// data structure of different implementations and easy change it if required.
type ObjectProps struct {
TableName string
Type reflect.Type // to which type the table bust be mapped.
IsGlobal bool // doesn't belong to other table, for example to project or user.
ReferringColumnSuffix string
PrimaryColumnName string
SortableColumns []string
DefaultSortingColumn string
SortInverted bool // sort from high to low object ID by default. It is useful for some NoSQL implementations.
}
var ErrNotFound = errors.New("no rows in result set")
var ErrInvalidOperation = errors.New("invalid operation")
type ValidationError struct {
Message string
}
func (e *ValidationError) Error() string {
return e.Message
}
type Store interface {
// Connect connects to the database.
// Token parameter used if PermanentConnection returns false.
// Token used for debugging of session connections.
Connect(token string)
Close(token string)
// PermanentConnection returns true if connection should be kept from start to finish of the app.
// This mode is suitable for MySQL and Postgres but not for BoltDB.
// For BoltDB we should reconnect for each request because BoltDB support only one connection at time.
PermanentConnection() bool
// IsInitialized indicates is database already initialized, or it is empty.
// The method is useful for creating required entities in database during first run.
IsInitialized() (bool, error)
// IsMigrationApplied queries the database to see if a migration table with
// this version id exists already
IsMigrationApplied(version Migration) (bool, error)
// ApplyMigration runs executes a database migration
ApplyMigration(version Migration) error
// TryRollbackMigration attempts to roll back the database to an earlier version
// if a rollback exists
TryRollbackMigration(version Migration)
GetEnvironment(projectID int, environmentID int) (Environment, error)
GetEnvironmentRefs(projectID int, environmentID int) (ObjectReferrers, error)
GetEnvironments(projectID int, params RetrieveQueryParams) ([]Environment, error)
UpdateEnvironment(env Environment) error
CreateEnvironment(env Environment) (Environment, error)
DeleteEnvironment(projectID int, templateID int) error
GetInventory(projectID int, inventoryID int) (Inventory, error)
GetInventoryRefs(projectID int, inventoryID int) (ObjectReferrers, error)
GetInventories(projectID int, params RetrieveQueryParams) ([]Inventory, error)
UpdateInventory(inventory Inventory) error
CreateInventory(inventory Inventory) (Inventory, error)
DeleteInventory(projectID int, inventoryID int) error
GetRepository(projectID int, repositoryID int) (Repository, error)
GetRepositoryRefs(projectID int, repositoryID int) (ObjectReferrers, error)
GetRepositories(projectID int, params RetrieveQueryParams) ([]Repository, error)
UpdateRepository(repository Repository) error
CreateRepository(repository Repository) (Repository, error)
DeleteRepository(projectID int, repositoryID int) error
GetAccessKey(projectID int, accessKeyID int) (AccessKey, error)
GetAccessKeyRefs(projectID int, accessKeyID int) (ObjectReferrers, error)
GetAccessKeys(projectID int, params RetrieveQueryParams) ([]AccessKey, error)
RekeyAccessKeys(oldKey string) error
UpdateAccessKey(accessKey AccessKey) error
CreateAccessKey(accessKey AccessKey) (AccessKey, error)
DeleteAccessKey(projectID int, accessKeyID int) error
GetUsers(params RetrieveQueryParams) ([]User, error)
CreateUserWithoutPassword(user User) (User, error)
CreateUser(user UserWithPwd) (User, error)
DeleteUser(userID int) error
// UpdateUser updates all fields of the entity except Pwd.
// Pwd should be present of you want update user password. Empty Pwd ignored.
UpdateUser(user UserWithPwd) error
SetUserPassword(userID int, password string) error
GetUser(userID int) (User, error)
GetUserByLoginOrEmail(login string, email string) (User, error)
GetProject(projectID int) (Project, error)
GetProjects(userID int) ([]Project, error)
CreateProject(project Project) (Project, error)
DeleteProject(projectID int) error
UpdateProject(project Project) error
GetTemplates(projectID int, filter TemplateFilter, params RetrieveQueryParams) ([]Template, error)
GetTemplateRefs(projectID int, templateID int) (ObjectReferrers, error)
CreateTemplate(template Template) (Template, error)
UpdateTemplate(template Template) error
GetTemplate(projectID int, templateID int) (Template, error)
DeleteTemplate(projectID int, templateID int) error
GetSchedules() ([]Schedule, error)
GetTemplateSchedules(projectID int, templateID int) ([]Schedule, error)
CreateSchedule(schedule Schedule) (Schedule, error)
UpdateSchedule(schedule Schedule) error
SetScheduleCommitHash(projectID int, scheduleID int, hash string) error
GetSchedule(projectID int, scheduleID int) (Schedule, error)
DeleteSchedule(projectID int, scheduleID int) error
GetProjectUsers(projectID int, params RetrieveQueryParams) ([]UserWithProjectRole, error)
CreateProjectUser(projectUser ProjectUser) (ProjectUser, error)
DeleteProjectUser(projectID int, userID int) error
GetProjectUser(projectID int, userID int) (ProjectUser, error)
UpdateProjectUser(projectUser ProjectUser) error
CreateEvent(event Event) (Event, error)
GetUserEvents(userID int, params RetrieveQueryParams) ([]Event, error)
GetEvents(projectID int, params RetrieveQueryParams) ([]Event, error)
GetAPITokens(userID int) ([]APIToken, error)
CreateAPIToken(token APIToken) (APIToken, error)
GetAPIToken(tokenID string) (APIToken, error)
ExpireAPIToken(userID int, tokenID string) error
DeleteAPIToken(userID int, tokenID string) error
GetSession(userID int, sessionID int) (Session, error)
CreateSession(session Session) (Session, error)
ExpireSession(userID int, sessionID int) error
TouchSession(userID int, sessionID int) error
CreateTask(task Task) (Task, error)
UpdateTask(task Task) error
GetTemplateTasks(projectID int, templateID int, params RetrieveQueryParams) ([]TaskWithTpl, error)
GetProjectTasks(projectID int, params RetrieveQueryParams) ([]TaskWithTpl, error)
GetTask(projectID int, taskID int) (Task, error)
DeleteTaskWithOutputs(projectID int, taskID int) error
GetTaskOutputs(projectID int, taskID int) ([]TaskOutput, error)
CreateTaskOutput(output TaskOutput) (TaskOutput, error)
GetView(projectID int, viewID int) (View, error)
GetViews(projectID int) ([]View, error)
UpdateView(view View) error
CreateView(view View) (View, error)
DeleteView(projectID int, viewID int) error
SetViewPositions(projectID int, viewPositions map[int]int) error
GetRunner(projectID int, runnerID int) (Runner, error)
GetRunners(projectID int) ([]Runner, error)
DeleteRunner(projectID int, runnerID int) error
GetGlobalRunner(runnerID int) (Runner, error)
GetGlobalRunners() ([]Runner, error)
DeleteGlobalRunner(runnerID int) error
UpdateRunner(runner Runner) error
CreateRunner(runner Runner) (Runner, error)
}
var AccessKeyProps = ObjectProps{
TableName: "access_key",
Type: reflect.TypeOf(AccessKey{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "key_id",
SortableColumns: []string{"name", "type"},
DefaultSortingColumn: "name",
}
var EnvironmentProps = ObjectProps{
TableName: "project__environment",
Type: reflect.TypeOf(Environment{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "environment_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
var InventoryProps = ObjectProps{
TableName: "project__inventory",
Type: reflect.TypeOf(Inventory{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "inventory_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
var RepositoryProps = ObjectProps{
TableName: "project__repository",
Type: reflect.TypeOf(Repository{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "repository_id",
DefaultSortingColumn: "name",
}
var TemplateProps = ObjectProps{
TableName: "project__template",
Type: reflect.TypeOf(Template{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "template_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
var ScheduleProps = ObjectProps{
TableName: "project__schedule",
Type: reflect.TypeOf(Schedule{}),
PrimaryColumnName: "id",
}
var ProjectUserProps = ObjectProps{
TableName: "project__user",
Type: reflect.TypeOf(ProjectUser{}),
PrimaryColumnName: "user_id",
}
var ProjectProps = ObjectProps{
TableName: "project",
Type: reflect.TypeOf(Project{}),
PrimaryColumnName: "id",
DefaultSortingColumn: "name",
IsGlobal: true,
}
var UserProps = ObjectProps{
TableName: "user",
Type: reflect.TypeOf(User{}),
PrimaryColumnName: "id",
IsGlobal: true,
}
var SessionProps = ObjectProps{
TableName: "session",
Type: reflect.TypeOf(Session{}),
PrimaryColumnName: "id",
}
var TokenProps = ObjectProps{
TableName: "user__token",
Type: reflect.TypeOf(APIToken{}),
PrimaryColumnName: "id",
}
var TaskProps = ObjectProps{
TableName: "task",
Type: reflect.TypeOf(Task{}),
PrimaryColumnName: "id",
IsGlobal: true,
SortInverted: true,
}
var TaskOutputProps = ObjectProps{
TableName: "task__output",
Type: reflect.TypeOf(TaskOutput{}),
}
var ViewProps = ObjectProps{
TableName: "project__view",
Type: reflect.TypeOf(View{}),
PrimaryColumnName: "id",
DefaultSortingColumn: "position",
}
var GlobalRunnerProps = ObjectProps{
TableName: "runner",
Type: reflect.TypeOf(Runner{}),
PrimaryColumnName: "id",
IsGlobal: true,
}
func (p ObjectProps) GetReferringFieldsFrom(t reflect.Type) (fields []string, err error) {
n := t.NumField()
for i := 0; i < n; i++ {
if !strings.HasSuffix(t.Field(i).Tag.Get("db"), p.ReferringColumnSuffix) {
continue
}
fields = append(fields, t.Field(i).Tag.Get("db"))
}
for i := 0; i < n; i++ {
if t.Field(i).Tag != "" || t.Field(i).Type.Kind() != reflect.Struct {
continue
}
var nested []string
nested, err = p.GetReferringFieldsFrom(t.Field(i).Type)
if err != nil {
return
}
fields = append(fields, nested...)
}
return
}
func StoreSession(store Store, token string, callback func()) {
if !store.PermanentConnection() {
store.Connect(token)
}
callback()
if !store.PermanentConnection() {
store.Close(token)
}
}