start.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. package cmd
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "github.com/porter-dev/porter/cli/cmd/docker"
  7. "k8s.io/client-go/util/homedir"
  8. "github.com/porter-dev/porter/cli/cmd/credstore"
  9. "github.com/spf13/cobra"
  10. "github.com/docker/docker/api/types/mount"
  11. )
  12. type startOps struct {
  13. insecure *bool
  14. skipKubeconfig *bool
  15. kubeconfigPath string
  16. contexts *[]string
  17. imageTag string `form:"required"`
  18. db string `form:"oneof=sqlite postgres"`
  19. }
  20. var opts = &startOps{}
  21. // startCmd represents the start command
  22. var startCmd = &cobra.Command{
  23. Args: func(cmd *cobra.Command, args []string) error {
  24. return nil
  25. },
  26. Use: "start",
  27. Short: "Starts a Porter instance using the Docker engine.",
  28. Run: func(cmd *cobra.Command, args []string) {
  29. closeHandler(stop)
  30. err := start(
  31. opts.imageTag,
  32. opts.kubeconfigPath,
  33. opts.db,
  34. *opts.contexts,
  35. *opts.insecure,
  36. *opts.skipKubeconfig,
  37. )
  38. if err != nil {
  39. fmt.Println("Error running start:", err.Error())
  40. fmt.Println("Shutting down...")
  41. err = stop()
  42. if err != nil {
  43. fmt.Println("Shutdown unsuccessful:", err.Error())
  44. }
  45. os.Exit(1)
  46. }
  47. },
  48. }
  49. func init() {
  50. rootCmd.AddCommand(startCmd)
  51. opts.insecure = startCmd.PersistentFlags().Bool(
  52. "insecure",
  53. false,
  54. "skip admin setup and authorization",
  55. )
  56. opts.skipKubeconfig = startCmd.PersistentFlags().Bool(
  57. "skip-kubeconfig",
  58. false,
  59. "skip initialization of the kubeconfig",
  60. )
  61. opts.contexts = startCmd.PersistentFlags().StringArray(
  62. "contexts",
  63. nil,
  64. "the list of contexts to use (defaults to the current context)",
  65. )
  66. startCmd.PersistentFlags().StringVar(
  67. &opts.db,
  68. "db",
  69. "sqlite",
  70. "the db to use, one of sqlite or postgres",
  71. )
  72. startCmd.PersistentFlags().StringVar(
  73. &opts.kubeconfigPath,
  74. "kubeconfig",
  75. "",
  76. "path to kubeconfig",
  77. )
  78. startCmd.PersistentFlags().StringVar(
  79. &opts.imageTag,
  80. "image-tag",
  81. "latest",
  82. "the Porter image tag to use",
  83. )
  84. }
  85. func stop() error {
  86. agent, err := docker.NewAgentFromEnv()
  87. if err != nil {
  88. return err
  89. }
  90. err = agent.StopPorterContainers()
  91. if err != nil {
  92. return err
  93. }
  94. return nil
  95. }
  96. func start(
  97. imageTag string,
  98. kubeconfigPath string,
  99. db string,
  100. contexts []string,
  101. insecure bool,
  102. skipKubeconfig bool,
  103. ) error {
  104. var username, pw string
  105. var err error
  106. home := homedir.HomeDir()
  107. outputConfPath := filepath.Join(home, ".porter", "porter.kubeconfig")
  108. containerConfPath := "/porter/porter.kubeconfig"
  109. port := 8080
  110. // if not insecure, or username/pw set incorrectly, prompt for new username/pw
  111. if username, pw, err = credstore.Get(); !insecure && err != nil {
  112. fmt.Println("Please register your admin account with an email and password:")
  113. username, err = promptPlaintext("Email: ")
  114. if err != nil {
  115. return err
  116. }
  117. pw, err = promptPasswordWithConfirmation()
  118. if err != nil {
  119. return err
  120. }
  121. credstore.Set(username, pw)
  122. }
  123. if !skipKubeconfig {
  124. err = generate(
  125. kubeconfigPath,
  126. outputConfPath,
  127. false,
  128. contexts,
  129. )
  130. if err != nil {
  131. return err
  132. }
  133. }
  134. agent, err := docker.NewAgentFromEnv()
  135. if err != nil {
  136. return err
  137. }
  138. // the volume mounts to use
  139. mounts := make([]mount.Mount, 0)
  140. // the volumes passed to the Porter container
  141. volumesMap := make(map[string]struct{})
  142. if !skipKubeconfig {
  143. // add a bind mount with the kubeconfig
  144. mount := mount.Mount{
  145. Type: mount.TypeBind,
  146. Source: outputConfPath,
  147. Target: containerConfPath,
  148. ReadOnly: true,
  149. Consistency: mount.ConsistencyFull,
  150. }
  151. mounts = append(mounts, mount)
  152. }
  153. netID, err := agent.CreateBridgeNetworkIfNotExist("porter_network")
  154. if err != nil {
  155. return err
  156. }
  157. env := make([]string, 0)
  158. env = append(env, []string{
  159. "ADMIN_INIT=true",
  160. "ADMIN_EMAIL=" + username,
  161. "ADMIN_PASSWORD=" + pw,
  162. }...)
  163. switch db {
  164. case "sqlite":
  165. // check if sqlite volume exists, create it if not
  166. vol, err := agent.CreateLocalVolumeIfNotExist("porter_sqlite")
  167. if err != nil {
  168. return err
  169. }
  170. // create mount
  171. mount := mount.Mount{
  172. Type: mount.TypeVolume,
  173. Source: vol.Name,
  174. Target: "/sqlite",
  175. ReadOnly: false,
  176. Consistency: mount.ConsistencyFull,
  177. }
  178. mounts = append(mounts, mount)
  179. volumesMap[vol.Name] = struct{}{}
  180. env = append(env, []string{
  181. "SQL_LITE=true",
  182. "SQL_LITE_PATH=/sqlite/porter.db",
  183. }...)
  184. case "postgres":
  185. // check if postgres volume exists, create it if not
  186. vol, err := agent.CreateLocalVolumeIfNotExist("porter_postgres")
  187. if err != nil {
  188. return err
  189. }
  190. // pgMount is mount for postgres container
  191. pgMount := []mount.Mount{
  192. mount.Mount{
  193. Type: mount.TypeVolume,
  194. Source: vol.Name,
  195. Target: "/var/lib/postgresql/data",
  196. ReadOnly: false,
  197. Consistency: mount.ConsistencyFull,
  198. },
  199. }
  200. // create postgres container with mount
  201. startOpts := docker.PostgresOpts{
  202. Name: "porter_postgres",
  203. Image: "postgres:latest",
  204. Mounts: pgMount,
  205. VolumeMap: map[string]struct{}{
  206. "porter_postgres": struct{}{},
  207. },
  208. NetworkID: netID,
  209. Env: []string{
  210. "POSTGRES_USER=porter",
  211. "POSTGRES_PASSWORD=porter",
  212. "POSTGRES_DB=porter",
  213. },
  214. }
  215. pgID, err := agent.StartPostgresContainer(startOpts)
  216. if err != nil {
  217. return err
  218. }
  219. env = append(env, []string{
  220. "SQL_LITE=false",
  221. "DB_USER=porter",
  222. "DB_PASS=porter",
  223. "DB_NAME=porter",
  224. "DB_HOST=porter_postgres",
  225. "DB_PORT=5432",
  226. }...)
  227. defer agent.WaitForContainerStop(pgID)
  228. }
  229. // create Porter container
  230. // TODO -- look for unused port
  231. startOpts := docker.PorterStartOpts{
  232. Name: "porter_server",
  233. Image: "porter1/porter:" + imageTag,
  234. HostPort: uint(port),
  235. ContainerPort: 8080,
  236. Mounts: mounts,
  237. VolumeMap: volumesMap,
  238. NetworkID: netID,
  239. Env: env,
  240. }
  241. id, err := agent.StartPorterContainer(startOpts)
  242. if err != nil {
  243. return err
  244. }
  245. if !insecure {
  246. fmt.Printf("Server ready: go to localhost:%d/login and log in with the admin account you just created.\n", port)
  247. } else {
  248. fmt.Printf("Server ready: go to localhost:%d/login\n", port)
  249. }
  250. agent.WaitForContainerStop(id)
  251. return nil
  252. }