// Status prints out an environment healthcheck. The status of the environment // and every service in the environment is printed out. func Status(settings *models.Settings) { helpers.SignIn(settings) env := helpers.RetrieveEnvironment("pod", settings) fmt.Printf("%s (environment ID = %s):\n", env.Data.Name, env.ID) for _, service := range *env.Data.Services { if service.Type != "utility" { if service.Type == "code" { switch service.Size.(type) { case string: printLegacySizing(&service) default: printNewSizing(&service) } } else { switch service.Size.(type) { case string: sizeString := service.Size.(string) defer fmt.Printf("\t%s (size = %s, image = %s, status = %s) ID: %s\n", service.Label, sizeString, service.Name, service.DeployStatus, service.ID) default: serviceSize := service.Size.(map[string]interface{}) defer fmt.Printf("\t%s (ram = %.0f, storage = %.0f, behavior = %s, type = %s, cpu = %.0f, image = %s, status = %s) ID: %s\n", service.Label, serviceSize["ram"], serviceSize["storage"], serviceSize["behavior"], serviceSize["type"], serviceSize["cpu"], service.Name, service.DeployStatus, service.ID) } } } } }
// Import imports data into a database service. The import is accomplished // by encrypting the file locally, requesting a location that it can be uploaded // to, then uploads the file. Once uploaded an automated service processes the // file and acts according to the given parameters. // // The type of file that should be imported depends on the database. For // PostgreSQL and MySQL, this should be a single `.sql` file. For Mongo, this // should be a single tar'ed, gzipped archive (`.tar.gz`) of the database dump // that you want to import. func Import(databaseLabel string, filePath string, mongoCollection string, mongoDatabase string, wipeFirst bool, settings *models.Settings) { helpers.SignIn(settings) if _, err := os.Stat(filePath); os.IsNotExist(err) { fmt.Printf("A file does not exist at path '%s'\n", filePath) os.Exit(1) } service := helpers.RetrieveServiceByLabel(databaseLabel, settings) if service == nil { fmt.Printf("Could not find a service with the label \"%s\"\n", databaseLabel) os.Exit(1) } env := helpers.RetrieveEnvironment("spec", settings) pod := helpers.RetrievePodMetadata(env.PodID, settings) fmt.Printf("Importing '%s' into %s (ID = %s)\n", filePath, databaseLabel, service.ID) key := make([]byte, 32) iv := make([]byte, aes.BlockSize) rand.Read(key) rand.Read(iv) fmt.Println("Encrypting...") encrFilePath := helpers.EncryptFile(filePath, key, iv, pod.ImportRequiresLength) defer os.Remove(encrFilePath) options := map[string]string{} if mongoCollection != "" { options["mongoCollection"] = mongoCollection } if mongoDatabase != "" { options["mongoDatabase"] = mongoDatabase } fmt.Println("Uploading...") tempURL := helpers.RetrieveTempUploadURL(service.ID, settings) task := helpers.InitiateImport(tempURL.URL, encrFilePath, string(helpers.Base64Encode(helpers.Hex(key))), string(helpers.Base64Encode(helpers.Hex(iv))), options, wipeFirst, service.ID, settings) fmt.Printf("Processing import... (task ID = %s)\n", task.ID) ch := make(chan string, 1) go helpers.PollTaskStatus(task.ID, ch, settings) status := <-ch task.Status = status fmt.Printf("\nImport complete (end status = '%s')\n", task.Status) helpers.DumpLogs(service, task, "restore", settings) if task.Status != "finished" { os.Exit(1) } }
// Logs is a way to stream logs from Kibana to your local terminal. This is // useful because Kibana is hard to look at because it splits every single // log statement into a separate block that spans multiple lines so it's // not very cohesive. This is intended to be similar to the `heroku logs` // command. func Logs(queryString string, tail bool, hours int, minutes int, seconds int, settings *models.Settings) { if settings.Username == "" || settings.Password == "" { // sometimes this will be filled in from env variables // if it is, just use that and don't prompt them settings.Username = "" settings.Password = "" fmt.Println("Please enter your logging dashboard credentials") } // if we remove the session token, the CLI will prompt for the // username/password normally. It will also set the username/password // on the settings object. sessionToken := settings.SessionToken settings.SessionToken = "" helpers.SignIn(settings) env := helpers.RetrieveEnvironment("pod", settings) var domain = env.Data.DNSName if domain == "" { domain = fmt.Sprintf("%s.catalyze.io", env.Data.Namespace) } urlString := fmt.Sprintf("https://%s/__es", domain) offset := time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second timestamp := time.Now().In(time.UTC).Add(-1 * offset) from := 0 query := &models.LogQuery{ Fields: []string{"@timestamp", "message"}, Query: &models.Query{ Wildcard: map[string]string{ "message": queryString, }, }, Filter: &models.FilterRange{ Range: &models.RangeTimestamp{ Timestamp: map[string]string{ "gt": fmt.Sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", timestamp.Year(), timestamp.Month(), timestamp.Day(), timestamp.Hour(), timestamp.Minute(), timestamp.Second()), }, }, }, Sort: &models.LogSort{ Timestamp: map[string]string{ "order": "asc", }, Message: map[string]string{ "order": "asc", }, }, From: from, Size: 50, } var tr = &http.Transport{ TLSClientConfig: &tls.Config{ MinVersion: tls.VersionTLS12, }, } client := &http.Client{ Transport: tr, } settings.SessionToken = sessionToken config.SaveSettings(settings) fmt.Println(" @timestamp - message") for { query.From = from b, err := json.Marshal(*query) if err != nil { panic(err) } reader := bytes.NewReader(b) req, _ := http.NewRequest("GET", fmt.Sprintf("%s/_search", urlString), reader) req.SetBasicAuth(settings.Username, settings.Password) resp, err := client.Do(req) if err != nil { fmt.Println(err.Error()) } respBody, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { fmt.Println(fmt.Errorf("%d %s", resp.StatusCode, string(respBody)).Error()) os.Exit(1) } var logs models.Logs json.Unmarshal(respBody, &logs) for _, lh := range *logs.Hits.Hits { fmt.Printf("%s - %s\n", lh.Fields["@timestamp"][0], lh.Fields["message"][0]) } if !tail { break } time.Sleep(2 * time.Second) from += len(*logs.Hits.Hits) } }