bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/cmd/synsec-cli/dashboard.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"os/user"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/AlecAivazis/survey/v2"
    13  	"bitbucket.org/Aishee/synsec/pkg/metabase"
    14  
    15  	log "github.com/sirupsen/logrus"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  var (
    20  	metabaseUser         = "synsec@synsec.net"
    21  	metabasePassword     string
    22  	metabaseDbPath       string
    23  	metabaseConfigPath   string
    24  	metabaseConfigFolder = "metabase/"
    25  	metabaseConfigFile   = "metabase.yaml"
    26  	metabaseImage        = "metabase/metabase"
    27  	/**/
    28  	metabaseListenAddress = "127.0.0.1"
    29  	metabaseListenPort    = "3000"
    30  	metabaseContainerID   = "/synsec-metabase"
    31  	synsecGroup         = "synsec"
    32  
    33  	forceYes bool
    34  
    35  	dockerGatewayIPAddr = "172.17.0.1"
    36  	/*informations needed to setup a random password on user's behalf*/
    37  )
    38  
    39  func NewDashboardCmd() *cobra.Command {
    40  	/* ---- UPDATE COMMAND */
    41  	var cmdDashboard = &cobra.Command{
    42  		Use:   "dashboard [command]",
    43  		Short: "Manage your metabase dashboard container",
    44  		Long:  `Install/Start/Stop/Remove a metabase container exposing dashboard and metrics.`,
    45  		Args:  cobra.ExactArgs(1),
    46  		Example: `
    47  ccscli dashboard setup
    48  ccscli dashboard start
    49  ccscli dashboard stop
    50  ccscli dashboard remove
    51  `,
    52  		PersistentPreRun: func(cmd *cobra.Command, args []string) {
    53  			metabaseConfigFolderPath := filepath.Join(csConfig.ConfigPaths.ConfigDir, metabaseConfigFolder)
    54  			metabaseConfigPath = filepath.Join(metabaseConfigFolderPath, metabaseConfigFile)
    55  			if err := os.MkdirAll(metabaseConfigFolderPath, os.ModePerm); err != nil {
    56  				log.Fatalf(err.Error())
    57  			}
    58  			if err := csConfig.LoadDBConfig(); err != nil {
    59  				log.Fatalf(err.Error())
    60  			}
    61  
    62  		},
    63  	}
    64  
    65  	var force bool
    66  	var cmdDashSetup = &cobra.Command{
    67  		Use:   "setup",
    68  		Short: "Setup a metabase container.",
    69  		Long:  `Perform a metabase docker setup, download standard dashboards, create a fresh user and start the container`,
    70  		Args:  cobra.ExactArgs(0),
    71  		Example: `
    72  ccscli dashboard setup
    73  ccscli dashboard setup --listen 0.0.0.0
    74  ccscli dashboard setup -l 0.0.0.0 -p 443 --password <password>
    75   `,
    76  		Run: func(cmd *cobra.Command, args []string) {
    77  			if metabaseDbPath == "" {
    78  				metabaseDbPath = csConfig.ConfigPaths.DataDir
    79  			}
    80  
    81  			if metabasePassword == "" {
    82  				metabasePassword = generatePassword(16)
    83  			}
    84  			var answer bool
    85  			groupExist := false
    86  			dockerGroup, err := user.LookupGroup(synsecGroup)
    87  			if err == nil {
    88  				groupExist = true
    89  			}
    90  			if !forceYes && !groupExist {
    91  				prompt := &survey.Confirm{
    92  					Message: fmt.Sprintf("For metabase docker to be able to access SQLite file we need to add a new group called '%s' to the system, is it ok for you ?", synsecGroup),
    93  					Default: true,
    94  				}
    95  				if err := survey.AskOne(prompt, &answer); err != nil {
    96  					log.Fatalf("unable to ask to force: %s", err)
    97  				}
    98  			}
    99  			if !answer && !forceYes && !groupExist {
   100  				log.Fatalf("unable to continue without creating '%s' group", synsecGroup)
   101  			}
   102  			if !groupExist {
   103  				groupAddCmd, err := exec.LookPath("groupadd")
   104  				if err != nil {
   105  					log.Fatalf("unable to find 'groupadd' command, can't continue")
   106  				}
   107  
   108  				groupAdd := &exec.Cmd{Path: groupAddCmd, Args: []string{groupAddCmd, synsecGroup}}
   109  				if err := groupAdd.Run(); err != nil {
   110  					log.Fatalf("unable to add group '%s': %s", dockerGroup, err)
   111  				}
   112  				dockerGroup, err = user.LookupGroup(synsecGroup)
   113  				if err != nil {
   114  					log.Fatalf("unable to lookup '%s' group: %+v", dockerGroup, err)
   115  				}
   116  			}
   117  			intID, err := strconv.Atoi(dockerGroup.Gid)
   118  			if err != nil {
   119  				log.Fatalf("unable to convert group ID to int: %s", err)
   120  			}
   121  			if err := os.Chown(csConfig.DbConfig.DbPath, 0, intID); err != nil {
   122  				log.Fatalf("unable to chown sqlite db file '%s': %s", csConfig.DbConfig.DbPath, err)
   123  			}
   124  
   125  			mb, err := metabase.SetupMetabase(csConfig.API.Server.DbConfig, metabaseListenAddress, metabaseListenPort, metabaseUser, metabasePassword, metabaseDbPath, dockerGroup.Gid)
   126  			if err != nil {
   127  				log.Fatalf(err.Error())
   128  			}
   129  
   130  			if err := mb.DumpConfig(metabaseConfigPath); err != nil {
   131  				log.Fatalf(err.Error())
   132  			}
   133  
   134  			log.Infof("Metabase is ready")
   135  			fmt.Println()
   136  			fmt.Printf("\tURL       : '%s'\n", mb.Config.ListenURL)
   137  			fmt.Printf("\tusername  : '%s'\n", mb.Config.Username)
   138  			fmt.Printf("\tpassword  : '%s'\n", mb.Config.Password)
   139  		},
   140  	}
   141  	cmdDashSetup.Flags().BoolVarP(&force, "force", "f", false, "Force setup : override existing files.")
   142  	cmdDashSetup.Flags().StringVarP(&metabaseDbPath, "dir", "d", "", "Shared directory with metabase container.")
   143  	cmdDashSetup.Flags().StringVarP(&metabaseListenAddress, "listen", "l", metabaseListenAddress, "Listen address of container")
   144  	cmdDashSetup.Flags().StringVarP(&metabaseListenPort, "port", "p", metabaseListenPort, "Listen port of container")
   145  	cmdDashSetup.Flags().BoolVarP(&forceYes, "yes", "y", false, "force  yes")
   146  	//cmdDashSetup.Flags().StringVarP(&metabaseUser, "user", "u", "synsec@synsec.net", "metabase user")
   147  	cmdDashSetup.Flags().StringVar(&metabasePassword, "password", "", "metabase password")
   148  
   149  	cmdDashboard.AddCommand(cmdDashSetup)
   150  
   151  	var cmdDashStart = &cobra.Command{
   152  		Use:   "start",
   153  		Short: "Start the metabase container.",
   154  		Long:  `Stats the metabase container using docker.`,
   155  		Args:  cobra.ExactArgs(0),
   156  		Run: func(cmd *cobra.Command, args []string) {
   157  			mb, err := metabase.NewMetabase(metabaseConfigPath)
   158  			if err != nil {
   159  				log.Fatalf(err.Error())
   160  			}
   161  			if err := mb.Container.Start(); err != nil {
   162  				log.Fatalf("Failed to start metabase container : %s", err)
   163  			}
   164  			log.Infof("Started metabase")
   165  			log.Infof("url : http://%s:%s", metabaseListenAddress, metabaseListenPort)
   166  		},
   167  	}
   168  	cmdDashboard.AddCommand(cmdDashStart)
   169  
   170  	var cmdDashStop = &cobra.Command{
   171  		Use:   "stop",
   172  		Short: "Stops the metabase container.",
   173  		Long:  `Stops the metabase container using docker.`,
   174  		Args:  cobra.ExactArgs(0),
   175  		Run: func(cmd *cobra.Command, args []string) {
   176  			if err := metabase.StopContainer(metabaseContainerID); err != nil {
   177  				log.Fatalf("unable to stop container '%s': %s", metabaseContainerID, err)
   178  			}
   179  		},
   180  	}
   181  	cmdDashboard.AddCommand(cmdDashStop)
   182  
   183  	var cmdDashRemove = &cobra.Command{
   184  		Use:   "remove",
   185  		Short: "removes the metabase container.",
   186  		Long:  `removes the metabase container using docker.`,
   187  		Args:  cobra.ExactArgs(0),
   188  		Example: `
   189  ccscli dashboard remove
   190  ccscli dashboard remove --force
   191   `,
   192  		Run: func(cmd *cobra.Command, args []string) {
   193  			answer := true
   194  			if !forceYes {
   195  				prompt := &survey.Confirm{
   196  					Message: "Do you really want to remove synsec dashboard? (all your changes will be lost)",
   197  					Default: true,
   198  				}
   199  				if err := survey.AskOne(prompt, &answer); err != nil {
   200  					log.Fatalf("unable to ask to force: %s", err)
   201  				}
   202  			}
   203  			if answer {
   204  				if metabase.IsContainerExist(metabaseContainerID) {
   205  					log.Debugf("Stopping container %s", metabaseContainerID)
   206  					if err := metabase.StopContainer(metabaseContainerID); err != nil {
   207  						log.Warningf("unable to stop container '%s': %s", metabaseContainerID, err)
   208  					}
   209  					dockerGroup, err := user.LookupGroup(synsecGroup)
   210  					if err == nil { // if group exist, remove it
   211  						groupDelCmd, err := exec.LookPath("groupdel")
   212  						if err != nil {
   213  							log.Fatalf("unable to find 'groupdel' command, can't continue")
   214  						}
   215  
   216  						groupDel := &exec.Cmd{Path: groupDelCmd, Args: []string{groupDelCmd, synsecGroup}}
   217  						if err := groupDel.Run(); err != nil {
   218  							log.Errorf("unable to delete group '%s': %s", dockerGroup, err)
   219  						}
   220  					}
   221  					log.Debugf("Removing container %s", metabaseContainerID)
   222  					if err := metabase.RemoveContainer(metabaseContainerID); err != nil {
   223  						log.Warningf("unable to remove container '%s': %s", metabaseContainerID, err)
   224  					}
   225  					log.Infof("container %s stopped & removed", metabaseContainerID)
   226  				}
   227  				log.Debugf("Removing metabase db %s", csConfig.ConfigPaths.DataDir)
   228  				if err := metabase.RemoveDatabase(csConfig.ConfigPaths.DataDir); err != nil {
   229  					log.Warningf("failed to remove metabase internal db : %s", err)
   230  				}
   231  				if force {
   232  					if err := metabase.RemoveImageContainer(); err != nil {
   233  						if !strings.Contains(err.Error(), "No such image") {
   234  							log.Fatalf("removing docker image: %s", err)
   235  						}
   236  					}
   237  				}
   238  			}
   239  		},
   240  	}
   241  	cmdDashRemove.Flags().BoolVarP(&force, "force", "f", false, "Remove also the metabase image")
   242  	cmdDashRemove.Flags().BoolVarP(&forceYes, "yes", "y", false, "force  yes")
   243  	cmdDashboard.AddCommand(cmdDashRemove)
   244  
   245  	return cmdDashboard
   246  }