Skip to content

Commit ab3a33e

Browse files
committed
Interactive setup
1 parent 3f0d318 commit ab3a33e

13 files changed

+152
-57
lines changed

database/mysql.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,20 @@ import (
1111
var Mysql *gorp.DbMap
1212

1313
// Mysql database
14-
func init() {
14+
func Connect() error {
1515
url := util.Config.MySQL.Username + ":" + util.Config.MySQL.Password + "@tcp(" + util.Config.MySQL.Hostname + ")/" + util.Config.MySQL.DbName + "?parseTime=true&interpolateParams=true"
1616

1717
db, err := sql.Open("mysql", url)
18-
1918
if err != nil {
20-
panic(err)
19+
return err
2120
}
2221

2322
err = db.Ping()
2423
if err != nil {
25-
panic(err)
24+
return err
2625
}
2726

2827
Mysql = &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}}
28+
29+
return nil
2930
}

main.go

+104-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"strings"
9+
10+
"golang.org/x/crypto/bcrypt"
511

612
"github.com/ansible-semaphore/semaphore/database"
713
"github.com/ansible-semaphore/semaphore/migration"
14+
"github.com/ansible-semaphore/semaphore/models"
815
"github.com/ansible-semaphore/semaphore/routes"
916
"github.com/ansible-semaphore/semaphore/routes/sockets"
1017
"github.com/ansible-semaphore/semaphore/routes/tasks"
@@ -14,12 +21,22 @@ import (
1421
)
1522

