start.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. username, err = promptPlaintext("Email: ")
  113. if err != nil {
  114. return err
  115. }
  116. pw, err = promptPasswordWithConfirmation()
  117. if err != nil {
  118. return err
  119. }
  120. credstore.Set(username, pw)
  121. }
  122. if !skipKubeconfig {
  123. err = generate(
  124. kubeconfigPath,
  125. outputConfPath,
  126. false,
  127. contexts,
  128. )
  129. if err != nil {
  130. return err
  131. }
  132. }
  133. agent, err := docker.NewAgentFromEnv()
  134. if err != nil {
  135. return err
  136. }
  137. // the volume mounts to use
  138. mounts := make([]mount.Mount, 0)
  139. // the volumes passed to the Porter container
  140. volumesMap := make(map[string]struct{})
  141. if !skipKubeconfig {
  142. // add a bind mount with the kubeconfig
  143. mount := mount.Mount{
  144. Type: mount.TypeBind,
  145. Source: outputConfPath,
  146. Target: containerConfPath,
  147. ReadOnly: true,
  148. Consistency: mount.ConsistencyFull,
  149. }
  150. mounts = append(mounts, mount)
  151. }
  152. netID, err := agent.CreateBridgeNetworkIfNotExist("porter_network")
  153. if err != nil {
  154. return err
  155. }
  156. env := make([]string, 0)
  157. env = append(env, []string{
  158. "ADMIN_INIT=true",
  159. "ADMIN_EMAIL=" + username,
  160. "ADMIN_PASSWORD=" + pw,
  161. }...)
  162. switch db {
  163. case "sqlite":
  164. // check if sqlite volume exists, create it if not
  165. vol, err := agent.CreateLocalVolumeIfNotExist("porter_sqlite")
  166. if err != nil {
  167. return err
  168. }
  169. // create mount
  170. mount := mount.Mount{
  171. Type: mount.TypeVolume,
  172. Source: vol.Name,
  173. Target: "/sqlite",
  174. ReadOnly: false,
  175. Consistency: mount.ConsistencyFull,
  176. }
  177. mounts = append(mounts, mount)
  178. volumesMap[vol.Name] = struct{}{}
  179. env = append(env, []string{
  180. "SQL_LITE=true",
  181. "SQL_LITE_PATH=/sqlite/porter.db",
  182. }...)
  183. case "postgres":
  184. // check if postgres volume exists, create it if not
  185. vol, err := agent.CreateLocalVolumeIfNotExist("porter_postgres")
  186. if err != nil {
  187. return err
  188. }
  189. // pgMount is mount for postgres container
  190. pgMount := []mount.Mount{
  191. mount.Mount{
  192. Type: mount.TypeVolume,
  193. Source: vol.Name,
  194. Target: "/var/lib/postgresql/data",
  195. ReadOnly: false,
  196. Consistency: mount.ConsistencyFull,
  197. },
  198. }
  199. // create postgres container with mount
  200. startOpts := docker.PostgresOpts{
  201. Name: "porter_postgres",
  202. Image: "postgres:latest",
  203. Mounts: pgMount,
  204. VolumeMap: map[string]struct{}{
  205. "porter_postgres": struct{}{},
  206. },
  207. NetworkID: netID,
  208. Env: []string{
  209. "POSTGRES_USER=porter",
  210. "POSTGRES_PASSWORD=porter",
  211. "POSTGRES_DB=porter",
  212. },
  213. }
  214. pgID, err := agent.StartPostgresContainer(startOpts)
  215. if err != nil {
  216. return err
  217. }
  218. env = append(env, []string{
  219. "SQL_LITE=false",
  220. "DB_USER=porter",
  221. "DB_PASS=porter",
  222. "DB_NAME=porter",
  223. "DB_HOST=porter_postgres",
  224. "DB_PORT=5432",
  225. }...)
  226. defer agent.WaitForContainerStop(pgID)
  227. }
  228. // create Porter container
  229. // TODO -- look for unused port
  230. startOpts := docker.PorterStartOpts{
  231. Name: "porter_server",
  232. Image: "porter1/porter:" + imageTag,
  233. HostPort: uint(port),
  234. ContainerPort: 8080,
  235. Mounts: mounts,
  236. VolumeMap: volumesMap,
  237. NetworkID: netID,
  238. Env: env,
  239. }
  240. id, err := agent.StartPorterContainer(startOpts)
  241. if err != nil {
  242. return err
  243. }
  244. fmt.Printf("Server ready: listening on localhost:%d\n", port)
  245. agent.WaitForContainerStop(id)
  246. return nil
  247. }