Semaphore/db/Store.go

532 lines
18 KiB
Go
Raw Permalink Normal View History

package db
import (
2024-07-15 10:57:12 +02:00
"database/sql/driver"
2022-01-19 13:05:48 +01:00
"encoding/json"
"errors"
"reflect"
2022-02-03 08:05:13 +01:00
"strings"
2020-12-04 23:22:05 +01:00
"time"
log "github.com/sirupsen/logrus"
)
2020-12-04 23:22:05 +01:00
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
}
2022-01-19 13:05:48 +01:00
func ObjectToJSON(obj interface{}) *string {
2024-10-07 13:50:25 +02:00
if obj == nil ||
(reflect.ValueOf(obj).Kind() == reflect.Ptr && reflect.ValueOf(obj).IsNil()) ||
(reflect.ValueOf(obj).Kind() == reflect.Slice && reflect.ValueOf(obj).IsZero()) {
2022-01-19 13:05:48 +01:00
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
2024-07-07 19:12:21 +02:00
Filter string
}
2022-02-03 08:05:13 +01:00
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"`
}
2024-02-11 20:52:14 +01:00
type IntegrationReferrers struct {
IntegrationMatchers []ObjectReferrer `json:"matchers"`
IntegrationExtractValues []ObjectReferrer `json:"values"`
2023-07-03 01:41:13 +02:00
}
2024-02-11 20:52:14 +01:00
type IntegrationExtractorChildReferrers struct {
2024-03-06 14:28:24 +01:00
Integrations []ObjectReferrer `json:"integrations"`
2023-07-03 01:41:13 +02:00
}
2022-02-03 08:05:13 +01:00
// ObjectProps describe database entities.
2021-10-12 13:37:51 +02:00
// It mainly used for NoSQL implementations (currently BoltDB) to preserve same
// data structure of different implementations and easy change it if required.
2022-02-03 08:05:13 +01:00
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")
2021-11-02 20:30:45 +01:00
type ValidationError struct {
Message string
}
func (e *ValidationError) Error() string {
return e.Message
}
type Store interface {
// Connect connects to the database.
2022-11-19 11:58:46 +01:00
// 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
2021-12-19 13:31:23 +01:00
// IsInitialized indicates is database already initialized, or it is empty.
// The method is useful for creating required entities in database during first run.
2021-12-18 14:16:34 +01:00
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
2022-02-03 08:05:13 +01:00
// TryRollbackMigration attempts to roll back the database to an earlier version
// if a rollback exists
TryRollbackMigration(version Migration)
2024-07-07 19:12:21 +02:00
GetOptions(params RetrieveQueryParams) (map[string]string, error)
2024-03-10 18:55:42 +01:00
GetOption(key string) (string, error)
SetOption(key string, value string) error
DeleteOption(key string) error
DeleteOptions(filter string) error
2024-03-10 18:55:42 +01:00
GetEnvironment(projectID int, environmentID int) (Environment, error)
2022-02-03 08:05:13 +01:00
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
2024-07-02 19:23:45 +02:00
GetEnvironmentSecrets(projectID int, environmentID int) ([]AccessKey, error)
2020-12-07 13:13:59 +01:00
GetInventory(projectID int, inventoryID int) (Inventory, error)
2022-02-03 08:05:13 +01:00
GetInventoryRefs(projectID int, inventoryID int) (ObjectReferrers, error)
2020-12-07 13:13:59 +01:00
GetInventories(projectID int, params RetrieveQueryParams) ([]Inventory, error)
UpdateInventory(inventory Inventory) error
CreateInventory(inventory Inventory) (Inventory, error)
2020-12-07 20:48:52 +01:00
DeleteInventory(projectID int, inventoryID int) error
GetRepository(projectID int, repositoryID int) (Repository, error)
2022-02-03 08:05:13 +01:00
GetRepositoryRefs(projectID int, repositoryID int) (ObjectReferrers, error)
2020-12-07 20:48:52 +01:00
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)
2022-02-03 08:05:13 +01:00
GetAccessKeyRefs(projectID int, accessKeyID int) (ObjectReferrers, error)
2020-12-08 08:23:33 +01:00
GetAccessKeys(projectID int, params RetrieveQueryParams) ([]AccessKey, error)
RekeyAccessKeys(oldKey string) error
2024-02-11 20:52:14 +01:00
CreateIntegration(integration Integration) (newIntegration Integration, err error)
GetIntegrations(projectID int, params RetrieveQueryParams) ([]Integration, error)
GetIntegration(projectID int, integrationID int) (integration Integration, err error)
UpdateIntegration(integration Integration) error
GetIntegrationRefs(projectID int, integrationID int) (IntegrationReferrers, error)
DeleteIntegration(projectID int, integrationID int) error
2024-03-04 12:24:47 +01:00
CreateIntegrationExtractValue(projectId int, value IntegrationExtractValue) (newValue IntegrationExtractValue, err error)
2024-03-06 14:28:24 +01:00
GetIntegrationExtractValues(projectID int, params RetrieveQueryParams, integrationID int) ([]IntegrationExtractValue, error)
GetIntegrationExtractValue(projectID int, valueID int, integrationID int) (value IntegrationExtractValue, err error)
2024-03-04 12:24:47 +01:00
UpdateIntegrationExtractValue(projectID int, integrationExtractValue IntegrationExtractValue) error
2024-03-06 14:28:24 +01:00
GetIntegrationExtractValueRefs(projectID int, valueID int, integrationID int) (IntegrationExtractorChildReferrers, error)
DeleteIntegrationExtractValue(projectID int, valueID int, integrationID int) error
2024-02-11 20:52:14 +01:00
2024-03-04 12:36:24 +01:00
CreateIntegrationMatcher(projectID int, matcher IntegrationMatcher) (newMatcher IntegrationMatcher, err error)
2024-03-06 14:28:24 +01:00
GetIntegrationMatchers(projectID int, params RetrieveQueryParams, integrationID int) ([]IntegrationMatcher, error)
GetIntegrationMatcher(projectID int, matcherID int, integrationID int) (matcher IntegrationMatcher, err error)
2024-03-04 12:36:24 +01:00
UpdateIntegrationMatcher(projectID int, integrationMatcher IntegrationMatcher) error
2024-03-06 14:28:24 +01:00
GetIntegrationMatcherRefs(projectID int, matcherID int, integrationID int) (IntegrationExtractorChildReferrers, error)
DeleteIntegrationMatcher(projectID int, matcherID int, integrationID int) error
2023-07-03 01:41:13 +02:00
2024-03-07 10:32:25 +01:00
CreateIntegrationAlias(alias IntegrationAlias) (IntegrationAlias, error)
GetIntegrationAliases(projectID int, integrationID *int) ([]IntegrationAlias, error)
GetIntegrationsByAlias(alias string) ([]Integration, error)
DeleteIntegrationAlias(projectID int, aliasID int) error
2024-03-23 13:37:15 +01:00
GetAllSearchableIntegrations() ([]Integration, error)
2024-03-07 10:32:25 +01:00
2020-12-08 08:23:33 +01:00
UpdateAccessKey(accessKey AccessKey) error
CreateAccessKey(accessKey AccessKey) (AccessKey, error)
DeleteAccessKey(projectID int, accessKeyID int) error
2024-04-25 13:28:28 +02:00
GetUserCount() (int, error)
GetUsers(params RetrieveQueryParams) ([]User, error)
2021-05-06 14:41:31 +02:00
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)
2020-12-17 15:00:05 +01:00
GetUserByLoginOrEmail(login string, email string) (User, error)
GetProject(projectID int) (Project, error)
2023-09-17 16:15:44 +02:00
GetAllProjects() ([]Project, error)
GetProjects(userID int) ([]Project, error)
CreateProject(project Project) (Project, error)
DeleteProject(projectID int) error
UpdateProject(project Project) error
2022-01-19 20:35:59 +01:00
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
2021-09-06 13:05:10 +02:00
GetSchedules() ([]Schedule, error)
2024-06-23 19:24:22 +02:00
GetProjectSchedules(projectID int) ([]ScheduleWithTpl, error)
2021-09-06 13:05:10 +02:00
GetTemplateSchedules(projectID int, templateID int) ([]Schedule, error)
CreateSchedule(schedule Schedule) (Schedule, error)
UpdateSchedule(schedule Schedule) error
2022-01-30 18:43:15 +01:00
SetScheduleCommitHash(projectID int, scheduleID int, hash string) error
2024-06-30 23:12:49 +02:00
SetScheduleActive(projectID int, scheduleID int, active bool) error
2021-09-06 13:05:10 +02:00
GetSchedule(projectID int, scheduleID int) (Schedule, error)
DeleteSchedule(projectID int, scheduleID int) error
2024-04-02 23:50:52 +02:00
GetAllAdmins() ([]User, error)
GetProjectUsers(projectID int, params RetrieveQueryParams) ([]UserWithProjectRole, error)
CreateProjectUser(projectUser ProjectUser) (ProjectUser, error)
2020-12-17 15:00:05 +01:00
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
2022-11-09 09:10:42 +01:00
DeleteAPIToken(userID int, tokenID string) error
GetSession(userID int, sessionID int) (Session, error)
2021-03-12 18:41:41 +01:00
CreateSession(session Session) (Session, error)
ExpireSession(userID int, sessionID int) error
TouchSession(userID int, sessionID int) error
2024-06-30 09:48:36 +02:00
CreateTask(task Task, maxTasks int) (Task, error)
2021-03-12 18:41:41 +01:00
UpdateTask(task Task) error
GetTemplateTasks(projectID int, templateID int, params RetrieveQueryParams) ([]TaskWithTpl, error)
2021-03-12 18:41:41 +01:00
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)
GetTaskStages(projectID int, taskID int) ([]TaskStage, error)
CreateTaskStage(stage TaskStage) (TaskStage, error)
2021-10-26 20:19:12 +02:00
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)
2024-11-17 19:13:11 +01:00
GetRunners(projectID int, activeOnly bool) ([]Runner, error)
DeleteRunner(projectID int, runnerID int) error
GetGlobalRunnerByToken(token string) (Runner, error)
GetGlobalRunner(runnerID int) (Runner, error)
GetGlobalRunners(activeOnly bool) ([]Runner, error)
DeleteGlobalRunner(runnerID int) error
UpdateRunner(runner Runner) error
CreateRunner(runner Runner) (Runner, error)
GetTemplateVaults(projectID int, templateID int) ([]TemplateVault, error)
2024-10-03 21:41:36 +02:00
CreateTemplateVault(vault TemplateVault) (TemplateVault, error)
UpdateTemplateVaults(projectID int, templateID int, vaults []TemplateVault) error
}
2020-12-07 20:48:52 +01:00
2022-02-03 08:05:13 +01:00
var AccessKeyProps = ObjectProps{
TableName: "access_key",
Type: reflect.TypeOf(AccessKey{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "key_id",
SortableColumns: []string{"name", "type"},
DefaultSortingColumn: "name",
}
2024-02-11 20:52:14 +01:00
var IntegrationProps = ObjectProps{
TableName: "project__integration",
Type: reflect.TypeOf(Integration{}),
2023-07-03 01:41:13 +02:00
PrimaryColumnName: "id",
ReferringColumnSuffix: "integration_id",
2023-07-03 01:41:13 +02:00
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
2024-02-11 20:52:14 +01:00
var IntegrationExtractValueProps = ObjectProps{
TableName: "project__integration_extract_value",
Type: reflect.TypeOf(IntegrationExtractValue{}),
PrimaryColumnName: "id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
2023-07-03 01:41:13 +02:00
}
2024-02-11 20:52:14 +01:00
var IntegrationMatcherProps = ObjectProps{
TableName: "project__integration_matcher",
Type: reflect.TypeOf(IntegrationMatcher{}),
PrimaryColumnName: "id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
2023-07-03 01:41:13 +02:00
}
2024-03-07 10:32:25 +01:00
var IntegrationAliasProps = ObjectProps{
TableName: "project__integration_alias",
Type: reflect.TypeOf(IntegrationAlias{}),
PrimaryColumnName: "id",
}
2022-02-03 08:05:13 +01:00
var EnvironmentProps = ObjectProps{
TableName: "project__environment",
Type: reflect.TypeOf(Environment{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "environment_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
2022-02-03 08:05:13 +01:00
var InventoryProps = ObjectProps{
TableName: "project__inventory",
Type: reflect.TypeOf(Inventory{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "inventory_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
2022-02-03 08:05:13 +01:00
var RepositoryProps = ObjectProps{
TableName: "project__repository",
Type: reflect.TypeOf(Repository{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "repository_id",
DefaultSortingColumn: "name",
}
2020-12-07 20:48:52 +01:00
2022-02-03 08:05:13 +01:00
var TemplateProps = ObjectProps{
TableName: "project__template",
Type: reflect.TypeOf(Template{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "template_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
}
2022-02-03 08:05:13 +01:00
var ScheduleProps = ObjectProps{
2021-09-06 13:05:10 +02:00
TableName: "project__schedule",
Type: reflect.TypeOf(Schedule{}),
2022-02-03 08:05:13 +01:00
PrimaryColumnName: "id",
2021-09-06 13:05:10 +02:00
}
2022-02-03 08:05:13 +01:00
var ProjectUserProps = ObjectProps{
2021-06-24 19:45:28 +02:00
TableName: "project__user",
Type: reflect.TypeOf(ProjectUser{}),
2022-02-03 08:05:13 +01:00
PrimaryColumnName: "user_id",
}
2022-02-03 08:05:13 +01:00
var ProjectProps = ObjectProps{
2024-02-11 20:52:14 +01:00
TableName: "project",
Type: reflect.TypeOf(Project{}),
PrimaryColumnName: "id",
2023-07-03 01:41:13 +02:00
ReferringColumnSuffix: "project_id",
2024-02-11 20:52:14 +01:00
DefaultSortingColumn: "name",
IsGlobal: true,
}
2022-02-03 08:05:13 +01:00
var UserProps = ObjectProps{
2021-06-24 19:45:28 +02:00
TableName: "user",
Type: reflect.TypeOf(User{}),
2022-02-03 08:05:13 +01:00
PrimaryColumnName: "id",
IsGlobal: true,
}
2022-02-03 08:05:13 +01:00
var SessionProps = ObjectProps{
2021-06-24 19:45:28 +02:00
TableName: "session",
Type: reflect.TypeOf(Session{}),
2022-02-03 08:05:13 +01:00
PrimaryColumnName: "id",
}
2022-02-03 08:05:13 +01:00
var TokenProps = ObjectProps{
2021-06-24 19:45:28 +02:00
TableName: "user__token",
Type: reflect.TypeOf(APIToken{}),
2022-02-03 08:05:13 +01:00
PrimaryColumnName: "id",
}
2022-02-03 08:05:13 +01:00
var TaskProps = ObjectProps{
2021-06-24 19:45:28 +02:00
TableName: "task",
2022-02-03 08:05:13 +01:00
Type: reflect.TypeOf(Task{}),
2021-05-16 23:44:42 +02:00
PrimaryColumnName: "id",
2022-02-03 08:05:13 +01:00
IsGlobal: true,
2021-06-24 21:53:36 +02:00
SortInverted: true,
}
2022-02-03 08:05:13 +01:00
var TaskOutputProps = ObjectProps{
TableName: "task__output",
Type: reflect.TypeOf(TaskOutput{}),
}
var TaskStageProps = ObjectProps{
TableName: "task__stage",
Type: reflect.TypeOf(TaskStage{}),
}
2022-02-03 08:05:13 +01:00
var ViewProps = ObjectProps{
2021-10-27 18:22:52 +02:00
TableName: "project__view",
Type: reflect.TypeOf(View{}),
2022-02-03 08:05:13 +01:00
PrimaryColumnName: "id",
2021-10-27 18:22:52 +02:00
DefaultSortingColumn: "position",
}
2022-02-03 08:05:13 +01:00
var RunnerProps = ObjectProps{
TableName: "runner",
Type: reflect.TypeOf(Runner{}),
PrimaryColumnName: "id",
}
var GlobalRunnerProps = ObjectProps{
TableName: "runner",
Type: reflect.TypeOf(Runner{}),
PrimaryColumnName: "id",
IsGlobal: true,
}
2024-03-10 18:55:42 +01:00
var OptionProps = ObjectProps{
TableName: "option",
Type: reflect.TypeOf(Option{}),
PrimaryColumnName: "key",
IsGlobal: true,
}
var TemplateVaultProps = ObjectProps{
TableName: "project__template_vault",
Type: reflect.TypeOf(TemplateVault{}),
PrimaryColumnName: "id",
ReferringColumnSuffix: "template_id",
}
2022-02-03 08:05:13 +01:00
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)
}
}
2024-03-18 15:33:40 +01:00
2024-03-18 15:37:40 +01:00
func ValidateRepository(store Store, repo *Repository) (err error) {
_, err = store.GetAccessKey(repo.ProjectID, repo.SSHKeyID)
return
}
2024-03-18 15:33:40 +01:00
func ValidateInventory(store Store, inventory *Inventory) (err error) {
if inventory.SSHKeyID != nil {
_, err = store.GetAccessKey(inventory.ProjectID, *inventory.SSHKeyID)
}
if err != nil {
return
}
if inventory.BecomeKeyID != nil {
_, err = store.GetAccessKey(inventory.ProjectID, *inventory.BecomeKeyID)
}
if err != nil {
return
}
if inventory.HolderID != nil {
_, err = store.GetTemplate(inventory.ProjectID, *inventory.HolderID)
}
return
}
2024-07-15 10:57:12 +02:00
type MapStringAnyField map[string]interface{}
func (m *MapStringAnyField) Scan(value interface{}) error {
if value == nil {
*m = nil
return nil
}
switch v := value.(type) {
case []byte:
return json.Unmarshal(v, m)
case string:
return json.Unmarshal([]byte(v), m)
default:
return errors.New("unsupported type for MapStringAnyField")
}
}
// Value implements the driver.Valuer interface for MapStringAnyField
func (m MapStringAnyField) Value() (driver.Value, error) {
if m == nil {
return nil, nil
}
return json.Marshal(m)
}