func cmdRun(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } webAddr := c.String("web-addr") saslPaths := c.StringSlice("sock") var wg sync.WaitGroup if webAddr != "" { wg.Add(1) go func() { defer wg.Done() if err := runWebAddr(webAddr, s.GetInterface(), c.GlobalString("web-static-dir")); err != nil { fmt.Printf("warning running web interface failed: %s\n", err) } }() } for _, path := range saslPaths { p := path wg.Add(1) go func() { defer wg.Done() if err := runSaslAuthSocket(p, s.GetInterface()); err != nil { fmt.Printf("warning running auth agent(%s) failed: %s\n", p, err) } }() } wg.Wait() return cli.NewExitError(fmt.Sprintf("shutting down since all auth sockets have closed."), 0) }
func upAction(c *cli.Context) error { // TODO: get port from args port := "3000" consPort, err := getConsolePort(port) if err != nil { return cli.NewExitError(err.Error(), 1) } // TODO: apiServerURL := "https://api.leancloud.cn" appInfo, err := apps.CurrentAppInfo(".") if err != nil { return cli.NewExitError(err.Error(), 1) } rtm, err := apps.DetectRuntime(".") if err != nil { return cli.NewExitError(err.Error(), 1) } rtm.Envs["LC_APP_ID"] = appInfo.AppID rtm.Envs["LC_APP_KEY"] = appInfo.AppKey rtm.Envs["LC_APP_MASTER_KEY"] = appInfo.MasterKey rtm.Envs["LC_APP_PORT"] = port rtm.Envs["LC_API_SERVER"] = apiServerURL rtm.Envs["LEANCLOUD_APP_ID"] = appInfo.AppID rtm.Envs["LEANCLOUD_APP_KEY"] = appInfo.AppKey rtm.Envs["LEANCLOUD_APP_MASTER_KEY"] = appInfo.MasterKey rtm.Envs["LEANCLOUD_APP_PORT"] = port rtm.Envs["LEANCLOUD_API_SERVER"] = apiServerURL go func() { err := rtm.Run() if err != nil { panic(err) } }() cons := &console.Server{ AppID: appInfo.AppID, AppKey: appInfo.AppKey, MasterKey: appInfo.MasterKey, AppPort: port, ConsolePort: consPort, } cons.Run() return nil }
func cmdCheck(c *cli.Context) error { s, err := NewStore(c.GlobalString("store"), c.GlobalString("do-upgrades"), c.GlobalString("policy-type"), c.GlobalString("policy-condition"), c.GlobalString("hooks-dir")) if err != nil { return cli.NewExitError(fmt.Sprintf("Error opening whawty store: %s", err), 3) } ok, err := s.GetInterface().Check() if err != nil { return cli.NewExitError(fmt.Sprintf("Error checking whawty store: %s", err), 3) } if !ok { return cli.NewExitError(fmt.Sprintf("whawty store is invalid!"), 1) } return cli.NewExitError(fmt.Sprintf("whawty store is ok!"), 0) }
func jobs_push(c *cli.Context) error { // push the image docker_image, err := get_docker_image_name(c) if err != nil { return cli.NewExitError(err.Error(), 1) } log.Info("Pushing", docker_image) cmd := exec.Command("docker", "push", docker_image) output, err := cmd.CombinedOutput() if err != nil { return cli.NewExitError(err.Error(), 1) } log.Info(string(output)) return nil }
func cmdList(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } if c.Bool("full") { err = cmdListFull(s.GetInterface()) } else { err = cmdListSupported(s.GetInterface()) } if err != nil { return cli.NewExitError(err.Error(), 3) } return cli.NewExitError("", 0) }
func loginAction(c *cli.Context) error { email, password := inputAccountInfo() info, err := api.Login(email, password) if err != nil { switch e := err.(type) { case api.Error: return cli.NewExitError(e.Content, 1) default: return cli.NewExitError(e.Error(), 1) } } fmt.Println("登录成功:") fmt.Printf("用户名: %s\r\n", info.Get("username").MustString()) fmt.Printf("邮箱: %s\r\n", info.Get("email").MustString()) return nil }
func cmdRunSa(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } listeners, err := activation.Listeners(true) if err != nil { return cli.NewExitError(fmt.Sprintf("fetching socket listeners from systemd failed: %s", err), 2) } fmt.Printf("got %d sockets from systemd\n", len(listeners)) if len(listeners) == 0 { return cli.NewExitError("shutting down since there are no sockets to lissten on.", 2) } var wg sync.WaitGroup for idx, listener := range listeners { switch listener.(type) { case *net.UnixListener: fmt.Printf("listener[%d]: is a UNIX socket (-> saslauthd)\n", idx) wg.Add(1) ln := listener.(*net.UnixListener) go func() { defer wg.Done() if err := runSaslAuthSocketListener(ln, s.GetInterface()); err != nil { fmt.Printf("warning running auth agent failed: %s\n", err) } }() case *net.TCPListener: fmt.Printf("listener[%d]: is a TCP socket (-> HTTP)\n", idx) wg.Add(1) ln := listener.(*net.TCPListener) go func() { defer wg.Done() if err := runWebListener(ln, s.GetInterface(), c.GlobalString("web-static-dir")); err != nil { fmt.Printf("error running web-api: %s", err) } }() default: fmt.Printf("listener[%d]: has type %T (ingnoring)\n", idx, listener) } } wg.Wait() return cli.NewExitError(fmt.Sprintf("shutting down since all auth sockets have closed."), 0) }
func cmdRemove(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } username := c.Args().First() if username == "" { cli.ShowCommandHelp(c, "remove") return cli.NewExitError("", 0) } if err := s.GetInterface().Remove(username); err != nil { return cli.NewExitError(fmt.Sprintf("Error removing user '%s': %s", username, err), 3) } return cli.NewExitError(fmt.Sprintf("user '%s' successfully removed!", username), 0) }
func jobs_commit(c *cli.Context) error { // push json into Dockerfile var job_type scalecli.JobType err := Parse_json_or_yaml("job_type", &job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } registry := c.GlobalString("registry") if registry != "" { if !strings.HasPrefix(job_type.DockerImage, registry) { job_type.DockerImage = registry + "/" + job_type.DockerImage } } tag := c.GlobalString("tag") if tag != "" { if !strings.HasSuffix(job_type.DockerImage, tag) { job_type.DockerImage = job_type.DockerImage + ":" + tag } } json_data, err := json.Marshal(job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } err = set_label_value("Dockerfile", "com.ngageoint.scale.job-type", string(json_data)) if err != nil { return cli.NewExitError(err.Error(), 1) } // build the docker image docker_image, err := get_docker_image_name(c) if err != nil { return cli.NewExitError(err.Error(), 1) } log.Info("Building", docker_image) cmd := exec.Command("docker", "build", "-t", docker_image, ".") output, err := cmd.CombinedOutput() if err != nil { return cli.NewExitError(string(output), 1) } log.Info(string(output)) if c.Bool("push") { jobs_push(c) } return nil }
func strike_create(c *cli.Context) error { url := c.GlobalString("url") if url == "" { return cli.NewExitError("A URL must be provided with the SCALE_URL environment variable or the --url argument", 1) } data_file := c.String("data") var strike_data scalecli.StrikeData err := Parse_json_or_yaml(data_file, &strike_data) if err != nil { return cli.NewExitError(err.Error(), 1) } strike_process_id, err := scalecli.CreateStrikeProcess(url, strike_data) if err != nil { return cli.NewExitError(err.Error(), 1) } color.Blue(fmt.Sprintf("Strike process %d created.", strike_process_id)) return nil }
func workspaces_list(c *cli.Context) error { max := c.Int("max") url := c.GlobalString("url") if url == "" { return cli.NewExitError("A URL must be provided with the SCALE_URL environment variable or the --url argument", 1) } workspaces, err := scalecli.GetWorkspaceList(url, max) if err != nil { return cli.NewExitError(err.Error(), 1) } for _, workspace := range workspaces { if workspace.Is_active { color.Green(workspace.String()) } else { color.White(workspace.String()) } } return nil }
func cmdSetAdmin(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } username := c.Args().First() if username == "" { cli.ShowCommandHelp(c, "set-admin") return cli.NewExitError("", 0) } isAdmin, err := strconv.ParseBool(c.Args().Get(1)) if err != nil { cli.ShowCommandHelp(c, "set-admin") return cli.NewExitError("", 0) } if err := s.GetInterface().SetAdmin(username, isAdmin); err != nil { return cli.NewExitError(fmt.Sprintf("Error changing admin status of user '%s': %s", username, err), 3) } if isAdmin { return cli.NewExitError(fmt.Sprintf("user '%s' is now an admin!", username), 0) } else { return cli.NewExitError(fmt.Sprintf("user '%s' is now a normal user!", username), 0) } }
func cmdUpdate(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } username := c.Args().First() if username == "" { cli.ShowCommandHelp(c, "update") return cli.NewExitError("", 0) } password := c.Args().Get(1) if password == "" { pwd, err := askPass() if err != nil { if err != gopass.ErrInterrupted { return cli.NewExitError(err.Error(), 2) } return cli.NewExitError("", 2) } password = pwd } if err := s.GetInterface().Update(username, password); err != nil { return cli.NewExitError(fmt.Sprintf("Error updating user '%s': %s", username, err), 3) } return cli.NewExitError(fmt.Sprintf("user '%s' successfully updated!", username), 0) }
func cmdInit(c *cli.Context) error { username := c.Args().First() if username == "" { cli.ShowCommandHelp(c, "init") return cli.NewExitError("", 0) } password := c.Args().Get(1) if password == "" { pwd, err := askPass() if err != nil { if err != gopass.ErrInterrupted { return cli.NewExitError(err.Error(), 2) } return cli.NewExitError("", 2) } password = pwd } s, err := NewStore(c.GlobalString("store"), c.GlobalString("do-upgrades"), c.GlobalString("policy-type"), c.GlobalString("policy-condition"), c.GlobalString("hooks-dir")) if err != nil { return cli.NewExitError(fmt.Sprintf("Error initializing whawty store: %s", err), 3) } if err := s.GetInterface().Init(username, password); err != nil { return cli.NewExitError(fmt.Sprintf("Error initializing whawty store: %s", err), 3) } return cli.NewExitError(fmt.Sprintf("whawty store successfully initialized!"), 0) }
func jobs_validate(c *cli.Context) error { var job_type scalecli.JobType err := Parse_json_or_yaml("job_type", &job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } url := c.GlobalString("url") if url == "" { return cli.NewExitError("A URL must be provided with the SCALE_URL environment variable or the --url argument", 1) } warnings, err := scalecli.ValidateJobType(url, job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } if warnings == "" { color.White("Job type specification is valid.") } else { color.Yellow(warnings) } return nil }
func workspaces_create(c *cli.Context) error { url := c.GlobalString("url") if url == "" { return cli.NewExitError("A URL must be provided with the SCALE_URL environment variable or the --url argument", 1) } data_file := c.String("data") var ws_data scalecli.NewWorkspace err := Parse_json_or_yaml(data_file, &ws_data) if err != nil { return cli.NewExitError(err.Error(), 1) } if ws_data.Version == "" { ws_data.Version = "1.0" } warnings, err := scalecli.CreateWorkspace(url, ws_data) if err != nil { return cli.NewExitError(err.Error(), 1) } if warnings != "" { return cli.NewExitError(warnings, 1) } return nil }
func diffCommandAction(c *cli.Context) error { config := diff.Config{ Certification: c.Args().First(), OpencontrolDir: opencontrolDir, } inventory, errs := diff.ComputeGapAnalysis(config) if errs != nil && len(errs) > 0 { return cli.NewExitError(cli.NewMultiError(errs...).Error(), 1) } fmt.Fprintf(c.App.Writer, "\nNumber of missing controls: %d\n", len(inventory.MissingControlList)) for _, standardAndControl := range sortmap.ByKey(inventory.MissingControlList) { fmt.Fprintf(c.App.Writer, "%s\n", standardAndControl.Key) } return nil }
func cmdAuthenticate(c *cli.Context) error { s, err := openAndCheck(c) if err != nil { return cli.NewExitError(err.Error(), 3) } username := c.Args().First() if username == "" { cli.ShowCommandHelp(c, "authenticate") return cli.NewExitError("", 0) } password := c.Args().Get(1) if password == "" { fmt.Printf("password for '%s': ", username) pwd, err := gopass.GetPasswd() if err != nil { if err != gopass.ErrInterrupted { return cli.NewExitError(err.Error(), 2) } return cli.NewExitError("", 2) } password = string(pwd) } ok, isAdmin, _, err := s.GetInterface().Authenticate(username, password) if err != nil { return cli.NewExitError(fmt.Sprintf("Error authenticating user '%s': %s", username, err), 3) } if !ok { return cli.NewExitError(fmt.Sprintf("Error wrong password for user '%s'", username), 1) } // wait for potential upgrades - this might still be to fast for remote upgrades // TODO: find a better way to handle this situation time.Sleep(100 * time.Millisecond) if isAdmin { return cli.NewExitError(fmt.Sprintf("user '%s' is an admin.", username), 0) } else { return cli.NewExitError(fmt.Sprintf("user '%s' is a normal user.", username), 0) } }
// NewCLIApp creates a new instances of the CLI func NewCLIApp() *cli.App { app := cli.NewApp() app.Name = "Compliance Masonry" app.Usage = "Open Control CLI Tool" app.Version = "1.1.1" app.Flags = []cli.Flag{ cli.BoolFlag{ Name: "verbose", Usage: "Indicates whether to run the command with verbosity.", }, } app.Before = func(c *cli.Context) error { // Resets the log to output to nothing log.SetOutput(ioutil.Discard) if c.Bool("verbose") { log.SetOutput(os.Stderr) log.Println("Running with verbosity") } return nil } app.Commands = []cli.Command{ { Name: "get", Aliases: []string{"g"}, Usage: "Install compliance dependencies", Flags: []cli.Flag{ cli.StringFlag{ Name: "dest", Value: constants.DefaultDestination, Usage: "Location to download the repos.", }, cli.StringFlag{ Name: "config", Value: constants.DefaultConfigYaml, Usage: "Location of system yaml", }, }, Action: func(c *cli.Context) error { f := fs.OSUtil{} config := c.String("config") configBytes, err := f.OpenAndReadFile(config) if err != nil { app.Writer.Write([]byte(err.Error())) os.Exit(1) } wd, err := os.Getwd() if err != nil { app.Writer.Write([]byte(err.Error())) os.Exit(1) } destination := filepath.Join(wd, c.String("dest")) err = Get(destination, configBytes, &common.ConfigWorker{Downloader: common.NewVCSDownloader(), Parser: parser.Parser{}, ResourceMap: mapset.Init(), FSUtil: f}) if err != nil { return cli.NewExitError(err.Error(), 1) } app.Writer.Write([]byte("Compliance Dependencies Installed")) return nil }, }, { Name: "docs", Aliases: []string{"d"}, Usage: "Create Documentation", Subcommands: []cli.Command{ { Name: "gitbook", Aliases: []string{"g"}, Usage: "Create Gitbook Documentation", Flags: []cli.Flag{ cli.StringFlag{ Name: "opencontrols, o", Value: "opencontrols", Usage: "Set opencontrols directory", Destination: &opencontrolDir, }, cli.StringFlag{ Name: "exports, e", Value: "exports", Usage: "Sets the export directory", Destination: &exportPath, }, cli.StringFlag{ Name: "markdowns, m", Value: "markdowns", Usage: "Sets the markdowns directory", Destination: &markdownPath, }, }, Action: func(c *cli.Context) error { config := gitbook.Config{ Certification: c.Args().First(), OpencontrolDir: opencontrolDir, ExportPath: exportPath, MarkdownPath: markdownPath, } warning, errMessages := docs.MakeGitbook(config) if warning != "" { app.Writer.Write([]byte(warning)) } if errMessages != nil && len(errMessages) > 0 { err := cli.NewMultiError(errMessages...) return cli.NewExitError(err.Error(), 1) } else { app.Writer.Write([]byte("New Gitbook Documentation Created")) return nil } }, }, { Name: "docx", Aliases: []string{"d"}, Usage: "Create Docx Documentation using a Template", Flags: []cli.Flag{ cli.StringFlag{ Name: "opencontrols, o", Value: "opencontrols", Usage: "Set opencontrols directory", Destination: &opencontrolDir, }, cli.StringFlag{ Name: "template, t", Value: "", Usage: "Set template to build", Destination: &templatePath, }, cli.StringFlag{ Name: "export, e", Value: "export.docx", Usage: "Sets the export directory", Destination: &exportPath, }, }, Action: func(c *cli.Context) error { config := docx.Config{ OpencontrolDir: opencontrolDir, TemplatePath: templatePath, ExportPath: exportPath, } if err := docs.BuildTemplate(config); err != nil && len(err.Error()) > 0 { return cli.NewExitError(err.Error(), 1) } else { app.Writer.Write([]byte("New Docx Created")) return nil } }, }, }, }, diffCommand, } return app }
func jobs_template(c *cli.Context) error { // locate the template...if none is specified, fall back to a hard coded default template_name := c.String("template") if template_name != "" { template_path := c.GlobalString("template-path") tmp, err := find_template_in_path(template_name, template_path) if err != nil { return cli.NewExitError(err.Error(), 1) } template_name = tmp } // setup the target dir target_dir := "." if c.NArg() > 0 { target_dir = c.Args()[0] } d, err := os.Stat(target_dir) if err == nil { if !d.Mode().IsDir() { return cli.NewExitError("Target exists and is not a directory.", 1) } else if c.Bool("f") { log.Warning("Target directory", strconv.Quote(target_dir), "exists. Overriting contents.") } else { return cli.NewExitError(fmt.Sprint("Target directory", strconv.Quote(target_dir), "exists. Use -f to overrite."), -1) } } else { if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOENT { os.MkdirAll(target_dir, 0755) } else { return cli.NewExitError(err.Error(), 1) } } context := map[string]string{} for _, val := range c.StringSlice("arg") { tmp := strings.Split(val, "=") if len(tmp) != 2 { log.Warning("Invalid arg", val) } else { context[tmp[0]] = tmp[1] } } // use the hardcoded defaults if template_name == "" { for fname, val := range defaults { log.Info("Processing", fname) target_path := filepath.Join(target_dir, fname) // process template tmpl, err := template.New(fname).Parse(val) if err != nil { return cli.NewExitError(err.Error(), 1) } outfile, err := os.Create(target_path) if err != nil { return cli.NewExitError(err.Error(), 1) } err = tmpl.Execute(outfile, context) outfile.Close() if err != nil { return cli.NewExitError(err.Error(), 1) } } } // go through all files in the template directory and apply the template to the output dir filepath.Walk(template_name, func(path string, info os.FileInfo, err error) error { if err != nil { return err } relpath, err := filepath.Rel(template_name, path) if err != nil { return err } if relpath == "." || relpath == ".." { return nil } log.Info("Processing", relpath) target_path := filepath.Join(target_dir, relpath) if info.IsDir() { os.Mkdir(target_path, 0755) } else { // process template tmpl, err := template.New(info.Name()).ParseFiles(path) if err != nil { return err } outfile, err := os.Create(target_path) if err != nil { return err } err = tmpl.Execute(outfile, context) outfile.Close() if err != nil { return err } } return nil }) return nil }
func jobs_deploy(c *cli.Context) error { // pull the image err := error(nil) // some weird scoping issues if we don't declare here docker_image := c.String("image") if docker_image == "" { docker_image, err = get_docker_image_name(c) if err != nil { return cli.NewExitError(err.Error(), 1) } } else { if c.GlobalString("registry") != "" { docker_image = c.GlobalString("registry") + "/" + docker_image } if c.GlobalString("tag") != "" { docker_image = docker_image + ":" + c.GlobalString("tag") } } log.Info("Using docker image:", docker_image) if c.Bool("pull") { log.Info("Pulling", docker_image) cmd := exec.Command("docker", "pull", docker_image) _, err = cmd.CombinedOutput() if err != nil { log.Warning("Unable to pull the image. Checking if it is locally available.", err) } } // extract the JSON cmd := exec.Command("docker", "inspect", "-f", "{{(index .Config.Labels \"com.ngageoint.scale.job-type\")}}", docker_image) output, err := cmd.CombinedOutput() if err != nil { log.Debug(string(output)) return cli.NewExitError(err.Error(), 1) } json_value := strings.Replace(string(output), "$ {", "${", -1) if strings.TrimSpace(json_value) == "" { return cli.NewExitError(fmt.Sprint("Scale job type information not found in ", docker_image), 1) } var job_type scalecli.JobType err = json.Unmarshal([]byte(json_value), &job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } url := c.GlobalString("url") if url == "" { return cli.NewExitError("A URL must be provided with the SCALE_URL environment variable or the --url argument", 1) } if c.Bool("n") { // validate only warnings, err := scalecli.ValidateJobType(url, job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } if warnings == "" { color.White("Job type specification is valid.") } else { color.Yellow(warnings) } return nil } // check for existing job type job_types, err := scalecli.GetJobTypes(url, job_type.Name) if err != nil { return cli.NewExitError(err.Error(), 1) } if len(job_types) == 0 { // create a new job type log.Info("Creating new job type entry") err = scalecli.CreateJobType(url, job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } } else { for _, jt := range job_types { if jt.Version == job_type.Version { // found an exising entry, either update or create a new one log.Info("Updating job type metadata", jt.Id) err = scalecli.UpdateJobType(url, jt.Id, job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } return nil } } // no entry found, create a new one log.Info("Creating new job type entry for", job_type.Name, "version", job_type.Version) err = scalecli.CreateJobType(url, job_type) if err != nil { return cli.NewExitError(err.Error(), 1) } } return nil }
func recipes_run(c *cli.Context) error { url := c.GlobalString("url") if url == "" { return cli.NewExitError("A URL must be provided with the SCALE_URL environment variable or the --url argument", 1) } if c.NArg() != 1 && c.NArg() != 2 { return cli.NewExitError("Must specify a single recipe type name or id.", 1) } var recipe_type scalecli.RecipeType found := false id, err := strconv.Atoi(c.Args()[0]) if err == nil { var resp_code int recipe_type, resp_code, err = scalecli.GetRecipeTypeDetails(url, id) if err != nil && resp_code != 404 { return cli.NewExitError(err.Error(), 1) } else if err == nil { found = true } } if !found { name := c.Args()[0] recipe_types, err := scalecli.GetRecipeTypes(url) if err != nil { return cli.NewExitError(err.Error(), 1) } var version string if c.NArg() == 2 { version = c.Args()[1] } switch len(recipe_types) { case 0: return cli.NewExitError("Recipe type not found.", 1) case 1: recipe_type = recipe_types[0] found = true break default: for _, rt := range recipe_types { if rt.Name == name && rt.Version == version { recipe_type = rt found = true break } } if !found { for _, rt := range recipe_types { fmt.Printf("%4d %8s [%25s] - %s\n", rt.Id, rt.Version, rt.Name, rt.Title) } return cli.NewExitError("Multiple recipe types found", 1) } } } data_file := c.String("data") var recipe_data scalecli.RecipeData err = Parse_json_or_yaml(data_file, &recipe_data) if err != nil { return cli.NewExitError(err.Error(), 1) } update_location, err := scalecli.RunRecipe(url, recipe_type.Id, recipe_data) if err != nil { return cli.NewExitError(err.Error(), 1) } color.Blue(fmt.Sprintf("Recipe submited, updates available at %s", update_location)) return nil }
func (g graphDef) getBaseGraph(graphType string, height int, width int) (baseGraph baseGraph, err error) { if g.isHostGraph() { if g.GraphName == "" { return nil, cli.NewExitError("graph_name is required for host graph.", 1) } return hostGraph{ g.HostID, graphType, g.GraphName, g.Period, height, width, }, nil } if g.isServiceGraph() { if g.GraphName == "" { return nil, cli.NewExitError("graph_name is required for service graph.", 1) } return serviceGraph{ g.ServiceName, graphType, g.GraphName, g.Period, height, width, }, nil } if g.isRoleGraph() { if g.GraphName == "" { return nil, cli.NewExitError("graph_name is required for role graph.", 1) } return roleGraph{ g.ServiceName, g.RoleName, graphType, g.GraphName, g.Period, g.Stacked, g.Simplified, height, width, }, nil } if g.isExpressionGraph() { return expressionGraph{ g.Query, graphType, g.Period, height, width, }, nil } return nil, cli.NewExitError("either host_id, service_name or query should be specified.", 1) }
func doGenerateDashboards(c *cli.Context) error { conffile := c.GlobalString("conf") isStdout := c.Bool("print") argFilePath := c.Args() if len(argFilePath) < 1 { cli.ShowCommandHelp(c, "generate") return cli.NewExitError("specify a yaml file.", 1) } buf, err := ioutil.ReadFile(argFilePath[0]) logger.DieIf(err) yml := graphsConfig{} err = yaml.Unmarshal(buf, &yml) logger.DieIf(err) client := newMackerel(conffile) org, err := client.GetOrg() logger.DieIf(err) if yml.ConfigVersion == "" { return cli.NewExitError("config_version is required in yaml.", 1) } if yml.ConfigVersion != "0.9" { return cli.NewExitError(fmt.Sprintf("config_version %s is not suport.", yml.ConfigVersion), 1) } if yml.Title == "" { return cli.NewExitError("title is required in yaml.", 1) } if yml.URLPath == "" { return cli.NewExitError("url_path is required in yaml.", 1) } if yml.Format == "" { yml.Format = "iframe" } if yml.Format != "iframe" && yml.Format != "image" { return cli.NewExitError("graph_type should be 'iframe' or 'image'.", 1) } if yml.Height == 0 { yml.Height = 200 } if yml.Width == 0 { yml.Width = 400 } if yml.HostGraphFormat != nil && yml.GraphFormat != nil { return cli.NewExitError("you cannot specify both 'graphs' and host_graphs'.", 1) } var markdown string for _, h := range yml.HostGraphFormat { mdf := generateHostGraphsMarkdownFactory(h, yml.Format, yml.Height, yml.Width) markdown += mdf.generate(org.Name) } for _, g := range yml.GraphFormat { mdf, err := generateGraphsMarkdownFactory(g, yml.Format, yml.Height, yml.Width) if err != nil { return err } markdown += mdf.generate(org.Name) } if isStdout { fmt.Println(markdown) } else { updateDashboard := &mackerel.Dashboard{ Title: yml.Title, BodyMarkDown: markdown, URLPath: yml.URLPath, } dashboards, fetchError := client.FindDashboards() logger.DieIf(fetchError) dashboardID := "" for _, ds := range dashboards { if ds.URLPath == yml.URLPath { dashboardID = ds.ID } } if dashboardID == "" { _, createError := client.CreateDashboard(updateDashboard) logger.DieIf(createError) } else { _, updateError := client.UpdateDashboard(dashboardID, updateDashboard) logger.DieIf(updateError) } } return nil }
func main() { app := cli.NewApp() app.Name = "triton" app.Usage = "Utilities for the Triton Data Pipeline" app.Version = "0.0.1" app.Commands = []cli.Command{ { Name: "store", Aliases: []string{"s"}, Usage: "store triton data to s3", Flags: []cli.Flag{ cli.StringFlag{ Name: "stream", Usage: "Named triton stream", }, cli.StringFlag{ Name: "bucket", Usage: "Destination S3 bucket", EnvVar: "TRITON_BUCKET", }, cli.BoolFlag{ Name: "skip-to-latest", Usage: "Skip to latest in stream (ignoring previous checkpoints)", }, cli.StringFlag{ Name: "checkpoint-db", Usage: "Database connect string for storing checkpoints. Defaults to local sqlite.", Value: "sqlite://triton.db", EnvVar: "TRITON_DB", }, cli.StringFlag{ Name: "client-name", Usage: "optional name of triton client", Value: "store", EnvVar: "TRITON_CLIENT", }, }, Action: func(c *cli.Context) error { if c.String("bucket") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("bucket name required", 1) } if c.String("stream") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("stream name required", 1) } if strings.Contains(c.String("client-name"), "-") { cli.ShowSubcommandHelp(c) return cli.NewExitError("client name cannot contain a -", 1) } store(c.String("client-name"), c.String("stream"), c.String("bucket"), c.String("checkpoint-db"), c.Bool("skip-to-latest")) return nil }, }, { Name: "stats", Usage: "output stats for triton processes", Flags: []cli.Flag{ cli.StringFlag{ Name: "checkpoint-db", Usage: "Database connect string for storing checkpoints. Defaults to local sqlite.", Value: "sqlite://triton.db", EnvVar: "TRITON_DB", }, cli.StringFlag{ Name: "client-name", Usage: "name of triton client", EnvVar: "TRITON_CLIENT", }, }, Action: func(c *cli.Context) error { if c.String("client-name") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("missing client name", 1) } if strings.Contains(c.String("client-name"), "-") { cli.ShowSubcommandHelp(c) return cli.NewExitError("client name cannot contain a -", 1) } checkpointStats(c.String("client-name"), c.String("checkpoint-db")) return nil }, }, { Name: "shards", Usage: "list shards for stream", Flags: []cli.Flag{ cli.StringFlag{ Name: "stream", Usage: "Named triton stream", }}, Action: func(c *cli.Context) error { if c.String("stream") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("stream name required", 1) } listShards(c.String("stream")) return nil }, }, { Name: "cat", Usage: "cat stored triton data from s3", Flags: []cli.Flag{ cli.StringFlag{ Name: "stream", Usage: "Named triton stream", }, cli.StringFlag{ Name: "bucket", Usage: "Source S3 bucket", EnvVar: "TRITON_BUCKET", }, cli.StringFlag{ Name: "start-date", Usage: "Date to start streaming from YYYYMMDD", }, cli.StringFlag{ Name: "end-date", Usage: "(optional) Date to stop streaming from YYYYMMDD", }, cli.StringFlag{ Name: "client-name", Usage: "optional name of triton client. Defaults to any", Value: "", EnvVar: "TRITON_CLIENT", }}, Action: func(c *cli.Context) error { if c.String("stream") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("stream name required", 1) } if c.String("bucket") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("bucket name required", 1) } if c.String("start-date") == "" { cli.ShowSubcommandHelp(c) return cli.NewExitError("start-date required", 1) } // TODO: configure region sess := session.New(&aws.Config{Region: aws.String("us-west-1")}) s3Svc := s3.New(sess) start, err := time.Parse("20060102", c.String("start-date")) if err != nil { cli.ShowSubcommandHelp(c) return cli.NewExitError("invalid start-date", 1) } end := start if c.String("end-date") != "" { end, err = time.Parse("20060102", c.String("end-date")) if err != nil { cli.ShowSubcommandHelp(c) return cli.NewExitError("invalid end-date", 1) } } sc := openStreamConfig(c.String("stream")) set, err := triton.NewStoreReader(s3Svc, c.String("bucket"), c.String("client-name"), sc.StreamName, start, end) if err != nil { log.Fatalln("Failure listing archive:", err) } for { rec, err := set.ReadRecord() if err != nil { if err == io.EOF { break } else { log.Fatalln("Failed reading record", err) } } b, err := json.Marshal(rec) if err != nil { panic(err) } fmt.Println(string(b)) } return nil }, }, } raven.CapturePanic(func() { app.Run(os.Args) }, nil) }