2016-01-05 00:32:53 +01:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
2021-12-18 14:16:34 +01:00
|
|
|
"context"
|
2016-04-30 14:28:47 +02:00
|
|
|
"encoding/base64"
|
2016-01-05 00:32:53 +01:00
|
|
|
"encoding/json"
|
2020-11-28 22:49:44 +01:00
|
|
|
"errors"
|
2016-01-05 00:32:53 +01:00
|
|
|
"fmt"
|
2021-12-18 14:16:34 +01:00
|
|
|
"github.com/google/go-github/github"
|
2021-08-27 21:14:20 +02:00
|
|
|
"io"
|
2021-07-13 08:27:22 +02:00
|
|
|
"net/url"
|
2016-03-16 22:49:43 +01:00
|
|
|
"os"
|
2021-12-18 14:16:34 +01:00
|
|
|
"os/exec"
|
2016-04-24 20:11:43 +02:00
|
|
|
"path"
|
2021-12-18 14:16:34 +01:00
|
|
|
"path/filepath"
|
2019-07-09 19:49:17 +02:00
|
|
|
"strings"
|
|
|
|
|
2016-04-30 14:28:47 +02:00
|
|
|
"github.com/gorilla/securecookie"
|
2016-01-05 00:32:53 +01:00
|
|
|
)
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
// Cookie is a runtime generated secure cookie used for authentication
|
2016-04-30 14:28:47 +02:00
|
|
|
var Cookie *securecookie.SecureCookie
|
2019-07-09 19:49:17 +02:00
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
// WebHostURL is the public route to the semaphore server
|
2017-05-20 16:14:36 +02:00
|
|
|
var WebHostURL *url.URL
|
2016-01-05 00:32:53 +01:00
|
|
|
|
2021-08-28 14:04:56 +02:00
|
|
|
type DbDriver string
|
2020-11-28 22:49:44 +01:00
|
|
|
|
|
|
|
const (
|
2021-09-22 05:43:19 +02:00
|
|
|
DbDriverMySQL DbDriver = "mysql"
|
|
|
|
DbDriverBolt DbDriver = "bolt"
|
2021-08-28 14:04:56 +02:00
|
|
|
DbDriverPostgres DbDriver = "postgres"
|
2020-11-28 22:49:44 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type DbConfig struct {
|
2021-09-22 05:43:19 +02:00
|
|
|
Dialect DbDriver `json:"-"`
|
2021-08-28 14:04:56 +02:00
|
|
|
|
2021-10-14 21:14:21 +02:00
|
|
|
Hostname string `json:"host"`
|
|
|
|
Username string `json:"user"`
|
|
|
|
Password string `json:"pass"`
|
|
|
|
DbName string `json:"name"`
|
|
|
|
Options map[string]string `json:"options"`
|
2016-01-05 00:32:53 +01:00
|
|
|
}
|
|
|
|
|
2017-04-04 14:27:06 +02:00
|
|
|
type ldapMappings struct {
|
|
|
|
DN string `json:"dn"`
|
|
|
|
Mail string `json:"mail"`
|
2018-03-27 22:12:47 +02:00
|
|
|
UID string `json:"uid"`
|
2017-04-04 14:27:06 +02:00
|
|
|
CN string `json:"cn"`
|
|
|
|
}
|
|
|
|
|
2022-11-19 21:20:00 +01:00
|
|
|
// ConfigType mapping between Config and the json file that sets it
|
2019-07-09 19:49:17 +02:00
|
|
|
type ConfigType struct {
|
2021-09-22 05:43:19 +02:00
|
|
|
MySQL DbConfig `json:"mysql"`
|
|
|
|
BoltDb DbConfig `json:"bolt"`
|
2021-08-30 17:11:08 +02:00
|
|
|
Postgres DbConfig `json:"postgres"`
|
2020-11-28 22:49:44 +01:00
|
|
|
|
2021-08-28 14:04:56 +02:00
|
|
|
Dialect DbDriver `json:"dialect"`
|
|
|
|
|
2016-01-05 00:32:53 +01:00
|
|
|
// Format `:port_num` eg, :3000
|
2018-05-14 21:37:07 +02:00
|
|
|
// if : is missing it will be corrected
|
2017-05-20 16:14:36 +02:00
|
|
|
Port string `json:"port"`
|
2016-01-05 00:32:53 +01:00
|
|
|
|
2018-05-14 21:37:07 +02:00
|
|
|
// Interface ip, put in front of the port.
|
|
|
|
// defaults to empty
|
|
|
|
Interface string `json:"interface"`
|
|
|
|
|
2018-03-15 00:13:45 +01:00
|
|
|
// semaphore stores ephemeral projects here
|
2016-01-05 00:32:53 +01:00
|
|
|
TmpPath string `json:"tmp_path"`
|
2016-04-30 14:28:47 +02:00
|
|
|
|
|
|
|
// cookie hashing & encryption
|
2022-01-26 08:14:56 +01:00
|
|
|
CookieHash string `json:"cookie_hash"`
|
|
|
|
CookieEncryption string `json:"cookie_encryption"`
|
|
|
|
// AccessKeyEncryption is BASE64 encoded byte array used
|
|
|
|
// for encrypting and decrypting access keys stored in database.
|
|
|
|
// Do not use it! Use method GetAccessKeyEncryption instead of it.
|
2021-09-22 05:43:19 +02:00
|
|
|
AccessKeyEncryption string `json:"access_key_encryption"`
|
2017-02-22 09:46:42 +01:00
|
|
|
|
2017-04-18 16:36:09 +02:00
|
|
|
// email alerting
|
2021-09-22 05:43:19 +02:00
|
|
|
EmailSender string `json:"email_sender"`
|
|
|
|
EmailHost string `json:"email_host"`
|
|
|
|
EmailPort string `json:"email_port"`
|
|
|
|
EmailUsername string `json:"email_username"`
|
|
|
|
EmailPassword string `json:"email_password"`
|
2017-02-22 09:46:42 +01:00
|
|
|
|
2017-04-18 16:36:09 +02:00
|
|
|
// web host
|
2017-02-22 09:46:42 +01:00
|
|
|
WebHost string `json:"web_host"`
|
2017-03-27 06:53:00 +02:00
|
|
|
|
2017-04-18 16:36:09 +02:00
|
|
|
// ldap settings
|
2017-04-04 14:27:06 +02:00
|
|
|
LdapBindDN string `json:"ldap_binddn"`
|
|
|
|
LdapBindPassword string `json:"ldap_bindpassword"`
|
|
|
|
LdapServer string `json:"ldap_server"`
|
|
|
|
LdapSearchDN string `json:"ldap_searchdn"`
|
|
|
|
LdapSearchFilter string `json:"ldap_searchfilter"`
|
|
|
|
LdapMappings ldapMappings `json:"ldap_mappings"`
|
2017-04-04 13:49:00 +02:00
|
|
|
|
2017-04-18 16:36:09 +02:00
|
|
|
// telegram alerting
|
2017-03-22 08:22:09 +01:00
|
|
|
TelegramChat string `json:"telegram_chat"`
|
|
|
|
TelegramToken string `json:"telegram_token"`
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
2022-04-11 10:29:48 +02:00
|
|
|
// slack alerting
|
|
|
|
SlackUrl string `json:"slack_url"`
|
|
|
|
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
// task concurrency
|
2022-02-12 13:15:15 +01:00
|
|
|
MaxParallelTasks int `json:"max_parallel_tasks"`
|
2018-03-27 22:12:47 +02:00
|
|
|
|
|
|
|
// configType field ordering with bools at end reduces struct size
|
|
|
|
// (maligned check)
|
|
|
|
|
|
|
|
// feature switches
|
|
|
|
EmailAlert bool `json:"email_alert"`
|
2021-09-22 05:43:19 +02:00
|
|
|
EmailSecure bool `json:"email_secure"`
|
2018-03-27 22:12:47 +02:00
|
|
|
TelegramAlert bool `json:"telegram_alert"`
|
2022-04-11 10:29:48 +02:00
|
|
|
SlackAlert bool `json:"slack_alert"`
|
2018-03-27 22:12:47 +02:00
|
|
|
LdapEnable bool `json:"ldap_enable"`
|
|
|
|
LdapNeedTLS bool `json:"ldap_needtls"`
|
2021-09-12 00:18:26 +02:00
|
|
|
|
|
|
|
SshConfigPath string `json:"ssh_config_path"`
|
2021-10-14 21:14:21 +02:00
|
|
|
|
2023-04-25 20:04:03 +02:00
|
|
|
DemoMode bool `json:"demo_mode"`
|
|
|
|
|
|
|
|
GitClient string `json:"git_client"`
|
2016-01-05 00:32:53 +01:00
|
|
|
}
|
|
|
|
|
2022-11-19 21:20:00 +01:00
|
|
|
// Config exposes the application configuration storage for use in the application
|
2019-07-09 19:49:17 +02:00
|
|
|
var Config *ConfigType
|
2018-03-27 22:12:47 +02:00
|
|
|
|
2021-07-15 22:23:59 +02:00
|
|
|
// ToJSON returns a JSON string of the config
|
2021-08-25 22:12:19 +02:00
|
|
|
func (conf *ConfigType) ToJSON() ([]byte, error) {
|
|
|
|
return json.MarshalIndent(&conf, " ", "\t")
|
2020-12-03 14:44:30 +01:00
|
|
|
}
|
2016-01-05 00:32:53 +01:00
|
|
|
|
2022-01-26 08:14:56 +01:00
|
|
|
func (conf *ConfigType) GetAccessKeyEncryption() string {
|
|
|
|
ret := os.Getenv("SEMAPHORE_ACCESS_KEY_ENCRYPTION")
|
|
|
|
|
|
|
|
if ret == "" {
|
|
|
|
ret = conf.AccessKeyEncryption
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
// ConfigInit reads in cli flags, and switches actions appropriately on them
|
2021-08-25 22:12:19 +02:00
|
|
|
func ConfigInit(configPath string) {
|
2021-07-15 22:23:59 +02:00
|
|
|
loadConfig(configPath)
|
2018-03-20 01:28:59 +01:00
|
|
|
validateConfig()
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
2016-04-30 14:28:47 +02:00
|
|
|
var encryption []byte
|
|
|
|
|
|
|
|
hash, _ := base64.StdEncoding.DecodeString(Config.CookieHash)
|
|
|
|
if len(Config.CookieEncryption) > 0 {
|
|
|
|
encryption, _ = base64.StdEncoding.DecodeString(Config.CookieEncryption)
|
|
|
|
}
|
|
|
|
|
|
|
|
Cookie = securecookie.New(hash, encryption)
|
2017-05-20 16:14:36 +02:00
|
|
|
WebHostURL, _ = url.Parse(Config.WebHost)
|
2017-05-20 16:25:41 +02:00
|
|
|
if len(WebHostURL.String()) == 0 {
|
|
|
|
WebHostURL = nil
|
|
|
|
}
|
2016-01-05 00:32:53 +01:00
|
|
|
}
|
|
|
|
|
2021-08-25 22:12:19 +02:00
|
|
|
func loadConfig(configPath string) {
|
2021-08-27 21:14:20 +02:00
|
|
|
if configPath == "" {
|
|
|
|
configPath = os.Getenv("SEMAPHORE_CONFIG_PATH")
|
|
|
|
}
|
|
|
|
|
2021-07-15 22:23:59 +02:00
|
|
|
//If the configPath option has been set try to load and decode it
|
2021-08-28 13:44:41 +02:00
|
|
|
//var usedPath string
|
2021-08-27 21:14:20 +02:00
|
|
|
|
|
|
|
if configPath == "" {
|
2018-03-15 00:13:45 +01:00
|
|
|
cwd, err := os.Getwd()
|
2018-03-26 13:58:06 +02:00
|
|
|
exitOnConfigError(err)
|
2022-01-23 21:13:29 +01:00
|
|
|
paths := []string{
|
|
|
|
path.Join(cwd, "config.json"),
|
|
|
|
"/usr/local/etc/semaphore/config.json",
|
|
|
|
}
|
|
|
|
for _, p := range paths {
|
|
|
|
_, err = os.Stat(p)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var file *os.File
|
|
|
|
file, err = os.Open(p)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
decodeConfig(file)
|
|
|
|
break
|
|
|
|
}
|
2018-03-26 13:58:06 +02:00
|
|
|
exitOnConfigError(err)
|
2021-08-27 21:14:20 +02:00
|
|
|
} else {
|
2022-01-23 21:13:29 +01:00
|
|
|
p := configPath
|
|
|
|
file, err := os.Open(p)
|
2021-08-27 21:14:20 +02:00
|
|
|
exitOnConfigError(err)
|
|
|
|
decodeConfig(file)
|
2018-03-15 00:13:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
func validateConfig() {
|
2018-03-20 01:28:59 +01:00
|
|
|
|
|
|
|
validatePort()
|
|
|
|
|
|
|
|
if len(Config.TmpPath) == 0 {
|
|
|
|
Config.TmpPath = "/tmp/semaphore"
|
|
|
|
}
|
|
|
|
|
|
|
|
if Config.MaxParallelTasks < 1 {
|
|
|
|
Config.MaxParallelTasks = 10
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func validatePort() {
|
|
|
|
|
|
|
|
//TODO - why do we do this only with this variable?
|
|
|
|
if len(os.Getenv("PORT")) > 0 {
|
|
|
|
Config.Port = ":" + os.Getenv("PORT")
|
|
|
|
}
|
|
|
|
if len(Config.Port) == 0 {
|
|
|
|
Config.Port = ":3000"
|
|
|
|
}
|
2018-03-27 22:12:47 +02:00
|
|
|
if !strings.HasPrefix(Config.Port, ":") {
|
|
|
|
Config.Port = ":" + Config.Port
|
2018-03-20 01:28:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
func exitOnConfigError(err error) {
|
2018-03-26 13:58:06 +02:00
|
|
|
if err != nil {
|
2021-08-26 11:39:31 +02:00
|
|
|
fmt.Println("Cannot Find configuration! Use --config parameter to point to a JSON file generated by `semaphore setup`.")
|
2018-03-26 13:58:06 +02:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
func decodeConfig(file io.Reader) {
|
2018-03-15 00:13:45 +01:00
|
|
|
if err := json.NewDecoder(file).Decode(&Config); err != nil {
|
|
|
|
fmt.Println("Could not decode configuration!")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-22 14:18:40 +02:00
|
|
|
func mapToQueryString(m map[string]string) (str string) {
|
|
|
|
for option, value := range m {
|
|
|
|
if str != "" {
|
|
|
|
str += "&"
|
|
|
|
}
|
|
|
|
str += option + "=" + value
|
|
|
|
}
|
|
|
|
if str != "" {
|
|
|
|
str = "?" + str
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-18 14:16:34 +01:00
|
|
|
// FindSemaphore looks in the PATH for the semaphore variable
|
|
|
|
// if not found it will attempt to find the absolute path of the first
|
|
|
|
// os argument, the semaphore command, and return it
|
|
|
|
func FindSemaphore() string {
|
|
|
|
cmdPath, _ := exec.LookPath("semaphore") //nolint: gas
|
|
|
|
|
|
|
|
if len(cmdPath) == 0 {
|
|
|
|
cmdPath, _ = filepath.Abs(os.Args[0]) // nolint: gas
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmdPath
|
|
|
|
}
|
|
|
|
|
|
|
|
func AnsibleVersion() string {
|
|
|
|
bytes, err := exec.Command("ansible", "--version").Output()
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return string(bytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckUpdate uses the GitHub client to check for new tags in the semaphore repo
|
|
|
|
func CheckUpdate() (updateAvailable *github.RepositoryRelease, err error) {
|
|
|
|
// fetch releases
|
|
|
|
gh := github.NewClient(nil)
|
|
|
|
releases, _, err := gh.Repositories.ListReleases(context.TODO(), "ansible-semaphore", "semaphore", nil)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
updateAvailable = nil
|
|
|
|
if (*releases[0].TagName)[1:] != Version {
|
|
|
|
updateAvailable = releases[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-24 17:20:34 +02:00
|
|
|
// String returns dialect name for GORP.
|
2020-11-28 22:49:44 +01:00
|
|
|
func (d DbDriver) String() string {
|
2021-08-28 14:04:56 +02:00
|
|
|
return string(d)
|
2020-11-28 22:49:44 +01:00
|
|
|
}
|
|
|
|
|
2021-07-13 08:27:22 +02:00
|
|
|
func (d *DbConfig) IsPresent() bool {
|
2023-01-28 00:25:25 +01:00
|
|
|
return d.GetHostname() != ""
|
2020-11-28 22:49:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DbConfig) HasSupportMultipleDatabases() bool {
|
2020-12-04 09:39:56 +01:00
|
|
|
return true
|
2020-11-28 22:49:44 +01:00
|
|
|
}
|
|
|
|
|
2023-01-28 00:25:25 +01:00
|
|
|
func (d *DbConfig) GetDbName() string {
|
2023-01-27 19:54:46 +01:00
|
|
|
dbName := os.Getenv("SEMAPHORE_DB_NAME")
|
2023-01-28 00:25:25 +01:00
|
|
|
if dbName != "" {
|
|
|
|
return dbName
|
2023-01-27 19:54:46 +01:00
|
|
|
}
|
2023-01-28 00:25:25 +01:00
|
|
|
return d.DbName
|
|
|
|
}
|
2023-01-27 19:54:46 +01:00
|
|
|
|
2023-01-28 00:25:25 +01:00
|
|
|
func (d *DbConfig) GetUsername() string {
|
|
|
|
username := os.Getenv("SEMAPHORE_DB_USER")
|
|
|
|
if username != "" {
|
|
|
|
return username
|
2023-01-27 19:54:46 +01:00
|
|
|
}
|
2023-01-28 00:25:25 +01:00
|
|
|
return d.Username
|
|
|
|
}
|
2023-01-27 19:54:46 +01:00
|
|
|
|
2023-01-28 00:25:25 +01:00
|
|
|
func (d *DbConfig) GetPassword() string {
|
|
|
|
password := os.Getenv("SEMAPHORE_DB_PASS")
|
|
|
|
if password != "" {
|
|
|
|
return password
|
2023-01-27 19:54:46 +01:00
|
|
|
}
|
2023-01-28 00:25:25 +01:00
|
|
|
return d.Password
|
|
|
|
}
|
2023-01-27 19:54:46 +01:00
|
|
|
|
2023-01-28 00:25:25 +01:00
|
|
|
func (d *DbConfig) GetHostname() string {
|
|
|
|
hostname := os.Getenv("SEMAPHORE_DB_HOST")
|
|
|
|
if hostname != "" {
|
|
|
|
return hostname
|
2023-01-27 19:54:46 +01:00
|
|
|
}
|
2023-01-28 00:25:25 +01:00
|
|
|
return d.Hostname
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DbConfig) GetConnectionString(includeDbName bool) (connectionString string, err error) {
|
|
|
|
dbName := d.GetDbName()
|
|
|
|
dbUser := d.GetUsername()
|
|
|
|
dbPass := d.GetPassword()
|
|
|
|
dbHost := d.GetHostname()
|
2023-01-27 19:54:46 +01:00
|
|
|
|
2020-11-28 22:49:44 +01:00
|
|
|
switch d.Dialect {
|
2021-05-13 21:45:54 +02:00
|
|
|
case DbDriverBolt:
|
2023-01-27 19:54:46 +01:00
|
|
|
connectionString = dbHost
|
2020-11-28 22:49:44 +01:00
|
|
|
case DbDriverMySQL:
|
|
|
|
if includeDbName {
|
|
|
|
connectionString = fmt.Sprintf(
|
2021-09-22 14:18:40 +02:00
|
|
|
"%s:%s@tcp(%s)/%s",
|
2023-01-27 19:54:46 +01:00
|
|
|
dbUser,
|
|
|
|
dbPass,
|
|
|
|
dbHost,
|
|
|
|
dbName)
|
2020-11-28 22:49:44 +01:00
|
|
|
} else {
|
|
|
|
connectionString = fmt.Sprintf(
|
2021-09-22 14:18:40 +02:00
|
|
|
"%s:%s@tcp(%s)/",
|
2023-01-27 19:54:46 +01:00
|
|
|
dbUser,
|
|
|
|
dbPass,
|
|
|
|
dbHost)
|
2020-11-28 22:49:44 +01:00
|
|
|
}
|
2021-10-14 21:14:21 +02:00
|
|
|
options := map[string]string{
|
|
|
|
"parseTime": "true",
|
2021-09-22 14:18:40 +02:00
|
|
|
"interpolateParams": "true",
|
|
|
|
}
|
|
|
|
for v, k := range d.Options {
|
|
|
|
options[v] = k
|
|
|
|
}
|
|
|
|
connectionString += mapToQueryString(options)
|
2021-08-24 17:20:34 +02:00
|
|
|
case DbDriverPostgres:
|
|
|
|
if includeDbName {
|
|
|
|
connectionString = fmt.Sprintf(
|
|
|
|
"postgres://%s:%s@%s/%s",
|
2023-01-27 19:54:46 +01:00
|
|
|
dbUser,
|
|
|
|
url.QueryEscape(dbPass),
|
|
|
|
dbHost,
|
|
|
|
dbName)
|
2021-08-24 17:20:34 +02:00
|
|
|
} else {
|
|
|
|
connectionString = fmt.Sprintf(
|
2021-08-30 17:11:08 +02:00
|
|
|
"postgres://%s:%s@%s",
|
2023-01-27 19:54:46 +01:00
|
|
|
dbUser,
|
|
|
|
url.QueryEscape(dbPass),
|
|
|
|
dbHost)
|
2021-08-24 17:20:34 +02:00
|
|
|
}
|
2021-09-22 14:18:40 +02:00
|
|
|
connectionString += mapToQueryString(d.Options)
|
2020-11-28 22:49:44 +01:00
|
|
|
default:
|
|
|
|
err = fmt.Errorf("unsupported database driver: %s", d.Dialect)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-19 21:20:00 +01:00
|
|
|
func (conf *ConfigType) PrintDbInfo() {
|
|
|
|
dialect, err := conf.GetDialect()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
switch dialect {
|
|
|
|
case DbDriverMySQL:
|
2023-01-28 00:25:25 +01:00
|
|
|
fmt.Printf("MySQL %v@%v %v\n", conf.MySQL.GetUsername(), conf.MySQL.GetHostname(), conf.MySQL.GetDbName())
|
2022-11-19 21:20:00 +01:00
|
|
|
case DbDriverBolt:
|
2023-01-28 00:25:25 +01:00
|
|
|
fmt.Printf("BoltDB %v\n", conf.BoltDb.GetHostname())
|
2022-11-19 21:20:00 +01:00
|
|
|
case DbDriverPostgres:
|
2023-01-28 00:25:25 +01:00
|
|
|
fmt.Printf("Postgres %v@%v %v\n", conf.Postgres.GetUsername(), conf.Postgres.GetHostname(), conf.Postgres.GetDbName())
|
2022-11-19 21:20:00 +01:00
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("database configuration not found"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-28 14:04:56 +02:00
|
|
|
func (conf *ConfigType) GetDialect() (dialect DbDriver, err error) {
|
|
|
|
if conf.Dialect == "" {
|
|
|
|
switch {
|
|
|
|
case conf.MySQL.IsPresent():
|
|
|
|
dialect = DbDriverMySQL
|
|
|
|
case conf.BoltDb.IsPresent():
|
|
|
|
dialect = DbDriverBolt
|
|
|
|
case conf.Postgres.IsPresent():
|
|
|
|
dialect = DbDriverPostgres
|
|
|
|
default:
|
|
|
|
err = errors.New("database configuration not found")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dialect = conf.Dialect
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-28 22:49:44 +01:00
|
|
|
func (conf *ConfigType) GetDBConfig() (dbConfig DbConfig, err error) {
|
2021-08-28 14:04:56 +02:00
|
|
|
var dialect DbDriver
|
|
|
|
dialect, err = conf.GetDialect()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch dialect {
|
|
|
|
case DbDriverBolt:
|
2021-05-13 21:45:54 +02:00
|
|
|
dbConfig = conf.BoltDb
|
2021-08-28 14:04:56 +02:00
|
|
|
case DbDriverPostgres:
|
2021-08-24 17:20:34 +02:00
|
|
|
dbConfig = conf.Postgres
|
2021-08-28 14:04:56 +02:00
|
|
|
case DbDriverMySQL:
|
|
|
|
dbConfig = conf.MySQL
|
2020-11-28 22:49:44 +01:00
|
|
|
default:
|
|
|
|
err = errors.New("database configuration not found")
|
|
|
|
}
|
2021-08-28 18:24:54 +02:00
|
|
|
|
|
|
|
dbConfig.Dialect = dialect
|
|
|
|
|
2020-11-28 22:49:44 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-19 21:20:00 +01:00
|
|
|
// GenerateSecrets generates cookie secret during setup
|
2021-08-31 01:02:41 +02:00
|
|
|
func (conf *ConfigType) GenerateSecrets() {
|
2016-04-30 14:28:47 +02:00
|
|
|
hash := securecookie.GenerateRandomKey(32)
|
|
|
|
encryption := securecookie.GenerateRandomKey(32)
|
2021-08-31 01:02:41 +02:00
|
|
|
accessKeyEncryption := securecookie.GenerateRandomKey(32)
|
2016-01-05 00:32:53 +01:00
|
|
|
|
2016-04-30 14:28:47 +02:00
|
|
|
conf.CookieHash = base64.StdEncoding.EncodeToString(hash)
|
|
|
|
conf.CookieEncryption = base64.StdEncoding.EncodeToString(encryption)
|
2021-08-31 01:02:41 +02:00
|
|
|
conf.AccessKeyEncryption = base64.StdEncoding.EncodeToString(accessKeyEncryption)
|
2016-01-05 00:32:53 +01:00
|
|
|
}
|