| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- package cmd
- import (
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "time"
- "github.com/porter-dev/porter/cli/cmd/docker"
- "k8s.io/client-go/util/homedir"
- "github.com/porter-dev/porter/cli/cmd/credstore"
- "github.com/spf13/cobra"
- "github.com/docker/docker/api/types/mount"
- )
- type startOps struct {
- insecure *bool
- skipKubeconfig *bool
- kubeconfigPath string
- contexts *[]string
- imageTag string `form:"required"`
- db string `form:"oneof=sqlite postgres"`
- }
- var opts = &startOps{}
- // startCmd represents the start command
- var startCmd = &cobra.Command{
- Args: func(cmd *cobra.Command, args []string) error {
- return nil
- },
- Use: "start",
- Short: "Starts a Porter instance using the Docker engine.",
- Run: func(cmd *cobra.Command, args []string) {
- closeHandler(stop)
- err := start(
- opts.imageTag,
- opts.kubeconfigPath,
- opts.db,
- *opts.contexts,
- *opts.insecure,
- *opts.skipKubeconfig,
- )
- if err != nil {
- fmt.Println("Error running start:", err.Error())
- fmt.Println("Shutting down...")
- err = stop()
- if err != nil {
- fmt.Println("Shutdown unsuccessful:", err.Error())
- }
- os.Exit(1)
- }
- },
- }
- func init() {
- rootCmd.AddCommand(startCmd)
- opts.insecure = startCmd.PersistentFlags().Bool(
- "insecure",
- false,
- "skip admin setup and authorization",
- )
- opts.skipKubeconfig = startCmd.PersistentFlags().Bool(
- "skip-kubeconfig",
- false,
- "skip initialization of the kubeconfig",
- )
- opts.contexts = startCmd.PersistentFlags().StringArray(
- "contexts",
- nil,
- "the list of contexts to use (defaults to the current context)",
- )
- startCmd.PersistentFlags().StringVar(
- &opts.db,
- "db",
- "sqlite",
- "the db to use, one of sqlite or postgres",
- )
- startCmd.PersistentFlags().StringVar(
- &opts.kubeconfigPath,
- "kubeconfig",
- "",
- "path to kubeconfig",
- )
- startCmd.PersistentFlags().StringVar(
- &opts.imageTag,
- "image-tag",
- "latest",
- "the Porter image tag to use",
- )
- }
- func stop() error {
- agent, err := docker.NewAgentFromEnv()
- if err != nil {
- return err
- }
- err = agent.StopPorterContainers()
- if err != nil {
- return err
- }
- return nil
- }
- func start(
- imageTag string,
- kubeconfigPath string,
- db string,
- contexts []string,
- insecure bool,
- skipKubeconfig bool,
- ) error {
- var username, pw string
- var err error
- home := homedir.HomeDir()
- outputConfPath := filepath.Join(home, ".porter", "porter.kubeconfig")
- containerConfPath := "/porter/porter.kubeconfig"
- port := 8080
- // if not insecure, or username/pw set incorrectly, prompt for new username/pw
- if username, pw, err = credstore.Get(); !insecure && err != nil {
- fmt.Println("Please register your admin account with an email and password:")
- username, err = promptPlaintext("Email: ")
- if err != nil {
- return err
- }
- pw, err = promptPasswordWithConfirmation()
- if err != nil {
- return err
- }
- credstore.Set(username, pw)
- }
- if !skipKubeconfig {
- err = generate(
- kubeconfigPath,
- outputConfPath,
- false,
- contexts,
- )
- if err != nil {
- return err
- }
- }
- agent, err := docker.NewAgentFromEnv()
- if err != nil {
- return err
- }
- // the volume mounts to use
- mounts := make([]mount.Mount, 0)
- // the volumes passed to the Porter container
- volumesMap := make(map[string]struct{})
- if !skipKubeconfig {
- // add a bind mount with the kubeconfig
- mount := mount.Mount{
- Type: mount.TypeBind,
- Source: outputConfPath,
- Target: containerConfPath,
- ReadOnly: true,
- Consistency: mount.ConsistencyFull,
- }
- mounts = append(mounts, mount)
- }
- netID, err := agent.CreateBridgeNetworkIfNotExist("porter_network")
- if err != nil {
- return err
- }
- env := make([]string, 0)
- env = append(env, []string{
- "ADMIN_INIT=true",
- "ADMIN_EMAIL=" + username,
- "ADMIN_PASSWORD=" + pw,
- }...)
- switch db {
- case "sqlite":
- // check if sqlite volume exists, create it if not
- vol, err := agent.CreateLocalVolumeIfNotExist("porter_sqlite")
- if err != nil {
- return err
- }
- // create mount
- mount := mount.Mount{
- Type: mount.TypeVolume,
- Source: vol.Name,
- Target: "/sqlite",
- ReadOnly: false,
- Consistency: mount.ConsistencyFull,
- }
- mounts = append(mounts, mount)
- volumesMap[vol.Name] = struct{}{}
- env = append(env, []string{
- "SQL_LITE=true",
- "SQL_LITE_PATH=/sqlite/porter.db",
- }...)
- case "postgres":
- // check if postgres volume exists, create it if not
- vol, err := agent.CreateLocalVolumeIfNotExist("porter_postgres")
- if err != nil {
- return err
- }
- // pgMount is mount for postgres container
- pgMount := []mount.Mount{
- mount.Mount{
- Type: mount.TypeVolume,
- Source: vol.Name,
- Target: "/var/lib/postgresql/data",
- ReadOnly: false,
- Consistency: mount.ConsistencyFull,
- },
- }
- // create postgres container with mount
- startOpts := docker.PostgresOpts{
- Name: "porter_postgres",
- Image: "postgres:latest",
- Mounts: pgMount,
- VolumeMap: map[string]struct{}{
- "porter_postgres": struct{}{},
- },
- NetworkID: netID,
- Env: []string{
- "POSTGRES_USER=porter",
- "POSTGRES_PASSWORD=porter",
- "POSTGRES_DB=porter",
- },
- }
- pgID, err := agent.StartPostgresContainer(startOpts)
- fmt.Println("Waiting for postgres:latest to be healthy...")
- agent.WaitForContainerHealthy(pgID, 10)
- if err != nil {
- return err
- }
- env = append(env, []string{
- "SQL_LITE=false",
- "DB_USER=porter",
- "DB_PASS=porter",
- "DB_NAME=porter",
- "DB_HOST=porter_postgres",
- "DB_PORT=5432",
- }...)
- defer agent.WaitForContainerStop(pgID)
- }
- // create Porter container
- // TODO -- look for unused port
- startOpts := docker.PorterStartOpts{
- Name: "porter_server",
- Image: "porter1/porter:" + imageTag,
- HostPort: uint(port),
- ContainerPort: 8080,
- Mounts: mounts,
- VolumeMap: volumesMap,
- NetworkID: netID,
- Env: env,
- }
- id, err := agent.StartPorterContainer(startOpts)
- if err != nil {
- return err
- }
- fmt.Println("Spinning up the server...")
- time.Sleep(7 * time.Second)
- openBrowser(fmt.Sprintf("http://localhost:%d/login?email=%s", port, username))
- fmt.Printf("Server ready: listening on localhost:%d\n", port)
- agent.WaitForContainerStop(id)
- return nil
- }
- // openBrowser opens the specified URL in the default browser of the user.
- func openBrowser(url string) error {
- var cmd string
- var args []string
- switch runtime.GOOS {
- case "windows":
- cmd = "cmd"
- args = []string{"/c", "start"}
- case "darwin":
- cmd = "open"
- default: // "linux", "freebsd", "openbsd", "netbsd"
- cmd = "xdg-open"
- }
- args = append(args, url)
- return exec.Command(cmd, args...).Start()
- }
|