1623
func main() {
24+
if util.InteractiveSetup {
25+
os.Exit(doSetup())
26+
}
27+
1728
fmt.Printf("Semaphore %v\n", util.Version)
1829
fmt.Printf("Port %v\n", util.Config.Port)
19-
fmt.Printf("MySQL %v@%v\n", util.Config.MySQL.Username, util.Config.MySQL.Hostname)
30+
fmt.Printf("MySQL %v@%v %v\n", util.Config.MySQL.Username, util.Config.MySQL.Hostname, util.Config.MySQL.DbName)
2031
fmt.Printf("Redis %v\n", util.Config.SessionDb)
2132
fmt.Printf("Tmp Path (projects home) %v\n", util.Config.TmpPath)
2233

34+
if err := database.Connect(); err != nil {
35+
panic(err)
36+
}
37+
38+
models.SetupDBLink()
39+
2340
defer database.Mysql.Db.Close()
2441
database.RedisPing()
2542

@@ -46,3 +63,89 @@ func recovery(c *gin.Context) {
4663
defer bugsnag.AutoNotify()
4764
c.Next()
4865
}
66+
67+
func doSetup() int {
68+
fmt.Print(`
69+
Hello, you will now be guided through a setup to:
70+
71+
- Set up configuration for a MySQL/MariaDB database
72+
- Set up redis for session storage
73+
- Set up a path for your playbooks
74+
- Run DB Migrations
75+
- Set up your user and password
76+
77+
`)
78+
79+
var b []byte
80+
setup := util.ScanSetup()
81+
for true {
82+
var err error
83+
b, err = json.MarshalIndent(&setup, "", "\t")
84+
if err != nil {
85+
panic(err)
86+
}
87+
88+
fmt.Printf("Config:\n%v\n\n", string(b))
89+
fmt.Print("Is this correct? (yes/no): ")
90+
91+
var answer string
92+
fmt.Scanln(&answer)
93+
94+
if !(answer == "yes" || answer == "y") {
95+
fmt.Println()
96+
setup = util.ScanSetup()
97+
98+
continue
99+
}
100+
101+
break
102+
}
103+
104+
fmt.Print("Configuration written to /tmp/semaphore_config.json\n")
105+
if err := ioutil.WriteFile("/tmp/semaphore_config.json", b, 0644); err != nil {
106+
panic(err)
107+
}
108+
109+
fmt.Println("\nPinging database...")
110+
util.Config = setup
111+
112+
if err := database.Connect(); err != nil {
113+
fmt.Println("Connection to database unsuccessful.")
114+
panic(err)
115+
}
116+
117+
fmt.Println("Pinging redis...")
118+
database.RedisPing()
119+
120+
fmt.Println("\nRunning DB Migrations")
121+
if err := migration.MigrateAll(); err != nil {
122+
panic(err)
123+
}
124+
125+
var user models.User
126+
fmt.Print("\n\nYour name: ")
127+
fmt.Scanln(&user.Name)
128+
129+
fmt.Print("Username: ")
130+
fmt.Scanln(&user.Username)
131+
132+
fmt.Print("Email: ")
133+
fmt.Scanln(&user.Email)
134+
135+
fmt.Print("Password: ")
136+
fmt.Scanln(&user.Password)
137+
138+
pwdHash, _ := bcrypt.GenerateFromPassword([]byte(user.Password), 11)
139+
user.Username = strings.ToLower(user.Username)
140+
user.Email = strings.ToLower(user.Email)
141+
142+
if _, err := database.Mysql.Exec("insert into user set name=?, username=?, email=?, password=?, created=NOW()", user.Name, user.Username, user.Email, pwdHash); err != nil {
143+
panic(err)
144+
}
145+
146+
fmt.Printf("\nYou are all setup %v\n", user.Name)
147+
fmt.Println("Re-launch this program pointing to the configuration file\n./semaphore -config /tmp/semaphore_config.json")
148+
fmt.Println("Your login is %v or %v.", user.Email, user.Username)
149+
150+
return 0
151+
}

models/APIToken.go

+1-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
package models
22

3-
import (
4-
"github.com/ansible-semaphore/semaphore/database"
5-
6-
"time"
7-
)
3+
import "time"
84

95
type APIToken struct {
106
ID string `db:"id" json:"id"`
117
Created time.Time `db:"created" json:"created"`
128
Expired bool `db:"expired" json:"expired"`
139
UserID int `db:"user_id" json:"user_id"`
1410
}
15-
16-
func init() {
17-
database.Mysql.AddTableWithName(APIToken{}, "user__token").SetKeys(false, "id")
18-
}

models/Environment.go

-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
package models
22

3-
import "github.com/ansible-semaphore/semaphore/database"
4-
53
type Environment struct {
64
ID int `db:"id" json:"id"`
75
Name string `db:"name" json:"name" binding:"required"`
86
ProjectID int `db:"project_id" json:"project_id"`
97
Password *string `db:"password" json:"password"`
108
JSON string `db:"json" json:"json" binding:"required"`
119
}
12-
13-
func init() {
14-
database.Mysql.AddTableWithName(Environment{}, "project__environment").SetKeys(true, "id")
15-
}

models/Inventory.go

-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package models
22

3-
import "github.com/ansible-semaphore/semaphore/database"
4-
53
type Inventory struct {
64
ID int `db:"id" json:"id"`
75
Name string `db:"name" json:"name" binding:"required"`
@@ -18,7 +16,3 @@ type Inventory struct {
1816
// static/aws/do/gcloud
1917
Type string `db:"type" json:"type"`
2018
}
21-
22-
func init() {
23-
database.Mysql.AddTableWithName(Inventory{}, "project__inventory").SetKeys(true, "id")
24-
}

models/Project.go

-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,3 @@ func (project *Project) CreateProject() error {
2828

2929
return nil
3030
}
31-
32-
func init() {
33-
database.Mysql.AddTableWithName(Project{}, "project").SetKeys(true, "id")
34-
}

models/Repository.go

-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package models
22

3-
import "github.com/ansible-semaphore/semaphore/database"
4-
53
type Repository struct {
64
ID int `db:"id" json:"id"`
75
Name string `db:"name" json:"name" binding:"required"`
@@ -11,7 +9,3 @@ type Repository struct {
119

1210
SshKey AccessKey `db:"-" json:"-"`
1311
}
14-
15-
func init() {
16-
database.Mysql.AddTableWithName(Repository{}, "project__repository").SetKeys(true, "id")
17-
}

models/Task.go

-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package models
22

33
import "time"
4-
import "github.com/ansible-semaphore/semaphore/database"
54

65
type Task struct {
76
ID int `db:"id" json:"id"`
@@ -25,8 +24,3 @@ type TaskOutput struct {
2524
Time time.Time `db:"time" json:"time"`
2625
Output string `db:"output" json:"output"`
2726
}
28-
29-
func init() {
30-
database.Mysql.AddTableWithName(Task{}, "task").SetKeys(true, "id")
31-
database.Mysql.AddTableWithName(TaskOutput{}, "task__output").SetUniqueTogether("task_id", "time")
32-
}

models/Template.go

-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package models
22

3-
import "github.com/ansible-semaphore/semaphore/database"
4-
53
type Template struct {
64
ID int `db:"id" json:"id"`
75

@@ -23,7 +21,3 @@ type TemplateSchedule struct {
2321
TemplateID int `db:"template_id" json:"template_id"`
2422
CronFormat string `db:"cron_format" json:"cron_format"`
2523
}
26-
27-
func init() {
28-
database.Mysql.AddTableWithName(Template{}, "project__template").SetKeys(true, "id")
29-
}

models/User.go

-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,3 @@ func FetchUser(userID int) (*User, error) {
2121
err := database.Mysql.SelectOne(&user, "select * from user where id=?", userID)
2222
return &user, err
2323
}
24-
25-
func init() {
26-
database.Mysql.AddTableWithName(User{}, "user").SetKeys(true, "id")
27-
}

models/accessKey.go

-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package models
33
import (
44
"strconv"
55

6-
"github.com/ansible-semaphore/semaphore/database"
76
"github.com/ansible-semaphore/semaphore/util"
87
)
98

@@ -18,10 +17,6 @@ type AccessKey struct {
1817
Secret *string `db:"secret" json:"secret"`
1918
}
2019

21-
func init() {
22-
database.Mysql.AddTableWithName(AccessKey{}, "access_key").SetKeys(true, "id")
23-
}
24-
2520
func (key AccessKey) GetPath() string {
2621
return util.Config.TmpPath + "/access_key_" + strconv.Itoa(key.ID)
2722
}

models/models.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package models
2+
3+
import "github.com/ansible-semaphore/semaphore/database"
4+
5+
func SetupDBLink() {
6+
database.Mysql.AddTableWithName(APIToken{}, "user__token").SetKeys(false, "id")
7+
database.Mysql.AddTableWithName(AccessKey{}, "access_key").SetKeys(true, "id")
8+
database.Mysql.AddTableWithName(Environment{}, "project__environment").SetKeys(true, "id")
9+
database.Mysql.AddTableWithName(Inventory{}, "project__inventory").SetKeys(true, "id")
10+
database.Mysql.AddTableWithName(Project{}, "project").SetKeys(true, "id")
11+
database.Mysql.AddTableWithName(Repository{}, "project__repository").SetKeys(true, "id")
12+
database.Mysql.AddTableWithName(Task{}, "task").SetKeys(true, "id")
13+
database.Mysql.AddTableWithName(TaskOutput{}, "task__output").SetUniqueTogether("task_id", "time")
14+
database.Mysql.AddTableWithName(Template{}, "project__template").SetKeys(true, "id")
15+
database.Mysql.AddTableWithName(User{}, "user").SetKeys(true, "id")
16+
}

util/config.go

+26
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
var mandrillAPI *gochimp.MandrillAPI
1717
var Migration bool
18+
var InteractiveSetup bool
1819

1920
type mySQLConfig struct {
2021
Hostname string `json:"host"`
@@ -44,6 +45,7 @@ type configType struct {
4445
var Config configType
4546

4647
func init() {
48+
flag.BoolVar(&InteractiveSetup, "setup", false, "perform interactive setup")
4749
flag.BoolVar(&Migration, "migrate", false, "execute migrations")
4850
path := flag.String("config", "", "config path")
4951

@@ -156,3 +158,27 @@ func MandrillRecipient(name string, email string) gochimp.Recipient {
156158
func MandrillSend(message gochimp.Message) ([]gochimp.SendResponse, error) {
157159
return mandrillAPI.MessageSend(message, false)
158160
}
161+
162+
func ScanSetup() configType {
163+
var conf configType
164+
165+
fmt.Print("DB Hostname (example 127.0.0.1:3306): ")
166+
fmt.Scanln(&conf.MySQL.Hostname)
167+
168+
fmt.Print("DB User (example root): ")
169+
fmt.Scanln(&conf.MySQL.Username)
170+
171+
fmt.Print("DB Password: ")
172+
fmt.Scanln(&conf.MySQL.Password)
173+
174+
fmt.Print("DB Name: ")
175+
fmt.Scanln(&conf.MySQL.DbName)
176+
177+
fmt.Print("Redis Connection (example 127.0.0.1:6379): ")
178+
fmt.Scanln(&conf.SessionDb)
179+
180+
fmt.Print("Playbook path (will be auto-created if does not exist): ")
181+
fmt.Scanln(&conf.TmpPath)
182+
183+
return conf
184+
}

0 commit comments

Comments
 (0)