start.go 5.8 KB

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