<commit_before>// +build example

package main

import (
        "fmt"
        "io/ioutil"
        "log"
        "net/url"
        "os"
        "os/exec"
        "path/filepath"
        "regexp"
        "strings"
        "sync"
        "time"

        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
        "github.com/urfave/cli"

        "k8s.io/client-go/kubernetes"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        apiv1 "k8s.io/api/core/v1"
        k8sErrors "k8s.io/apimachinery/pkg/api/errors"
        rest "k8s.io/client-go/rest"
        discovery "k8s.io/apimachinery/pkg/version"
        wait "k8s.io/apimachinery/pkg/util/wait"

)

// Lists all objects in a bucket using pagination
//
// Usage:

const (
        // High enough QPS to fit all expected use cases. QPS=0 is not set here, because
        // client code is overriding it.
        defaultQPS = 1e6
        // High enough Burst to fit all expected use cases. Burst=0 is not set here, because
        // client code is overriding it.
        defaultBurst = 1e6
)

var secret_hash map[string]map[string][]byte

type S3Address struct {
        Bucket string
        Prefix string
}

func parseURL(path string) S3Address {
        var s S3Address

        if !strings.HasPrefix(path, "s3://") {
                path = fmt.Sprintf("s3://%s", path)
        }

        u, err := url.Parse(path)
        if err != nil {
                log.Fatal("Could not parse URL due to error: ", err)
        }

        if u.Scheme != "" && u.Scheme != "s3" {
                log.Fatal("Invalid URI scheme must be one of s3/NONE")
        }

        if strings.HasPrefix(u.Path, "/") {
                s.Prefix = u.Path[1:]
        } else {
                s.Prefix = u.Path
        }

        s.Bucket = u.Host
        return s
}

// listObjects <bucket>
func main() {
        app := cli.NewApp()
        app.Name = "s3sync"
        app.Version = "0.0.5"

        cli.VersionFlag = cli.BoolFlag{
                Name:  "version, V",
                Usage: "print version number",
        }

        app.Flags = []cli.Flag{
                cli.StringFlag{
                        Name:   "exec-on-change",
                        Usage:  "run a command in case of changes after sync",
                        EnvVar: "EXEC_ON_CHANGE,ON_CHANGE_EXEC,ON_CHANGE",
                },
                cli.StringFlag{
                        Name:   "match-regexp",
                        Usage:  "match s3 bucket contents with following regexp",
                        EnvVar: "MATCH_REGEXP",
                },
                cli.StringFlag{
                        Name:   "filter-out-regexp",
                        Usage:  "filter out s3 bucket with following regexp",
                        EnvVar: "FILTER_OUT_REGEXP",
                },
                cli.StringFlag{
                        Name:   "create-k8s-secret",
                        Usage:  "If true, will create secret on Kubernetes cluster instead of local file",
                        EnvVar: "CREATE_K8S_SECRET",
                },
                cli.StringFlag{
                        Name:   "k8s-secret-namespace",
                        Usage:  "Kubernetes cluster Namesace Name for secrets",
                        EnvVar: "K8S_SECRET_NAMESPACE",
                },
                cli.StringFlag{
                        Name:   "k8s-custom-label-name",
                        Usage:  "Kubernetes label name that will be assign to secret",
                        EnvVar: "K8S_CUSTOM_LABEL_NAME",
                },
                cli.StringFlag{
                        Name:   "k8s-custom-label-value",
                        Usage:  "Kubernetes label value that will be assign to secret",
                        EnvVar: "K8S_CUSTOM_LABEL_VALUE",
                },
                cli.StringFlag{
                        Name:   "verbose",
                        Usage:  "Verbose flag",
                        EnvVar: "VERBOSE",
                },
        }

        app.Commands = []cli.Command{
                {
                        Name:   "sync",
                        Usage:  "Synchronize a directory tree to S3 -- LOCAL_DIR s3://BUCKET[/PREFIX] or s3://BUCKET[/PREFIX] LOCAL_DIR",
                        Action: CmdSync,
                        Flags:  app.Flags,
                },
        }
        app.Run(os.Args)
}

func CmdSync(c *cli.Context) error {
        sess, err := session.NewSession()

        var wg sync.WaitGroup

        if err != nil {
                fmt.Println("failed to create session,", err)
                return err
        }

        svc := s3.New(sess)
        s3_path := c.Args().Get(0)
        local_path := c.Args().Get(1)

        u := parseURL(s3_path)

        params := &s3.ListObjectsV2Input{
                Bucket:     aws.String(u.Bucket), // Required
                FetchOwner: aws.Bool(true),
                Prefix:     aws.String(u.Prefix),
        }

        i := 0
        var ActionRequired bool
        secret_hash = make(map[string]map[string][]byte)

        err = svc.ListObjectsV2Pages(params,
                func(p *s3.ListObjectsV2Output, last bool) (shouldContinue bool) {
                        i++
                        for _, obj := range p.Contents {
                                // make magic here
                                var filter_out bool
                                matched := true

                                if c.GlobalString("match-regexp") != "" {
                                        matched, _ = regexp.MatchString(c.GlobalString("match-regexp"), *obj.Key)
                                }

                                if c.GlobalString("filter-out-regexp") != "" {
                                        filter_out, _ = regexp.MatchString(c.GlobalString("filter-out-regexp"), *obj.Key)
                                }

                                if matched && filter_out != true {
                                        if c.GlobalString("create-k8s-secret") == "true" {
                                                saveFileToSecretMapFromS3(svc, u.Bucket, *obj, filepath.Base(*obj.Key), &wg)
                                        } else {
                                                filename := filepath.Join(local_path, filepath.Base(*obj.Key))
                                                fileInfo, err := os.Stat(filename)

                                                if os.IsNotExist(err) {
                                                        fmt.Println("file: ", filename, "does not exists, copying from s3")
                                                        go copyFileFromS3(svc, u.Bucket, *obj, filename, &wg)
                                                        ActionRequired = true
                                                } else {
                                                        if fileInfo.ModTime().UTC() != *obj.LastModified {
                                                                fmt.Println("modification time of file:", filename, "does not match one from s3, ordering copy")
                                                                go copyFileFromS3(svc, u.Bucket, *obj, filename, &wg)
                                                                ActionRequired = true
                                                        }
                                                }
                                        }
                                }
                        }
                        wg.Wait()
                        return true
                })

        if c.GlobalString("create-k8s-secret") == "true" {
                secret_namespace := "default"

                if c.GlobalString("k8s-secret-namespace") != "" {
                        secret_namespace = c.GlobalString("k8s-secret-namespace")
                }

                err = saveSecretMapToK8s(secret_hash,secret_namespace,c.GlobalString("k8s-custom-label-name"),c.GlobalString("k8s-custom-label-value"),c.GlobalString("verbose"))
        }

        if err != nil {
                // Print the error, cast err to awserr.Error to get the Code and
                // Message from an error.
                fmt.Println(err.Error())
                return err
        }

        if ActionRequired {
                action := fmt.Sprintf("%v", c.GlobalString("exec-on-change"))
                fmt.Println("Executing command", action)

                cmd := exec.Command("/bin/sh", "-c", action)
                cmd.Stdout = os.Stdout
                cmd.Stderr = os.Stderr
                err = cmd.Run()
                if err != nil {
                        fmt.Println("could not execute due to error:", err)
                }
        }
        return nil

}

func copyFileFromS3(s *s3.S3, bucket string, obj s3.Object, filename string, wg *sync.WaitGroup) error {
        defer wg.Done()
        wg.Add(1)
        params := &s3.GetObjectInput{
                Bucket: aws.String(bucket),   // Required
                Key:    aws.String(*obj.Key), // Required
        }

        resp, err := s.GetObject(params)
        if err != nil {
                return err
        }

        data, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                fmt.Println("Could not download file from s3 due to:", err)
                return err
        }

        err = ioutil.WriteFile(filename, data, 0666)
        if err != nil {
                fmt.Println("Could not write file from s3 due to:", err)
                return err
        }
        return os.Chtimes(filename, *obj.LastModified, *obj.LastModified)
}

func createApiserverClient() (*kubernetes.Clientset, error) {
        cfg, err := rest.InClusterConfig()
        if err != nil {
                fmt.Println("could not execute due to error:", err)
                return nil, err
        }

        cfg.QPS = defaultQPS
        cfg.Burst = defaultBurst
        cfg.ContentType = "application/vnd.kubernetes.protobuf"

        client, err := kubernetes.NewForConfig(cfg)
        if err != nil {
                fmt.Println("could not execute due to error:", err)
                return nil, err
        }

        var v *discovery.Info

        // In some environments is possible the client cannot connect the API server in the first request
        // https://github.com/kubernetes/ingress-nginx/issues/1968
        defaultRetry := wait.Backoff{
                Steps:    10,
                Duration: 1 * time.Second,
                Factor:   1.5,
                Jitter:   0.1,
        }

        var lastErr error
        retries := 0
        err = wait.ExponentialBackoff(defaultRetry, func() (bool, error) {
                v, err = client.Discovery().ServerVersion()
                if err == nil {
                        return true, nil
                }

                lastErr = err
                retries++
                return false, nil
        })

        // err is not null only if there was a timeout in the exponential backoff (ErrWaitTimeout)
        if err != nil {
                return nil, lastErr
        }

        return client, nil
}

func EnsureSecret(secret *apiv1.Secret, verbose string) (*apiv1.Secret, error) {
        kubeClient, err := createApiserverClient()
        s, err := kubeClient.CoreV1().Secrets(secret.Namespace).Create(secret)
        if err != nil {
                if k8sErrors.IsAlreadyExists(err) {
                        if verbose == "true" {
                                fmt.Println("Secret" ,secret.Name," already exist in namespace ",secret.Namespace,", updating")
                        }
                        return kubeClient.CoreV1().Secrets(secret.Namespace).Update(secret)
                }
                fmt.Println("could not execute due to error:", err)
                return nil, err
        }
        fmt.Println("Secret" ,secret.Name," created in namespace ",secret.Namespace)
        return s, nil
}

func saveFileToSecretMapFromS3(s *s3.S3, bucket string, obj s3.Object, filename string, wg *sync.WaitGroup) (error) {
        defer wg.Done()
        wg.Add(1)
        params := &s3.GetObjectInput{
                Bucket: aws.String(bucket),   // Required
                Key:    aws.String(*obj.Key), // Required
        }
        resp, err := s.GetObject(params)
        if err != nil {
                return err
        }

        data, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                fmt.Println("Could not download file from s3 due to:", err)
                return err
        }

        r_secret_name, err := regexp.Compile("^(.+)\\.(?:key|crt)$")

        match_key, err := regexp.MatchString("^(.+)\\.key$", filename)
        match_cert, err := regexp.MatchString("^(.+)\\.crt$", filename)

        if match_key {
                if secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] == nil {
                        secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] = make(map[string][]byte)
                }
                secret_hash[r_secret_name.FindStringSubmatch(filename)[1]]["key"] = data
        }
        if match_cert {
                if secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] == nil {
                        secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] = make(map[string][]byte)
                }
                secret_hash[r_secret_name.FindStringSubmatch(filename)[1]]["cert"] = data
        }

        return err
}

func saveSecretMapToK8s(sh map[string]map[string][]byte, namespace string, label_name string, label_value string, verbose string) (err error) {
        fmt.Println("Start creating secrets in Kubernetes")

        for secret_domain := range sh {
                var err_message string
                if sh[secret_domain]["cert"] == nil {
                        err_message = "ERROR: " + secret_domain + " do not have cert file"
                        fmt.Println(err_message)
                } else if sh[secret_domain]["key"] == nil {
                        err_message = "ERROR: " + secret_domain + " do not have key file"
                        fmt.Println(err_message)
                } else {
                        var labels map[string]string
                        labels = make (map[string]string)
                        labels["cert_domain"] = secret_domain
                        labels["created_by"] = "s3sync"
                        if label_name != "" && label_value != "" {
                                labels[label_name] = label_value
                        }

                        _, err = EnsureSecret(&apiv1.Secret{
                                ObjectMeta: metav1.ObjectMeta{
                                        Name:      secret_domain,
                                        Namespace: namespace,
                                        Labels: labels,
                                },
                                Data: map[string][]byte{
                                        apiv1.TLSCertKey:       sh[secret_domain]["cert"],
                                        apiv1.TLSPrivateKeyKey: sh[secret_domain]["key"],
                                },
                        }, verbose)

                        if err != nil {
                                fmt.Println(err.Error())
                                return err
                        }
                }
        }
        return nil
}

                                                                                422,0-1       Bot

<commit_msg>fix typo<commit_after>// +build example

package main

import (
        "fmt"
        "io/ioutil"
        "log"
        "net/url"
        "os"
        "os/exec"
        "path/filepath"
        "regexp"
        "strings"
        "sync"
        "time"

        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
        "github.com/urfave/cli"

        "k8s.io/client-go/kubernetes"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        apiv1 "k8s.io/api/core/v1"
        k8sErrors "k8s.io/apimachinery/pkg/api/errors"
        rest "k8s.io/client-go/rest"
        discovery "k8s.io/apimachinery/pkg/version"
        wait "k8s.io/apimachinery/pkg/util/wait"

)

// Lists all objects in a bucket using pagination
//
// Usage:

const (
        // High enough QPS to fit all expected use cases. QPS=0 is not set here, because
        // client code is overriding it.
        defaultQPS = 1e6
        // High enough Burst to fit all expected use cases. Burst=0 is not set here, because
        // client code is overriding it.
        defaultBurst = 1e6
)

var secret_hash map[string]map[string][]byte

type S3Address struct {
        Bucket string
        Prefix string
}

func parseURL(path string) S3Address {
        var s S3Address

        if !strings.HasPrefix(path, "s3://") {
                path = fmt.Sprintf("s3://%s", path)
        }

        u, err := url.Parse(path)
        if err != nil {
                log.Fatal("Could not parse URL due to error: ", err)
        }

        if u.Scheme != "" && u.Scheme != "s3" {
                log.Fatal("Invalid URI scheme must be one of s3/NONE")
        }

        if strings.HasPrefix(u.Path, "/") {
                s.Prefix = u.Path[1:]
        } else {
                s.Prefix = u.Path
        }

        s.Bucket = u.Host
        return s
}

// listObjects <bucket>
func main() {
        app := cli.NewApp()
        app.Name = "s3sync"
        app.Version = "0.0.5"

        cli.VersionFlag = cli.BoolFlag{
                Name:  "version, V",
                Usage: "print version number",
        }

        app.Flags = []cli.Flag{
                cli.StringFlag{
                        Name:   "exec-on-change",
                        Usage:  "run a command in case of changes after sync",
                        EnvVar: "EXEC_ON_CHANGE,ON_CHANGE_EXEC,ON_CHANGE",
                },
                cli.StringFlag{
                        Name:   "match-regexp",
                        Usage:  "match s3 bucket contents with following regexp",
                        EnvVar: "MATCH_REGEXP",
                },
                cli.StringFlag{
                        Name:   "filter-out-regexp",
                        Usage:  "filter out s3 bucket with following regexp",
                        EnvVar: "FILTER_OUT_REGEXP",
                },
                cli.StringFlag{
                        Name:   "create-k8s-secret",
                        Usage:  "If true, will create secret on Kubernetes cluster instead of local file",
                        EnvVar: "CREATE_K8S_SECRET",
                },
                cli.StringFlag{
                        Name:   "k8s-secret-namespace",
                        Usage:  "Kubernetes cluster Namesace Name for secrets",
                        EnvVar: "K8S_SECRET_NAMESPACE",
                },
                cli.StringFlag{
                        Name:   "k8s-custom-label-name",
                        Usage:  "Kubernetes label name that will be assign to secret",
                        EnvVar: "K8S_CUSTOM_LABEL_NAME",
                },
                cli.StringFlag{
                        Name:   "k8s-custom-label-value",
                        Usage:  "Kubernetes label value that will be assign to secret",
                        EnvVar: "K8S_CUSTOM_LABEL_VALUE",
                },
                cli.StringFlag{
                        Name:   "verbose",
                        Usage:  "Verbose flag",
                        EnvVar: "VERBOSE",
                },
        }

        app.Commands = []cli.Command{
                {
                        Name:   "sync",
                        Usage:  "Synchronize a directory tree to S3 -- LOCAL_DIR s3://BUCKET[/PREFIX] or s3://BUCKET[/PREFIX] LOCAL_DIR",
                        Action: CmdSync,
                        Flags:  app.Flags,
                },
        }
        app.Run(os.Args)
}

func CmdSync(c *cli.Context) error {
        sess, err := session.NewSession()

        var wg sync.WaitGroup

        if err != nil {
                fmt.Println("failed to create session,", err)
                return err
        }

        svc := s3.New(sess)
        s3_path := c.Args().Get(0)
        local_path := c.Args().Get(1)

        u := parseURL(s3_path)

        params := &s3.ListObjectsV2Input{
                Bucket:     aws.String(u.Bucket), // Required
                FetchOwner: aws.Bool(true),
                Prefix:     aws.String(u.Prefix),
        }

        i := 0
        var ActionRequired bool
        secret_hash = make(map[string]map[string][]byte)

        err = svc.ListObjectsV2Pages(params,
                func(p *s3.ListObjectsV2Output, last bool) (shouldContinue bool) {
                        i++
                        for _, obj := range p.Contents {
                                // make magic here
                                var filter_out bool
                                matched := true

                                if c.GlobalString("match-regexp") != "" {
                                        matched, _ = regexp.MatchString(c.GlobalString("match-regexp"), *obj.Key)
                                }

                                if c.GlobalString("filter-out-regexp") != "" {
                                        filter_out, _ = regexp.MatchString(c.GlobalString("filter-out-regexp"), *obj.Key)
                                }

                                if matched && filter_out != true {
                                        if c.GlobalString("create-k8s-secret") == "true" {
                                                saveFileToSecretMapFromS3(svc, u.Bucket, *obj, filepath.Base(*obj.Key), &wg)
                                        } else {
                                                filename := filepath.Join(local_path, filepath.Base(*obj.Key))
                                                fileInfo, err := os.Stat(filename)

                                                if os.IsNotExist(err) {
                                                        fmt.Println("file: ", filename, "does not exists, copying from s3")
                                                        go copyFileFromS3(svc, u.Bucket, *obj, filename, &wg)
                                                        ActionRequired = true
                                                } else {
                                                        if fileInfo.ModTime().UTC() != *obj.LastModified {
                                                                fmt.Println("modification time of file:", filename, "does not match one from s3, ordering copy")
                                                                go copyFileFromS3(svc, u.Bucket, *obj, filename, &wg)
                                                                ActionRequired = true
                                                        }
                                                }
                                        }
                                }
                        }
                        wg.Wait()
                        return true
                })

        if c.GlobalString("create-k8s-secret") == "true" {
                secret_namespace := "default"

                if c.GlobalString("k8s-secret-namespace") != "" {
                        secret_namespace = c.GlobalString("k8s-secret-namespace")
                }

                err = saveSecretMapToK8s(secret_hash,secret_namespace,c.GlobalString("k8s-custom-label-name"),c.GlobalString("k8s-custom-label-value"),c.GlobalString("verbose"))
        }

        if err != nil {
                // Print the error, cast err to awserr.Error to get the Code and
                // Message from an error.
                fmt.Println(err.Error())
                return err
        }

        if ActionRequired {
                action := fmt.Sprintf("%v", c.GlobalString("exec-on-change"))
                fmt.Println("Executing command", action)

                cmd := exec.Command("/bin/sh", "-c", action)
                cmd.Stdout = os.Stdout
                cmd.Stderr = os.Stderr
                err = cmd.Run()
                if err != nil {
                        fmt.Println("could not execute due to error:", err)
                }
        }
        return nil

}

func copyFileFromS3(s *s3.S3, bucket string, obj s3.Object, filename string, wg *sync.WaitGroup) error {
        defer wg.Done()
        wg.Add(1)
        params := &s3.GetObjectInput{
                Bucket: aws.String(bucket),   // Required
                Key:    aws.String(*obj.Key), // Required
        }

        resp, err := s.GetObject(params)
        if err != nil {
                return err
        }

        data, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                fmt.Println("Could not download file from s3 due to:", err)
                return err
        }

        err = ioutil.WriteFile(filename, data, 0666)
        if err != nil {
                fmt.Println("Could not write file from s3 due to:", err)
                return err
        }
        return os.Chtimes(filename, *obj.LastModified, *obj.LastModified)
}

func createApiserverClient() (*kubernetes.Clientset, error) {
        cfg, err := rest.InClusterConfig()
        if err != nil {
                fmt.Println("could not execute due to error:", err)
                return nil, err
        }

        cfg.QPS = defaultQPS
        cfg.Burst = defaultBurst
        cfg.ContentType = "application/vnd.kubernetes.protobuf"

        client, err := kubernetes.NewForConfig(cfg)
        if err != nil {
                fmt.Println("could not execute due to error:", err)
                return nil, err
        }

        var v *discovery.Info

        // In some environments is possible the client cannot connect the API server in the first request
        // https://github.com/kubernetes/ingress-nginx/issues/1968
        defaultRetry := wait.Backoff{
                Steps:    10,
                Duration: 1 * time.Second,
                Factor:   1.5,
                Jitter:   0.1,
        }

        var lastErr error
        retries := 0
        err = wait.ExponentialBackoff(defaultRetry, func() (bool, error) {
                v, err = client.Discovery().ServerVersion()
                if err == nil {
                        return true, nil
                }

                lastErr = err
                retries++
                return false, nil
        })

        // err is not null only if there was a timeout in the exponential backoff (ErrWaitTimeout)
        if err != nil {
                return nil, lastErr
        }

        return client, nil
}

func EnsureSecret(secret *apiv1.Secret, verbose string) (*apiv1.Secret, error) {
        kubeClient, err := createApiserverClient()
        s, err := kubeClient.CoreV1().Secrets(secret.Namespace).Create(secret)
        if err != nil {
                if k8sErrors.IsAlreadyExists(err) {
                        if verbose == "true" {
                                fmt.Println("Secret" ,secret.Name," already exist in namespace ",secret.Namespace,", updating")
                        }
                        return kubeClient.CoreV1().Secrets(secret.Namespace).Update(secret)
                }
                fmt.Println("could not execute due to error:", err)
                return nil, err
        }
        fmt.Println("Secret" ,secret.Name," created in namespace ",secret.Namespace)
        return s, nil
}

func saveFileToSecretMapFromS3(s *s3.S3, bucket string, obj s3.Object, filename string, wg *sync.WaitGroup) (error) {
        defer wg.Done()
        wg.Add(1)
        params := &s3.GetObjectInput{
                Bucket: aws.String(bucket),   // Required
                Key:    aws.String(*obj.Key), // Required
        }
        resp, err := s.GetObject(params)
        if err != nil {
                return err
        }

        data, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                fmt.Println("Could not download file from s3 due to:", err)
                return err
        }

        r_secret_name, err := regexp.Compile("^(.+)\\.(?:key|crt)$")

        match_key, err := regexp.MatchString("^(.+)\\.key$", filename)
        match_cert, err := regexp.MatchString("^(.+)\\.crt$", filename)

        if match_key {
                if secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] == nil {
                        secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] = make(map[string][]byte)
                }
                secret_hash[r_secret_name.FindStringSubmatch(filename)[1]]["key"] = data
        }
        if match_cert {
                if secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] == nil {
                        secret_hash[r_secret_name.FindStringSubmatch(filename)[1]] = make(map[string][]byte)
                }
                secret_hash[r_secret_name.FindStringSubmatch(filename)[1]]["cert"] = data
        }

        return err
}

func saveSecretMapToK8s(sh map[string]map[string][]byte, namespace string, label_name string, label_value string, verbose string) (err error) {
        fmt.Println("Start creating secrets in Kubernetes")

        for secret_domain := range sh {
                var err_message string
                if sh[secret_domain]["cert"] == nil {
                        err_message = "ERROR: " + secret_domain + " do not have cert file"
                        fmt.Println(err_message)
                } else if sh[secret_domain]["key"] == nil {
                        err_message = "ERROR: " + secret_domain + " do not have key file"
                        fmt.Println(err_message)
                } else {
                        var labels map[string]string
                        labels = make (map[string]string)
                        labels["cert_domain"] = secret_domain
                        labels["created_by"] = "s3sync"
                        if label_name != "" && label_value != "" {
                                labels[label_name] = label_value
                        }

                        _, err = EnsureSecret(&apiv1.Secret{
                                ObjectMeta: metav1.ObjectMeta{
                                        Name:      secret_domain,
                                        Namespace: namespace,
                                        Labels: labels,
                                },
                                Data: map[string][]byte{
                                        apiv1.TLSCertKey:       sh[secret_domain]["cert"],
                                        apiv1.TLSPrivateKeyKey: sh[secret_domain]["key"],
                                },
                        }, verbose)

                        if err != nil {
                                fmt.Println(err.Error())
                                return err
                        }
                }
        }
        return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

const (
	TEMPLATE_DIR_DEFAULT_NAME = "templates"
	TEMPLATE_FILE_EXAMPLE     = "page.html"
	REPOSITORY_DIR            = "syaro"
)

var (
	wikiRoot    string
	templateDir string
)

func main() {
	// print welcome message
	fmt.Println("===== Syaro Wiki Server =====")
	fmt.Println("Starting...")

	templateDir = findTemplateDir("")
	if templateDir == "" {
		fmt.Println("Error: Can't find template dir.")
		return
	}
	fmt.Println("Template dir:", templateDir)

	// set repository root
	if len(os.Args) == 2 {
		wikiRoot = os.Args[1]
	} else {
		wikiRoot = "./"
	}
	fmt.Println("WikiRoot:", wikiRoot)

	fmt.Println("Server started. Waiting connection on port :8080")
	fmt.Println()

	// set http handler
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}

// findTemplateDir finds template directory contains html, css, etc...
// dir is directory specified by user as template dir.
// This search several directory and return right dir.
// If not found, return empty string.
func findTemplateDir(dir string) string {
	// if template dir is specified by user, search this dir
	if dir != "" {
		_, err := os.Stat(path.Join(dir, TEMPLATE_FILE_EXAMPLE))
		// if directory isn't exist
		if err != nil {
			fmt.Println("Error: Can't find template file dir specified in argument")
			return ""
		}
		return dir
	} else { // directory isn't specified by user so search it by myself
		// first, $GOROOT/src/...
		_, err := os.Stat(path.Join(os.Getenv("GOPATH"), "src", REPOSITORY_DIR,
			TEMPLATE_DIR_DEFAULT_NAME, TEMPLATE_FILE_EXAMPLE))
		if err == nil {
			return path.Join(os.Getenv("GOPATH"), "src", REPOSITORY_DIR,
				TEMPLATE_DIR_DEFAULT_NAME)
		}

		// second, /usr/local/share/syaro
		_, err = os.Stat(path.Join("/usr/local/share/syaro",
			TEMPLATE_DIR_DEFAULT_NAME, TEMPLATE_FILE_EXAMPLE))
		if err == nil {
			return path.Join("/usr/local/share/syaro", TEMPLATE_DIR_DEFAULT_NAME)
		}

		// can't find template dir
		return ""
	}
}

// handler is basic http request handler
func handler(rw http.ResponseWriter, req *http.Request) {
	fmt.Printf("Response received (%s)\n", req.URL.Path)

	filePath := req.URL.Path

	// FIXME wrong method (ext isn't always .md)
	if path.Ext(filePath) == "" {
		filePath += ".md"
	}

	if path.Ext(filePath) == ".md" {
		fmt.Println("Rendering page...")
		filePath = path.Clean(wikiRoot + filePath)

		// load md file
		page, err := LoadPage(filePath)
		if err != nil { // file not exist, so create new page
			fmt.Println("File not exist, create new page")
			page, err = NewPage(filePath)
			if err != nil { // strange error
				http.Error(rw, err.Error(), http.StatusInternalServerError)
			}
		}

		// render html
		err = page.Render(rw)
		if err != nil {
			fmt.Println("rendering error!", err.Error())
			http.Error(rw, err.Error(), http.StatusInternalServerError)
		}

	} else if ext := path.Ext(filePath); ext == ".css" || ext == ".js" {
		f, err := os.Open(path.Clean(templateDir + filePath))
		if err != nil {
			fmt.Println("not found")
			http.Error(rw, err.Error(), http.StatusNotFound)
		}
		b, err := ioutil.ReadAll(f)
		if err != nil {
			fmt.Println("can't read")
			http.Error(rw, err.Error(), http.StatusInternalServerError)
		}

		fmt.Fprint(rw, string(b))
	}
}
<commit_msg>add error handling of http<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

const (
	TEMPLATE_DIR_DEFAULT_NAME = "templates"
	TEMPLATE_FILE_EXAMPLE     = "page.html"
	REPOSITORY_DIR            = "syaro"
)

var (
	wikiRoot    string
	templateDir string
)

func main() {
	// print welcome message
	fmt.Println("===== Syaro Wiki Server =====")
	fmt.Println("Starting...")

	templateDir = findTemplateDir("")
	if templateDir == "" {
		fmt.Println("Error: Can't find template dir.")
		return
	}
	fmt.Println("Template dir:", templateDir)

	// set repository root
	if len(os.Args) == 2 {
		wikiRoot = os.Args[1]
	} else {
		wikiRoot = "./"
	}
	fmt.Println("WikiRoot:", wikiRoot)

	fmt.Println("Server started. Waiting connection on port :8080")
	fmt.Println()

	// set http handler
	http.HandleFunc("/", handler)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Println("Error:", err)
	}
}

// findTemplateDir finds template directory contains html, css, etc...
// dir is directory specified by user as template dir.
// This search several directory and return right dir.
// If not found, return empty string.
func findTemplateDir(dir string) string {
	// if template dir is specified by user, search this dir
	if dir != "" {
		_, err := os.Stat(path.Join(dir, TEMPLATE_FILE_EXAMPLE))
		// if directory isn't exist
		if err != nil {
			fmt.Println("Error: Can't find template file dir specified in argument")
			return ""
		}
		return dir
	} else { // directory isn't specified by user so search it by myself
		// first, $GOROOT/src/...
		_, err := os.Stat(path.Join(os.Getenv("GOPATH"), "src", REPOSITORY_DIR,
			TEMPLATE_DIR_DEFAULT_NAME, TEMPLATE_FILE_EXAMPLE))
		if err == nil {
			return path.Join(os.Getenv("GOPATH"), "src", REPOSITORY_DIR,
				TEMPLATE_DIR_DEFAULT_NAME)
		}

		// second, /usr/local/share/syaro
		_, err = os.Stat(path.Join("/usr/local/share/syaro",
			TEMPLATE_DIR_DEFAULT_NAME, TEMPLATE_FILE_EXAMPLE))
		if err == nil {
			return path.Join("/usr/local/share/syaro", TEMPLATE_DIR_DEFAULT_NAME)
		}

		// can't find template dir
		return ""
	}
}

// handler is basic http request handler
func handler(rw http.ResponseWriter, req *http.Request) {
	fmt.Printf("Response received (%s)\n", req.URL.Path)

	filePath := req.URL.Path

	// FIXME wrong method (ext isn't always .md)
	if path.Ext(filePath) == "" {
		filePath += ".md"
	}

	if path.Ext(filePath) == ".md" {
		fmt.Println("Rendering page...")
		filePath = path.Clean(wikiRoot + filePath)

		// load md file
		page, err := LoadPage(filePath)
		if err != nil { // file not exist, so create new page
			fmt.Println("File not exist, create new page")
			page, err = NewPage(filePath)
			if err != nil { // strange error
				fmt.Println("Error:", err)
				http.Error(rw, err.Error(), http.StatusInternalServerError)
			}
		}

		// render html
		err = page.Render(rw)
		if err != nil {
			fmt.Println("rendering error!", err.Error())
			http.Error(rw, err.Error(), http.StatusInternalServerError)
		}

	} else if ext := path.Ext(filePath); ext == ".css" || ext == ".js" {
		f, err := os.Open(path.Clean(templateDir + filePath))
		if err != nil {
			fmt.Println("not found")
			http.Error(rw, err.Error(), http.StatusNotFound)
		}
		b, err := ioutil.ReadAll(f)
		if err != nil {
			fmt.Println("can't read")
			http.Error(rw, err.Error(), http.StatusInternalServerError)
		}

		fmt.Fprint(rw, string(b))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcrpcclient"
	"github.com/btcsuite/btcutil"
	"github.com/freeusd/solebtc/handlers/v1"
	"github.com/freeusd/solebtc/middlewares"
	"github.com/freeusd/solebtc/models"
	"github.com/freeusd/solebtc/services/cache"
	"github.com/freeusd/solebtc/services/cache/memory"
	"github.com/freeusd/solebtc/services/hub"
	"github.com/freeusd/solebtc/services/hub/list"
	"github.com/freeusd/solebtc/services/mail"
	"github.com/freeusd/solebtc/services/mail/mandrill"
	"github.com/freeusd/solebtc/services/storage"
	"github.com/freeusd/solebtc/services/storage/mysql"
	"github.com/gin-gonic/gin"
	"github.com/robfig/cron"
	grayloghook "github.com/yumimobi/logrus-graylog2-hook"
)

var (
	logger               = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Llongfile)
	mailer               mail.Mailer
	store                storage.Storage
	memoryCache          cache.Cache
	connsHub             hub.Hub
	coinClient           *btcrpcclient.Client
	addressToReceiveCoin btcutil.Address
)

func init() {
	// ORDER MATTERs

	// configuration
	initConfig()

	// logging
	l := must(logrus.ParseLevel(config.Log.Level)).(logrus.Level)
	logrus.SetLevel(l)
	logrus.SetOutput(os.Stdout)

	// logging hooks
	graylogHookLevelThreshold := must(logrus.ParseLevel(config.Log.Graylog.Level)).(logrus.Level)
	graylogHook := must(grayloghook.New(config.Log.Graylog.Address, config.Log.Graylog.Facility, graylogHookLevelThreshold)).(logrus.Hook)
	logrus.AddHook(graylogHook)

	// connection hub
	connsHub = list.New()

	// storage
	s := mysql.New(config.DB.DataSourceName)
	s.SetMaxOpenConns(config.DB.MaxOpenConns)
	s.SetMaxIdleConns(config.DB.MaxIdleConns)
	store = s

	// cache
	memoryCache = memory.New(config.Cache.NumCachedIncomes)
	setCacheFromStore(memoryCache, store)

	// coin client
	initCoinClient()

	// cronjob
	initCronjob()

	// mailer
	mailer = mandrill.New(config.Mandrill.Key, config.Mandrill.FromEmail, config.Mandrill.FromName)
}

func main() {
	gin.SetMode(config.HTTP.Mode)
	router := gin.New()

	// middlewares
	authRequired := middlewares.AuthRequired(store.GetAuthToken, config.AuthToken.Lifetime)

	// globally use middlewares
	router.Use(
		middlewares.RecoveryWithWriter(os.Stderr),
		middlewares.Logger(),
		middlewares.CORS(),
		gin.ErrorLogger(),
	)

	// version 1 api endpoints
	v1Endpoints := router.Group("/v1")

	// user endpoints
	v1UserEndpoints := v1Endpoints.Group("/users")
	v1UserEndpoints.GET("", authRequired, v1.UserInfo(store.GetUserByID))
	v1UserEndpoints.POST("", v1.Signup(validateAddress, store.CreateUser, store.GetUserByID))
	v1UserEndpoints.PUT("/:id/status", v1.VerifyEmail(store.GetSessionByToken, store.GetUserByID, store.UpdateUserStatus))
	v1UserEndpoints.GET("/referees", authRequired, v1.RefereeList(store.GetRefereesSince, store.GetRefereesUntil))

	// auth token endpoints
	v1AuthTokenEndpoints := v1Endpoints.Group("/auth_tokens")
	v1AuthTokenEndpoints.POST("", v1.Login(store.GetUserByEmail, store.CreateAuthToken))
	v1AuthTokenEndpoints.DELETE("", authRequired, v1.Logout(store.DeleteAuthToken))

	// session endpoints
	v1SessionEndpoints := v1Endpoints.Group("/sessions")
	emailVerificationTemplate := template.Must(template.ParseFiles(config.Template.EmailVerificationTemplate))
	v1SessionEndpoints.POST("", authRequired,
		v1.RequestVerifyEmail(store.GetUserByID, store.UpsertSession, mailer.SendEmail, emailVerificationTemplate),
	)

	// income endpoints
	v1IncomeEndpoints := v1Endpoints.Group("/incomes", authRequired)
	v1IncomeEndpoints.POST("/rewards",
		v1.GetReward(store.GetUserByID,
			memoryCache.GetLatestTotalReward,
			memoryCache.GetLatestConfig,
			memoryCache.GetRewardRatesByType,
			createRewardIncome,
			memoryCache.InsertIncome,
			connsHub.Broadcast),
	)
	v1IncomeEndpoints.GET("/rewards", v1.RewardList(store.GetRewardIncomesSince, store.GetRewardIncomesUntil))
	v1IncomeEndpoints.GET("/rewards/referees/:referee_id", v1.RefereeRewardList(store.GetUserByID, store.GetRewardIncomesSince, store.GetRewardIncomesUntil))

	// websocket endpoint
	v1Endpoints.GET("/websocket",
		v1.Websocket(
			connsHub.Len,
			memoryCache.GetLatestConfig,
			memoryCache.GetLatestIncomes,
			connsHub.Broadcast,
			hub.WrapPutWebsocketConn(connsHub.PutConn)),
	)

	logrus.WithFields(logrus.Fields{
		"http_address": config.HTTP.Address,
		"coin_address": addressToReceiveCoin.String(),
	}).Info("service up")
	if err := router.Run(config.HTTP.Address); err != nil {
		logrus.WithError(err).Fatal("failed to start service")
	}
}

func createRewardIncome(income models.Income, now time.Time) error {
	if err := store.CreateRewardIncome(income, now); err != nil {
		return err
	}

	totalReward := income.Income
	if income.RefererID > 0 {
		totalReward += income.RefererIncome
	}
	memoryCache.IncrementTotalReward(now, totalReward)

	return nil
}

func setCacheFromStore(c cache.Cache, s storage.Storage) {
	c.SetLatestConfig(must(s.GetLatestConfig()).(models.Config))

	lessRates := must(s.GetRewardRatesByType(models.RewardRateTypeLess)).([]models.RewardRate)
	c.SetRewardRates(models.RewardRateTypeLess, lessRates)

	moreRates := must(s.GetRewardRatesByType(models.RewardRateTypeMore)).([]models.RewardRate)
	c.SetRewardRates(models.RewardRateTypeMore, moreRates)
}

func initCronjob() {
	c := cron.New()
	must(nil, c.AddFunc("@every 1m",
		safeFuncWrapper(func() {
			setCacheFromStore(memoryCache, store)
		}),
	))
	must(nil, c.AddFunc("@daily", createWithdrawal))
	c.Start()
}

// automatically create withdrawal
func createWithdrawal() {
	users, err := store.GetWithdrawableUsers()
	if err != nil {
		logger.Printf("get withdrawable users error: %v\n", err)
		logrus.WithError(err).Error("创建提现")
		return
	}

	total := 0.0
	transactionFee := 0.0001
	for i := range users {
		total += users[i].Balance + transactionFee
	}
	logrus.WithField("提现总额", total).Info("创建提现")

	f := func(users []models.User, handler func(err error, u models.User)) {
		for i := range users {
			handler(store.CreateWithdrawal(models.Withdrawal{
				UserID:  users[i].ID,
				Amount:  users[i].Balance,
				Address: users[i].Address,
			}), users[i])
		}
	}

	// create withdrawal, move errored ones into retry queue
	retryUsers := []models.User{}
	f(users, func(err error, u models.User) {
		if err != nil {
			retryUsers = append(retryUsers, u)
		}
	})

	// retry with error output
	f(retryUsers, func(err error, u models.User) {
		if err != nil {
			logger.Printf("create withdrawal for user %v error: %v\n", u, err)
			logrus.WithFields(logrus.Fields{
				"email":   u.Email,
				"address": u.Address,
				"balance": u.Balance,
				"status":  u.Status,
				"error":   err,
			}).Error("创建提现")
		}
	})
}

func initCoinClient() {
	config := &btcrpcclient.ConnConfig{
		Host:         "localhost:8332",
		User:         "rpcuser",
		Pass:         "rpcpass",
		HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
		DisableTLS:   true, // Bitcoin core does not provide TLS by default
	}
	coinClient = must(btcrpcclient.New(config, nil)).(*btcrpcclient.Client)
	addressToReceiveCoin = must(coinClient.GetAccountAddress("")).(btcutil.Address)
}

func validateAddress(address string) (bool, error) {
	addr, err := parseAddress(address)
	if err != nil {
		return false, err
	}

	result, err := coinClient.ValidateAddress(addr)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"address": address,
			"error":   err,
		}).Debug("failed to validate address")
		return false, err
	}

	return result.IsValid, nil
}

func parseAddress(address string) (btcutil.Address, error) {
	addr, err := btcutil.DecodeAddress(address, &chaincfg.MainNetParams)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"address": address,
			"error":   err,
		}).Debug("failed to parse address")
		return nil, err
	}

	return addr, nil
}

// fail fast on initialization
func must(i interface{}, err error) interface{} {
	if err != nil {
		panic(err)
	}

	return i
}

// wrap a function with recover
func safeFuncWrapper(f func()) func() {
	return func() {
		defer func() {
			if err := recover(); err != nil {
				buf := make([]byte, 4096)
				n := runtime.Stack(buf, false)
				logrus.WithFields(logrus.Fields{
					"error": err,
					"stack": string(buf[:n]),
				}).Error("panic")
				logger.Printf("%v\n%s\n", err, buf)
			}
		}()
		f()
	}
}
<commit_msg>Remove parseAddress function<commit_after>package main

import (
	"html/template"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcrpcclient"
	"github.com/btcsuite/btcutil"
	"github.com/freeusd/solebtc/handlers/v1"
	"github.com/freeusd/solebtc/middlewares"
	"github.com/freeusd/solebtc/models"
	"github.com/freeusd/solebtc/services/cache"
	"github.com/freeusd/solebtc/services/cache/memory"
	"github.com/freeusd/solebtc/services/hub"
	"github.com/freeusd/solebtc/services/hub/list"
	"github.com/freeusd/solebtc/services/mail"
	"github.com/freeusd/solebtc/services/mail/mandrill"
	"github.com/freeusd/solebtc/services/storage"
	"github.com/freeusd/solebtc/services/storage/mysql"
	"github.com/gin-gonic/gin"
	"github.com/robfig/cron"
	grayloghook "github.com/yumimobi/logrus-graylog2-hook"
)

var (
	logger               = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Llongfile)
	mailer               mail.Mailer
	store                storage.Storage
	memoryCache          cache.Cache
	connsHub             hub.Hub
	coinClient           *btcrpcclient.Client
	addressToReceiveCoin btcutil.Address
)

func init() {
	// ORDER MATTERs

	// configuration
	initConfig()

	// logging
	l := must(logrus.ParseLevel(config.Log.Level)).(logrus.Level)
	logrus.SetLevel(l)
	logrus.SetOutput(os.Stdout)

	// logging hooks
	graylogHookLevelThreshold := must(logrus.ParseLevel(config.Log.Graylog.Level)).(logrus.Level)
	graylogHook := must(grayloghook.New(config.Log.Graylog.Address, config.Log.Graylog.Facility, graylogHookLevelThreshold)).(logrus.Hook)
	logrus.AddHook(graylogHook)

	// connection hub
	connsHub = list.New()

	// storage
	s := mysql.New(config.DB.DataSourceName)
	s.SetMaxOpenConns(config.DB.MaxOpenConns)
	s.SetMaxIdleConns(config.DB.MaxIdleConns)
	store = s

	// cache
	memoryCache = memory.New(config.Cache.NumCachedIncomes)
	setCacheFromStore(memoryCache, store)

	// coin client
	initCoinClient()

	// cronjob
	initCronjob()

	// mailer
	mailer = mandrill.New(config.Mandrill.Key, config.Mandrill.FromEmail, config.Mandrill.FromName)
}

func main() {
	gin.SetMode(config.HTTP.Mode)
	router := gin.New()

	// middlewares
	authRequired := middlewares.AuthRequired(store.GetAuthToken, config.AuthToken.Lifetime)

	// globally use middlewares
	router.Use(
		middlewares.RecoveryWithWriter(os.Stderr),
		middlewares.Logger(),
		middlewares.CORS(),
		gin.ErrorLogger(),
	)

	// version 1 api endpoints
	v1Endpoints := router.Group("/v1")

	// user endpoints
	v1UserEndpoints := v1Endpoints.Group("/users")
	v1UserEndpoints.GET("", authRequired, v1.UserInfo(store.GetUserByID))
	v1UserEndpoints.POST("", v1.Signup(validateAddress, store.CreateUser, store.GetUserByID))
	v1UserEndpoints.PUT("/:id/status", v1.VerifyEmail(store.GetSessionByToken, store.GetUserByID, store.UpdateUserStatus))
	v1UserEndpoints.GET("/referees", authRequired, v1.RefereeList(store.GetRefereesSince, store.GetRefereesUntil))

	// auth token endpoints
	v1AuthTokenEndpoints := v1Endpoints.Group("/auth_tokens")
	v1AuthTokenEndpoints.POST("", v1.Login(store.GetUserByEmail, store.CreateAuthToken))
	v1AuthTokenEndpoints.DELETE("", authRequired, v1.Logout(store.DeleteAuthToken))

	// session endpoints
	v1SessionEndpoints := v1Endpoints.Group("/sessions")
	emailVerificationTemplate := template.Must(template.ParseFiles(config.Template.EmailVerificationTemplate))
	v1SessionEndpoints.POST("", authRequired,
		v1.RequestVerifyEmail(store.GetUserByID, store.UpsertSession, mailer.SendEmail, emailVerificationTemplate),
	)

	// income endpoints
	v1IncomeEndpoints := v1Endpoints.Group("/incomes", authRequired)
	v1IncomeEndpoints.POST("/rewards",
		v1.GetReward(store.GetUserByID,
			memoryCache.GetLatestTotalReward,
			memoryCache.GetLatestConfig,
			memoryCache.GetRewardRatesByType,
			createRewardIncome,
			memoryCache.InsertIncome,
			connsHub.Broadcast),
	)
	v1IncomeEndpoints.GET("/rewards", v1.RewardList(store.GetRewardIncomesSince, store.GetRewardIncomesUntil))
	v1IncomeEndpoints.GET("/rewards/referees/:referee_id", v1.RefereeRewardList(store.GetUserByID, store.GetRewardIncomesSince, store.GetRewardIncomesUntil))

	// websocket endpoint
	v1Endpoints.GET("/websocket",
		v1.Websocket(
			connsHub.Len,
			memoryCache.GetLatestConfig,
			memoryCache.GetLatestIncomes,
			connsHub.Broadcast,
			hub.WrapPutWebsocketConn(connsHub.PutConn)),
	)

	logrus.WithFields(logrus.Fields{
		"http_address": config.HTTP.Address,
		"coin_address": addressToReceiveCoin.String(),
	}).Info("service up")
	if err := router.Run(config.HTTP.Address); err != nil {
		logrus.WithError(err).Fatal("failed to start service")
	}
}

func createRewardIncome(income models.Income, now time.Time) error {
	if err := store.CreateRewardIncome(income, now); err != nil {
		return err
	}

	totalReward := income.Income
	if income.RefererID > 0 {
		totalReward += income.RefererIncome
	}
	memoryCache.IncrementTotalReward(now, totalReward)

	return nil
}

func setCacheFromStore(c cache.Cache, s storage.Storage) {
	c.SetLatestConfig(must(s.GetLatestConfig()).(models.Config))

	lessRates := must(s.GetRewardRatesByType(models.RewardRateTypeLess)).([]models.RewardRate)
	c.SetRewardRates(models.RewardRateTypeLess, lessRates)

	moreRates := must(s.GetRewardRatesByType(models.RewardRateTypeMore)).([]models.RewardRate)
	c.SetRewardRates(models.RewardRateTypeMore, moreRates)
}

func initCronjob() {
	c := cron.New()
	must(nil, c.AddFunc("@every 1m",
		safeFuncWrapper(func() {
			setCacheFromStore(memoryCache, store)
		}),
	))
	must(nil, c.AddFunc("@daily", createWithdrawal))
	c.Start()
}

// automatically create withdrawal
func createWithdrawal() {
	users, err := store.GetWithdrawableUsers()
	if err != nil {
		logger.Printf("get withdrawable users error: %v\n", err)
		logrus.WithError(err).Error("创建提现")
		return
	}

	total := 0.0
	transactionFee := 0.0001
	for i := range users {
		total += users[i].Balance + transactionFee
	}
	logrus.WithField("提现总额", total).Info("创建提现")

	f := func(users []models.User, handler func(err error, u models.User)) {
		for i := range users {
			handler(store.CreateWithdrawal(models.Withdrawal{
				UserID:  users[i].ID,
				Amount:  users[i].Balance,
				Address: users[i].Address,
			}), users[i])
		}
	}

	// create withdrawal, move errored ones into retry queue
	retryUsers := []models.User{}
	f(users, func(err error, u models.User) {
		if err != nil {
			retryUsers = append(retryUsers, u)
		}
	})

	// retry with error output
	f(retryUsers, func(err error, u models.User) {
		if err != nil {
			logger.Printf("create withdrawal for user %v error: %v\n", u, err)
			logrus.WithFields(logrus.Fields{
				"email":   u.Email,
				"address": u.Address,
				"balance": u.Balance,
				"status":  u.Status,
				"error":   err,
			}).Error("创建提现")
		}
	})
}

func initCoinClient() {
	config := &btcrpcclient.ConnConfig{
		Host:         "localhost:8332",
		User:         "rpcuser",
		Pass:         "rpcpass",
		HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
		DisableTLS:   true, // Bitcoin core does not provide TLS by default
	}
	coinClient = must(btcrpcclient.New(config, nil)).(*btcrpcclient.Client)
	addressToReceiveCoin = must(coinClient.GetAccountAddress("")).(btcutil.Address)
}

func validateAddress(address string) (bool, error) {
	addr, err := btcutil.DecodeAddress(address, &chaincfg.MainNetParams)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"address": address,
			"error":   err,
		}).Error("failed to parse address")
		return false, err
	}

	result, err := coinClient.ValidateAddress(addr)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"address": address,
			"error":   err,
		}).Debug("failed to validate address")
		return false, err
	}

	return result.IsValid, nil
}

	if err != nil {
		logrus.WithFields(logrus.Fields{
	}

	return addr, nil
}

// fail fast on initialization
func must(i interface{}, err error) interface{} {
	if err != nil {
		panic(err)
	}

	return i
}

// wrap a function with recover
func safeFuncWrapper(f func()) func() {
	return func() {
		defer func() {
			if err := recover(); err != nil {
				buf := make([]byte, 4096)
				n := runtime.Stack(buf, false)
				logrus.WithFields(logrus.Fields{
					"error": err,
					"stack": string(buf[:n]),
				}).Error("panic")
				logger.Printf("%v\n%s\n", err, buf)
			}
		}()
		f()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/mikesimons/earl"
)

func main() {
	opts := &PacYakOpts{}

	flag.StringVar(&opts.ListenAddr, "addr", "127.0.0.1:8080", "Proxy listen address")
	flag.StringVar(&opts.PingCheckHost, "pinghost", "", "Availability check host")
	flag.StringVar(&opts.PacProxy, "pacproxy", "", "Proxy for fetching PAC file")
	flag.StringVar(&opts.LogLevelStr, "loglevel", "info", "Log level: debug, info, warn, error")
	flag.Parse()

	tmp, err := logrus.ParseLevel(opts.LogLevelStr)
	if err != nil {
		fmt.Printf("Invalid log level '%s'. Valid levels are: debug, info, warn, error")
		os.Exit(1)
	}
	opts.LogLevel = tmp

	args := flag.Args()

	if len(args) < 1 {
		fmt.Printf("Usage: pacyak <pac-file>\n")
		os.Exit(1)
	}

	opts.PacFile = args[0]

	url := earl.Parse(opts.PingCheckHost)
	if url.Host == "" {
		url = earl.Parse(opts.PacFile)
		if url.Host == "" {
			fmt.Printf("-pinghost is required if pac-file is not a URL")
			os.Exit(1)
		}
	}

	opts.PingCheckHost = url.Host

	Run(opts)
}
<commit_msg>Swaps golang flag for urface cli for better control of output<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/mikesimons/earl"
	"gopkg.in/urfave/cli.v1"
)

func main() {
	app := cli.NewApp()
	app.Name = "pacyak"
	app.Version = "1.0"

	cli.AppHelpTemplate = `{{.Name}} version {{.Version}} - For the unfortunate souls stuck behind corporate proxies

{{.HelpName}} [options] <pac location>

OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}
`

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "listen",
			Usage: "Pacyak will listen for requests to this address",
			Value: "127.0.0.1:8080",
		},
		cli.StringFlag{
			Name:  "ping-host",
			Usage: "Host only accessible from within your proxy. Required if PAC location is a file. (default: Host of PAC location)",
		},
		cli.StringFlag{
			Name:  "pac-proxy",
			Usage: "Proxy for pac file. (Only necessary if your PAC location requires a proxy to be set)",
		},
		cli.StringFlag{
			Name:  "log-level",
			Usage: "Log level (debug, info, warn, error)",
			Value: "info",
		},
	}

	app.Action = func(c *cli.Context) error {
		opts := &PacYakOpts{}

		tmp, err := logrus.ParseLevel(c.String("log-level"))
		if err != nil {
			return cli.NewExitError(fmt.Sprintf("Invalid log level '%s'. Valid levels are: debug, info, warn, error", tmp), 1)
		}
		opts.LogLevel = tmp

		if c.NArg() < 1 {
			cli.ShowAppHelp(c)
			os.Exit(0)
		}

		opts.PacFile = c.Args().Get(0)

		url := earl.Parse(opts.PacFile)
		if url.Scheme == "" || url.Scheme == "file" {
			if _, err := os.Stat(opts.PacFile); os.IsNotExist(err) {
				return cli.NewExitError("PAC location is not a valid URL and file does not exist. If it is a URL, please specify a protocol (e.g. http://)", 1)
			}
		}

		opts.PingCheckHost = earl.Parse(c.String("ping-host")).Host
		if c.String("ping-host") == "" {
			url = earl.Parse(opts.PacFile)
			if url.Scheme == "" || url.Scheme == "file" {
				return cli.NewExitError("--ping-host is required if PAC location is not a URL", 1)
			}
			opts.PingCheckHost = url.Host
		}

		opts.PacProxy = c.String("pac-proxy")
		opts.ListenAddr = c.String("listen")

		Run(opts)
		return nil
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/elodina/stack-deploy/command"
	"github.com/elodina/stack-deploy/framework"
	"github.com/elodina/stack-deploy/mesosrunners"
	"github.com/elodina/stack-deploy/runners"
	"github.com/mitchellh/cli"
)

func main() {
	cli := cli.NewCLI("stack-deploy", "0.3.4.0")
	cli.Args = os.Args[1:]
	cli.Commands = commands()

	exitCode, err := cli.Run()
	if err != nil {
		fmt.Printf("Error exiting CLI: %s\n", err)
		os.Exit(1)
	}

	os.Exit(exitCode)
}

func commands() map[string]cli.CommandFactory {
	return map[string]cli.CommandFactory{
		"server": func() (cli.Command, error) {
			return command.NewServerCommand(taskRunners, mesosTaskRunners), nil
		},
		"ping": func() (cli.Command, error) {
			return new(command.PingCommand), nil
		},
		"list": func() (cli.Command, error) {
			return new(command.ListCommand), nil
		},
		"show": func() (cli.Command, error) {
			return new(command.ShowCommand), nil
		},
		"run": func() (cli.Command, error) {
			return new(command.RunCommand), nil
		},
		"add": func() (cli.Command, error) {
			return new(command.AddStackCommand), nil
		},
		"remove": func() (cli.Command, error) {
			return new(command.RemoveStackCommand), nil
		},
		"adduser": func() (cli.Command, error) {
			return new(command.AddUserCommand), nil
		},
		"refreshtoken": func() (cli.Command, error) {
			return new(command.RefreshTokenCommand), nil
		},
		"addlayer": func() (cli.Command, error) {
			return new(command.AddLayerCommand), nil
		},
	}
}

// Register your custom task runners here
var taskRunners map[string]framework.TaskRunner = map[string]framework.TaskRunner{
	"kafka-mesos-0.9.x":           new(runners.KafkaTaskRunner),
	"exhibitor-mesos-0.1.x":       new(runners.ExhibitorTaskRunner),
	"dse-mesos-0.1.x":             new(runners.DSETaskRunner),
	"dse-mesos-0.2.x":             new(runners.DSE02xTaskRunner),
	"statsd-mesos-0.1.x":          new(runners.StatsdTaskRunner),
	"syslog-mesos-0.1.x":          new(runners.SyslogTaskRunner),
	"zipkin-mesos-0.1.x":          new(runners.ZipkinTaskRunner),
	"go-kafka-client-mesos-0.3.x": new(runners.GoKafkaClientTaskRunner),
}

var mesosTaskRunners map[string]framework.MesosTaskRunner = map[string]framework.MesosTaskRunner{
	"run-once": mesosrunners.NewRunOnceRunner(),
}
<commit_msg>storage flag fix<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/elodina/stack-deploy/command"
	"github.com/elodina/stack-deploy/framework"
	"github.com/elodina/stack-deploy/mesosrunners"
	"github.com/elodina/stack-deploy/runners"
	"github.com/mitchellh/cli"
)

func main() {
	cli := cli.NewCLI("stack-deploy", "0.3.4.1")
	cli.Args = os.Args[1:]
	cli.Commands = commands()

	exitCode, err := cli.Run()
	if err != nil {
		fmt.Printf("Error exiting CLI: %s\n", err)
		os.Exit(1)
	}

	os.Exit(exitCode)
}

func commands() map[string]cli.CommandFactory {
	return map[string]cli.CommandFactory{
		"server": func() (cli.Command, error) {
			return command.NewServerCommand(taskRunners, mesosTaskRunners), nil
		},
		"ping": func() (cli.Command, error) {
			return new(command.PingCommand), nil
		},
		"list": func() (cli.Command, error) {
			return new(command.ListCommand), nil
		},
		"show": func() (cli.Command, error) {
			return new(command.ShowCommand), nil
		},
		"run": func() (cli.Command, error) {
			return new(command.RunCommand), nil
		},
		"add": func() (cli.Command, error) {
			return new(command.AddStackCommand), nil
		},
		"remove": func() (cli.Command, error) {
			return new(command.RemoveStackCommand), nil
		},
		"adduser": func() (cli.Command, error) {
			return new(command.AddUserCommand), nil
		},
		"refreshtoken": func() (cli.Command, error) {
			return new(command.RefreshTokenCommand), nil
		},
		"addlayer": func() (cli.Command, error) {
			return new(command.AddLayerCommand), nil
		},
	}
}

// Register your custom task runners here
var taskRunners map[string]framework.TaskRunner = map[string]framework.TaskRunner{
	"kafka-mesos-0.9.x":           new(runners.KafkaTaskRunner),
	"exhibitor-mesos-0.1.x":       new(runners.ExhibitorTaskRunner),
	"dse-mesos-0.1.x":             new(runners.DSETaskRunner),
	"dse-mesos-0.2.x":             new(runners.DSE02xTaskRunner),
	"statsd-mesos-0.1.x":          new(runners.StatsdTaskRunner),
	"syslog-mesos-0.1.x":          new(runners.SyslogTaskRunner),
	"zipkin-mesos-0.1.x":          new(runners.ZipkinTaskRunner),
	"go-kafka-client-mesos-0.3.x": new(runners.GoKafkaClientTaskRunner),
}

var mesosTaskRunners map[string]framework.MesosTaskRunner = map[string]framework.MesosTaskRunner{
	"run-once": mesosrunners.NewRunOnceRunner(),
}
<|endoftext|>


<commit_before>package model

import (
	"testing"
)

type testModel struct {
	Field1 string
	Field2 string
	Field3 string
	Model
}
type testNoValidateModel struct {
	Model
}

func newTestModel() *testModel {
	m := &testModel{}
	m.SetMessages(testMessage)
	m.SetFieldLabels(testLabels)
	return m
}
func (m *testModel) Validate() error {
	m.ValidateFieldf(m.Field1 != "", "Field1", "Field1 required.")
	m.ValidateField(m.Field2 != "", "Field2", "Field2 required.")
	m.ValidateFieldf(m.Field3 != "", "Field3", "Field3 required")

	return nil
}

var testMessage = &Messages{
	"Field1 required.": "%[1]s required.",
}

var testLabels = Messages{
	"Field1": "test field1",
}

var defaultMessage = &Messages{
	"Field2":          "default field2",
	"Field3 required": "test field3",
}

func TestModel(t *testing.T) {
	DefaultMessages = NewMessagesChain()
	Use(defaultMessage)
	m := newTestModel()
	m.SetModelID("test")
	if m.ModelID() != "test" {
		t.Fatal(m.ModelID())
	}
	MustValidate(m)
	if !m.HasError() {
		t.Error(m.HasError())
	}
	modelerrors := m.Errors()
	if len(modelerrors) != 3 {
		t.Error(modelerrors)
	}
	if modelerrors[0].Field != "Field1" {
		t.Error(modelerrors[0].Field)
	}
	if modelerrors[0].Label != "test field1" {
		t.Error(modelerrors[0].Label)
	}
	if modelerrors[0].Msg != "test field1 required." {
		t.Error(modelerrors[0].Msg)
	}
	if modelerrors[1].Field != "Field2" {
		t.Error(modelerrors[1].Field)
	}
	if modelerrors[1].Label != "Field2" {
		t.Error(modelerrors[1].Label)
	}
	if modelerrors[1].Msg != "Field2 required." {
		t.Error(modelerrors[1].Msg)
	}
	if modelerrors[2].Field != "Field3" {
		t.Error(modelerrors[2].Field)
	}
	if modelerrors[2].Label != "Field3" {
		t.Error(modelerrors[2].Label)
	}
	if modelerrors[2].Msg != "Field3 required" {
		t.Error(modelerrors[2].Msg)
	}
	m = newTestModel()
	m.Field1 = "value1"
	m.Field2 = "value2"
	m.Field3 = "value3"
	MustValidate(m)
	if m.HasError() {
		t.Error(m.HasError())
	}
	if len(m.Errors()) != 0 {
		t.Error(m.Errors())
	}

}

func TestNilMessage(t *testing.T) {
	DefaultMessages = NewMessagesChain()
	DefaultMessages.Use(defaultMessage)
	m := &testModel{}
	MustValidate(m)
	if !m.HasError() {
		t.Error(m.HasError())
	}
	modelerrors := m.Errors()
	if len(modelerrors) != 3 {
		t.Error(modelerrors)
	}
	if modelerrors[0].Field != "Field1" {
		t.Error(modelerrors[0].Field)
	}
	if modelerrors[0].Label != "Field1" {
		t.Error(modelerrors[0].Label)
	}
	if modelerrors[0].Msg != "Field1 required." {
		t.Error(modelerrors[0].Msg)
	}
	if modelerrors[1].Field != "Field2" {
		t.Error(modelerrors[1].Field)
	}
	if modelerrors[1].Label != "default field2" {
		t.Error(modelerrors[1].Label)
	}
	if modelerrors[1].Msg != "Field2 required." {
		t.Error(modelerrors[1].Msg)
	}
	if modelerrors[2].Field != "Field3" {
		t.Error(modelerrors[2].Field)
	}
	if modelerrors[2].Label != "Field3" {
		t.Error(modelerrors[2].Label)
	}
	if modelerrors[2].Msg != "test field3" {
		t.Error(modelerrors[2].Msg)
	}
}

func TestNoValidate(t *testing.T) {
	m := testNoValidateModel{}
	err := m.Validate()
	if err != ErrNoValidateMethod {
		t.Fatal(err)
	}
}
<commit_msg>update<commit_after>package model

import (
	"testing"
)

type testModel struct {
	Field1 string
	Field2 string
	Field3 string
	Model
}
type testNoValidateModel struct {
	Model
}

func newTestModel() *testModel {
	m := &testModel{}
	m.SetMessages(testMessage)
	m.SetFieldLabels(testLabels)
	return m
}
func (m *testModel) Validate() error {
	m.ValidateFieldf(m.Field1 != "", "Field1", "Field1 required.")
	m.ValidateField(m.Field2 != "", "Field2", "Field2 required.")
	m.ValidateFieldf(m.Field3 != "", "Field3", "Field3 required")

	return nil
}

var testMessage = &Messages{
	"Field1 required.": "%[1]s required.",
}

var testLabels = Messages{
	"Field1": "test field1",
}

var defaultMessage = &Messages{
	"Field2":          "default field2",
	"Field3 required": "test field3",
}

func TestModel(t *testing.T) {
	DefaultMessages = NewMessagesChain()
	Use(defaultMessage)
	m := newTestModel()
	m.SetModelID("test")
	if m.ModelID() != "test" {
		t.Fatal(m.ModelID())
	}
	MustValidate(m)
	if !m.HasError() {
		t.Error(m.HasError())
	}
	modelerrors := m.Errors()
	if len(modelerrors) != 3 {
		t.Error(modelerrors)
	}
	if modelerrors[0].Field != "Field1" {
		t.Error(modelerrors[0].Field)
	}
	if modelerrors[0].Label != "test field1" {
		t.Error(modelerrors[0].Label)
	}
	if modelerrors[0].Msg != "test field1 required." {
		t.Error(modelerrors[0].Msg)
	}
	if modelerrors[1].Field != "Field2" {
		t.Error(modelerrors[1].Field)
	}
	if modelerrors[1].Label != "Field2" {
		t.Error(modelerrors[1].Label)
	}
	if modelerrors[1].Msg != "Field2 required." {
		t.Error(modelerrors[1].Msg)
	}
	if modelerrors[2].Field != "Field3" {
		t.Error(modelerrors[2].Field)
	}
	if modelerrors[2].Label != "Field3" {
		t.Error(modelerrors[2].Label)
	}
	if modelerrors[2].Msg != "Field3 required" {
		t.Error(modelerrors[2].Msg)
	}
	m = newTestModel()
	m.Field1 = "value1"
	m.Field2 = "value2"
	m.Field3 = "value3"
	MustValidate(m)
	if m.HasError() {
		t.Error(m.HasError())
	}
	if len(m.Errors()) != 0 {
		t.Error(m.Errors())
	}

}

func TestNilMessage(t *testing.T) {
	DefaultMessages = NewMessagesChain()
	DefaultMessages.Use(defaultMessage)
	l, result := LoadMessage("notexist")
	if l != "notexist" || result != false {
		t.Fatal()
	}
	m := &testModel{}
	MustValidate(m)
	if !m.HasError() {
		t.Error(m.HasError())
	}
	modelerrors := m.Errors()
	if len(modelerrors) != 3 {
		t.Error(modelerrors)
	}
	if modelerrors[0].Field != "Field1" {
		t.Error(modelerrors[0].Field)
	}
	if modelerrors[0].Label != "Field1" {
		t.Error(modelerrors[0].Label)
	}
	if modelerrors[0].Msg != "Field1 required." {
		t.Error(modelerrors[0].Msg)
	}
	if modelerrors[1].Field != "Field2" {
		t.Error(modelerrors[1].Field)
	}
	if modelerrors[1].Label != "default field2" {
		t.Error(modelerrors[1].Label)
	}
	if modelerrors[1].Msg != "Field2 required." {
		t.Error(modelerrors[1].Msg)
	}
	if modelerrors[2].Field != "Field3" {
		t.Error(modelerrors[2].Field)
	}
	if modelerrors[2].Label != "Field3" {
		t.Error(modelerrors[2].Label)
	}
	if modelerrors[2].Msg != "test field3" {
		t.Error(modelerrors[2].Msg)
	}
}

func TestNoValidate(t *testing.T) {
	m := testNoValidateModel{}
	err := m.Validate()
	if err != ErrNoValidateMethod {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package orbit

import (
	"time"

	"github.com/robertkrimen/otto"
)

// Orbit is a node.go context.
type Orbit struct {
	// Underlying Otto instance.
	*otto.Otto
	// External runtime variables.
	Vars map[string]interface{}
	// quit
	quit chan error
	// Loop runs pending timers
	loop chan *task
	// Timeout timer
	timer *time.Timer
	// Runtime timers
	timers map[*task]*task
	// Timeout sets a timeout
	timeout time.Duration
	// Module outputs are cached for future use.
	modules map[string]otto.Value
}

type (
	// Module is a javascript module
	module func(*Orbit) (otto.Value, error)
)

var (
	// Event groups
	inits []func(*Orbit)
	exits []func(*Orbit)
	fails []func(*Orbit, error)
	// Finder loads files
	finder func(*Orbit, []string) (interface{}, string, error)
	// Modules stores registered packages
	modules = make(map[string]module)
)

// Add adds a module to the runtime
func Add(name string, item interface{}) {
	switch what := item.(type) {
	case string:
		addSource(name, what)
	case []byte:
		addSource(name, what)
	case func(*Orbit) (otto.Value, error):
		addModule(name, what)
	}
}

func Find(ctx *Orbit, files []string) (interface{}, string, error) {
	return finder(ctx, files)
}

// OnInit registers a callback for when the program starts up
func OnInit(call func(*Orbit)) {
	inits = append(inits, call)
}

// OnExit registers a callback for when the program shuts down
func OnExit(call func(*Orbit)) {
	exits = append(exits, call)
}

// OnFail registers a callback for when the program encounters and error
func OnFail(call func(*Orbit, error)) {
	fails = append(fails, call)
}

// OnFile registers a callback for finding required files
func OnFile(call func(*Orbit, []string) (interface{}, string, error)) {
	finder = call
}

// New creates a new Orbit runtime
func New(timeout time.Duration) *Orbit {

	orbit := &Orbit{
		Otto:    otto.New(),
		Vars:    make(map[string]interface{}),
		quit:    make(chan error),
		loop:    make(chan *task),
		timers:  make(map[*task]*task),
		modules: make(map[string]otto.Value),
		timeout: timeout * time.Millisecond,
	}

	orbit.Interrupt = make(chan func(), 1)

	return orbit

}

// Def sets a global variable in the runtime
func (ctx *Orbit) Def(name string, item interface{}) {
	obj, _ := ctx.Get("global")
	obj.Object().Set(name, item)
	ctx.Set(name, item)
}

// Run executes some code. Code may be a string or a byte slice.
func (ctx *Orbit) Run(name string, code interface{}) (err error) {

	defer func() {

		var ok bool

		if err, ok = recover().(error); ok {
			ctx.fail(err)
		}

		ctx.exit()

		if ctx.timer != nil {
			ctx.timer.Stop()
		}

		for timer := range ctx.timers {
			timer.timer.Stop()
			delete(ctx.timers, timer)
		}

	}()

	ctx.SetStackDepthLimit(20000)

	ctx.tick()

	ctx.init()

	// Run main code
	_, err = main(code, name)(ctx)
	if err != nil {
		panic(err)
	}

	// Wait for timers
	err = ctx.wait()
	if err != nil {
		panic(err)
	}

	return

}

func (ctx *Orbit) Quit(err error) {
	panic(err)
}

func (ctx *Orbit) init() {
	for _, e := range inits {
		e(ctx)
	}
}

func (ctx *Orbit) exit() {
	for _, e := range exits {
		e(ctx)
	}
}

func (ctx *Orbit) fail(err error) {
	for _, e := range fails {
		e(ctx, err)
	}
}
<commit_msg>Update comments<commit_after>// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package orbit

import (
	"time"

	"github.com/robertkrimen/otto"
)

// Orbit is a node.go context.
type Orbit struct {
	// Underlying Otto instance.
	*otto.Otto
	// External runtime variables.
	Vars map[string]interface{}
	// Quit channel
	quit chan error
	// Loop channel
	loop chan *task
	// Timeout timer
	timer *time.Timer
	// Runtime timers
	timers map[*task]*task
	// Timeout sets a timeout
	timeout time.Duration
	// Module outputs are cached for future use.
	modules map[string]otto.Value
}

type (
	// Module is a javascript module
	module func(*Orbit) (otto.Value, error)
)

var (
	// Event groups
	inits []func(*Orbit)
	exits []func(*Orbit)
	fails []func(*Orbit, error)
	// Finder loads files
	finder func(*Orbit, []string) (interface{}, string, error)
	// Modules stores registered packages
	modules = make(map[string]module)
)

// Add adds a module to the runtime
func Add(name string, item interface{}) {
	switch what := item.(type) {
	case string:
		addSource(name, what)
	case []byte:
		addSource(name, what)
	case func(*Orbit) (otto.Value, error):
		addModule(name, what)
	}
}

func Find(ctx *Orbit, files []string) (interface{}, string, error) {
	return finder(ctx, files)
}

// OnInit registers a callback for when the program starts up
func OnInit(call func(*Orbit)) {
	inits = append(inits, call)
}

// OnExit registers a callback for when the program shuts down
func OnExit(call func(*Orbit)) {
	exits = append(exits, call)
}

// OnFail registers a callback for when the program encounters and error
func OnFail(call func(*Orbit, error)) {
	fails = append(fails, call)
}

// OnFile registers a callback for finding required files
func OnFile(call func(*Orbit, []string) (interface{}, string, error)) {
	finder = call
}

// New creates a new Orbit runtime
func New(timeout time.Duration) *Orbit {

	orbit := &Orbit{
		Otto:    otto.New(),
		Vars:    make(map[string]interface{}),
		quit:    make(chan error),
		loop:    make(chan *task),
		timers:  make(map[*task]*task),
		modules: make(map[string]otto.Value),
		timeout: timeout * time.Millisecond,
	}

	orbit.Interrupt = make(chan func(), 1)

	return orbit

}

// Def sets a global variable in the runtime
func (ctx *Orbit) Def(name string, item interface{}) {
	obj, _ := ctx.Get("global")
	obj.Object().Set(name, item)
	ctx.Set(name, item)
}

// Run executes some code. Code may be a string or a byte slice.
func (ctx *Orbit) Run(name string, code interface{}) (err error) {

	defer func() {

		var ok bool

		if err, ok = recover().(error); ok {
			ctx.fail(err)
		}

		ctx.exit()

		if ctx.timer != nil {
			ctx.timer.Stop()
		}

		for timer := range ctx.timers {
			timer.timer.Stop()
			delete(ctx.timers, timer)
		}

	}()

	ctx.SetStackDepthLimit(20000)

	ctx.tick()

	ctx.init()

	// Run main code
	_, err = main(code, name)(ctx)
	if err != nil {
		panic(err)
	}

	// Wait for timers
	err = ctx.wait()
	if err != nil {
		panic(err)
	}

	return

}

func (ctx *Orbit) Quit(err error) {
	panic(err)
}

func (ctx *Orbit) init() {
	for _, e := range inits {
		e(ctx)
	}
}

func (ctx *Orbit) exit() {
	for _, e := range exits {
		e(ctx)
	}
}

func (ctx *Orbit) fail(err error) {
	for _, e := range fails {
		e(ctx, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"golang.org/x/crypto/acme"
	// careful: this comes from vendor/, because it has our
	// GetCertificateFromCSR in it.
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/context"
)

var version = flag.Bool("version", false, "Show the version and exit.")

var domainNames = map[string]bool{
	"prod.unifield.org": true,
	"dev.unifield.org":  true,
}

var cacheDir = "/cache"

type Instance struct {
	Id, Name string
}

func getDomain(host string) (string, error) {
	x := strings.Split(host, ".")
	if len(x) > 1 {
		return strings.Join(x[1:], "."), nil
	} else {
		return "", fmt.Errorf("host %v has no dots", host)
	}
}

func HostWhitelistByDomains(doms map[string]bool) autocert.HostPolicy {
	return func(_ context.Context, host string) error {
		// FQDNs can end in dot
		// Except, see https://github.com/letsencrypt/boulder/issues/2367
		if strings.HasSuffix(host, ".") {
			host = host[0 : len(host)-1]
		}
		host = strings.ToLower(host)

		dom, err := getDomain(host)
		if err != nil {
			return err
		}

		if doms[dom] {
			return nil
		}
		return fmt.Errorf("certomat: domain %v not allowed", dom)
	}
}

func notFound(w http.ResponseWriter, why string) {
	http.Error(w, why, http.StatusNotFound)
}

func getNameFromCSR(csr []byte) string {
	cr, err := x509.ParseCertificateRequest(csr)
	if err != nil {
		return ""
	}
	return cr.Subject.CommonName
}

// Handles URLs like https://certomat/get-cert-from-csr
//
// Read the CSR from the Body, send it to LetsEncrypt,
// send the cert back to them.
func getHandler(w http.ResponseWriter, r *http.Request) {
	log.Print(*r)

	if r.Method != "POST" {
		http.Error(w, "post only", http.StatusMethodNotAllowed)
	}

	csr, err := ioutil.ReadAll(r.Body)
	if err != nil {
		notFound(w, err.Error())
		return
	}

	name := getNameFromCSR(csr)
	tlscer, err := mgr.GetCertificateFromCSR(csr)
	if err != nil {
		log.Print(name, " not ok: ", err)
		notFound(w, err.Error())
		return
	}

	var buf bytes.Buffer
	for _, b := range tlscer.Certificate {
		pb := &pem.Block{Type: "CERTIFICATE", Bytes: b}
		if err := pem.Encode(&buf, pb); err != nil {
			log.Print(name, " not ok: ", err)
			notFound(w, err.Error())
		}
	}
	w.Write(buf.Bytes())
	log.Print(name, " ok")
}

var mgr *autocert.Manager

var gitRevision = "(dev)"

func main() {
	flag.Parse()
	if *version {
		fmt.Println(gitRevision)
		return
	}

	// Load the LetsEncrypt root
	roots := x509.NewCertPool()
	ok := roots.AppendCertsFromPEM([]byte(lePem))
	if !ok {
		log.Fatal("failed to load certs")
	}
	t := &tls.Config{
		RootCAs: roots,
	}
	hc := &http.Client{
		// Use the same defaults as http.DefaultTransport
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).DialContext,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
			// But add our own TLS config to trust
			// the LetsEncrypt certificates.
			TLSClientConfig: t,
		},
	}
	ac := &acme.Client{
		// Normally ACME_DIR_URL is unset. If you set it to
		// https://acme-staging.api.letsencrypt.org/directory
		// then certomat will talk to the staging server.
		// When it is unset, we talk to the live server.
		DirectoryURL: os.Getenv("ACME_DIR_URL"),
		HTTPClient:   hc,
	}
	mgr = &autocert.Manager{
		Client:     ac,
		Prompt:     autocert.AcceptTOS,
		HostPolicy: HostWhitelistByDomains(domainNames),
		Cache:      autocert.DirCache(cacheDir),
	}

	// Set up the server:
	// - listen on 443
	// - use autocert
	// - answer to /get
	// - log all other requests and return 404
	s := &http.Server{
		Addr: ":https",
		TLSConfig: &tls.Config{
			GetCertificate: mgr.GetCertificate,
		},
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		log.Print(*r)
		if r.URL.Path == "/" {
			w.Write(welcome)
			return
		} else {
			notFound(w, "unknown path")
		}
	})
	http.Handle("/get-cert-from-csr",
		http.StripPrefix("/get-cert-from-csr",
			http.HandlerFunc(getHandler)))

	err := s.ListenAndServeTLS("", "")
	if err != nil {
		log.Fatal(err)
	}
}

// Some text to put on the front page.
var welcome = []byte("
Certomat

For more info see the wiki.
")
<commit_msg>Added a domain for testing.<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"golang.org/x/crypto/acme"
	// careful: this comes from vendor/, because it has our
	// GetCertificateFromCSR in it.
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/context"
)

var version = flag.Bool("version", false, "Show the version and exit.")

var domainNames = map[string]bool{
	"prod.unifield.org": true,
	"dev.unifield.org":  true,
	"dev.unifield.biz":  true,
}

var cacheDir = "/cache"

type Instance struct {
	Id, Name string
}

func getDomain(host string) (string, error) {
	x := strings.Split(host, ".")
	if len(x) > 1 {
		return strings.Join(x[1:], "."), nil
	} else {
		return "", fmt.Errorf("host %v has no dots", host)
	}
}

func HostWhitelistByDomains(doms map[string]bool) autocert.HostPolicy {
	return func(_ context.Context, host string) error {
		// FQDNs can end in dot
		// Except, see https://github.com/letsencrypt/boulder/issues/2367
		if strings.HasSuffix(host, ".") {
			host = host[0 : len(host)-1]
		}
		host = strings.ToLower(host)

		dom, err := getDomain(host)
		if err != nil {
			return err
		}

		if doms[dom] {
			return nil
		}
		return fmt.Errorf("certomat: domain %v not allowed", dom)
	}
}

func notFound(w http.ResponseWriter, why string) {
	http.Error(w, why, http.StatusNotFound)
}

func getNameFromCSR(csr []byte) string {
	cr, err := x509.ParseCertificateRequest(csr)
	if err != nil {
		return ""
	}
	return cr.Subject.CommonName
}

// Handles URLs like https://certomat/get-cert-from-csr
//
// Read the CSR from the Body, send it to LetsEncrypt,
// send the cert back to them.
func getHandler(w http.ResponseWriter, r *http.Request) {
	log.Print(*r)

	if r.Method != "POST" {
		http.Error(w, "post only", http.StatusMethodNotAllowed)
	}

	csr, err := ioutil.ReadAll(r.Body)
	if err != nil {
		notFound(w, err.Error())
		return
	}

	name := getNameFromCSR(csr)
	tlscer, err := mgr.GetCertificateFromCSR(csr)
	if err != nil {
		log.Print(name, " not ok: ", err)
		notFound(w, err.Error())
		return
	}

	var buf bytes.Buffer
	for _, b := range tlscer.Certificate {
		pb := &pem.Block{Type: "CERTIFICATE", Bytes: b}
		if err := pem.Encode(&buf, pb); err != nil {
			log.Print(name, " not ok: ", err)
			notFound(w, err.Error())
		}
	}
	w.Write(buf.Bytes())
	log.Print(name, " ok")
}

var mgr *autocert.Manager

var gitRevision = "(dev)"

func main() {
	flag.Parse()
	if *version {
		fmt.Println(gitRevision)
		return
	}

	// Load the LetsEncrypt root
	roots := x509.NewCertPool()
	ok := roots.AppendCertsFromPEM([]byte(lePem))
	if !ok {
		log.Fatal("failed to load certs")
	}
	t := &tls.Config{
		RootCAs: roots,
	}
	hc := &http.Client{
		// Use the same defaults as http.DefaultTransport
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).DialContext,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
			// But add our own TLS config to trust
			// the LetsEncrypt certificates.
			TLSClientConfig: t,
		},
	}
	ac := &acme.Client{
		// Normally ACME_DIR_URL is unset. If you set it to
		// https://acme-staging.api.letsencrypt.org/directory
		// then certomat will talk to the staging server.
		// When it is unset, we talk to the live server.
		DirectoryURL: os.Getenv("ACME_DIR_URL"),
		HTTPClient:   hc,
	}
	mgr = &autocert.Manager{
		Client:     ac,
		Prompt:     autocert.AcceptTOS,
		HostPolicy: HostWhitelistByDomains(domainNames),
		Cache:      autocert.DirCache(cacheDir),
	}

	// Set up the server:
	// - listen on 443
	// - use autocert
	// - answer to /get
	// - log all other requests and return 404
	s := &http.Server{
		Addr: ":https",
		TLSConfig: &tls.Config{
			GetCertificate: mgr.GetCertificate,
		},
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		log.Print(*r)
		if r.URL.Path == "/" {
			w.Write(welcome)
			return
		} else {
			notFound(w, "unknown path")
		}
	})
	http.Handle("/get-cert-from-csr",
		http.StripPrefix("/get-cert-from-csr",
			http.HandlerFunc(getHandler)))

	err := s.ListenAndServeTLS("", "")
	if err != nil {
		log.Fatal(err)
	}
}

// Some text to put on the front page.
var welcome = []byte("
Certomat

For more info see the wiki.
")
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/howeyc/fsnotify"
)

// parseArguments ...
func parseArguments(ev *fsnotify.FileEvent) (cmd string, args []string) {
	commandline := strings.Fields(flag.Args()[0])
	cmd = commandline[0]
	args = make([]string, len(commandline[1:]))

	// Do file name interpolation on the arguments if %f is in them
	for index, arg := range commandline[1:] {
		var err error
		if strings.Contains(arg, "%f") {
			arg, err = filepath.Abs(strings.Replace(arg, "%f", ev.Name, -1))
			if err != nil {
				log.Fatal("Could not get absolute path: ", err)
			}
		}
		args[index] = arg
	}

	return
}

func handle(ev *fsnotify.FileEvent, done chan bool) {
	command, args := parseArguments(ev)
	cmd := exec.Command(command, args...)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal("stdout not gotten: ", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal("stderr not gotten: ", err)
	}

	// Clear the screen
	fmt.Print("\033[H\033[2J")

	yellow := color.New(color.FgYellow, color.Bold).SprintfFunc()
	magenta := color.New(color.FgMagenta, color.Bold).SprintfFunc()
	red := color.New(color.FgRed, color.Bold).SprintfFunc()
	green := color.New(color.FgGreen, color.Bold).SprintfFunc()

	// Print the command in nice colors
	out := fmt.Sprintf("Running %s %s...", yellow(command), magenta(strings.Join(args, " ")))

	log.Println(out)

	// Repeatedly try to start the command.
	// There are cases in which this would fail, and just looping seems to fix it.
	// This is neither nice nor elegant, but hey, it works and if it can be kept
	// simple then it should be.
	for {
		err = cmd.Start()
		if err != nil {
			log.Print("Command failed to start: ", err)
		} else {
			break
		}
	}

	outchan := make(chan string)
	errchan := make(chan string)
	outdonechan := make(chan bool)
	errdonechan := make(chan bool)
	outdone := false

	go loopOutput(outchan, outdonechan, stdout)
	go loopOutput(errchan, errdonechan, stderr)

	for outdone == false {
		select {
		case msg := <-outchan:
			fmt.Print(msg)
		case errmsg := <-errchan:
			fmt.Print(red(errmsg))
		case <-outdonechan:
			outdone = true
		}
	}

	// Print red error message or green success message
	if err = cmd.Wait(); err != nil {
		log.Println(red(err.Error()))
	} else {
		log.Println(green("Execution successful."))
	}

	done <- true
}

func loopOutput(c chan string, done chan bool, pipe io.ReadCloser) {
	buf := make([]byte, 1024)

	for {
		n, err := pipe.Read(buf)

		// Either if the pipe was empty or an EOF or other error was returned.
		if n == 0 && err == nil || err != nil {
			done <- true
			return
		}

		c <- string(buf[:n])
	}
}

func main() {
	dir := flag.String("d", ".", "directory to watch")
	flag.Parse()

	if flag.NArg() != 1 {
		log.Fatal("One command line argument sting required.")
	}

	watcher, err := fsnotify.NewWatcher()
	defer watcher.Close()

	if err != nil {
		log.Fatal("Unable to create fsnotify watcher: ", err)
	}

	done := make(chan bool)
	item := make(chan bool)

	// Process events
	go func() {
		running := false
		for {
			select {
			case ev := <-watcher.Event:
				// A watcher event arrived - act on it, but only if
				// no other action is already happening
				if running == false {
					running = true
					go handle(ev, item)
				}

			case <-item:
				// The runner has returned! Let the others roam!
				running = false

			case err := <-watcher.Error:
				log.Println("watcher error: ", err)

			}
		}
	}()

	log.Printf("Watching %s...", *dir)
	err = watcher.Watch(*dir)
	if err != nil {
		log.Fatal("Directory watching failed: ", err)
	}

	<-done
}
<commit_msg>Add more documentation in the code<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/howeyc/fsnotify"
)

// parseArguments parses the command line flags and does interpolation
// of special formatting strings, like %f for the file name of the
// file that triggered the FileEvent.
func parseArguments(ev *fsnotify.FileEvent) (cmd string, args []string) {
	commandline := strings.Fields(flag.Args()[0])
	cmd = commandline[0]
	args = make([]string, len(commandline[1:]))

	// Do file name interpolation on the arguments if %f is in them
	for index, arg := range commandline[1:] {
		var err error
		if strings.Contains(arg, "%f") {
			arg, err = filepath.Abs(strings.Replace(arg, "%f", ev.Name, -1))
			if err != nil {
				log.Fatal("Could not get absolute path: ", err)
			}
		}
		args[index] = arg
	}

	return
}

// handle executes an event
func handle(ev *fsnotify.FileEvent, done chan bool) {
	command, args := parseArguments(ev)
	cmd := exec.Command(command, args...)

	// Set up pipes
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal("stdout not gotten: ", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal("stderr not gotten: ", err)
	}

	// Clear the screen
	fmt.Print("\033[H\033[2J")

	// Generate the color functions
	yellow := color.New(color.FgYellow, color.Bold).SprintfFunc()
	magenta := color.New(color.FgMagenta, color.Bold).SprintfFunc()
	red := color.New(color.FgRed, color.Bold).SprintfFunc()
	green := color.New(color.FgGreen, color.Bold).SprintfFunc()

	// Print the command in nice colors
	out := fmt.Sprintf("Running %s %s...", yellow(command), magenta(strings.Join(args, " ")))
	log.Println(out)

	// Repeatedly try to start the command.
	// There are cases in which this would fail, and just looping seems to fix it.
	// This is neither nice nor elegant, but hey, it works and if it can be kept
	// simple then it should be.
	for {
		err = cmd.Start()
		if err != nil {
			log.Print("Command failed to start: ", err)
		} else {
			break
		}
	}

	// Set up channels and states for the pipe listening
	outchan := make(chan string)
	errchan := make(chan string)
	outdonechan := make(chan bool)
	errdonechan := make(chan bool)
	outdone := false

	// Trigger the pipes
	go loopOutput(outchan, outdonechan, stdout)
	go loopOutput(errchan, errdonechan, stderr)

	// Handle pipe output and wait for them to drain completely.
	for outdone == false {
		select {
		case msg := <-outchan:
			fmt.Print(msg)
		case errmsg := <-errchan:
			fmt.Print(red(errmsg))
		case <-outdonechan:
			outdone = true
		}
	}

	// Print red error message or green success message
	if err = cmd.Wait(); err != nil {
		log.Println(red(err.Error()))
	} else {
		log.Println(green("Execution successful."))
	}

	done <- true
}

// loopOutput takes a pipe and drains it until EOF.
func loopOutput(c chan string, done chan bool, pipe io.ReadCloser) {
	buf := make([]byte, 1024)

	for {
		n, err := pipe.Read(buf)

		// Either if the pipe was empty or an EOF or other error was returned.
		if n == 0 && err == nil || err != nil {
			done <- true
			return
		}

		c <- string(buf[:n])
	}
}

func main() {
	dir := flag.String("d", ".", "directory to watch")
	flag.Parse()

	if flag.NArg() != 1 {
		log.Fatal("One command line argument sting required.")
	}

	watcher, err := fsnotify.NewWatcher()
	defer watcher.Close()

	if err != nil {
		log.Fatal("Unable to create fsnotify watcher: ", err)
	}

	done := make(chan bool)
	item := make(chan bool)

	// Process events
	go func() {
		running := false
		for {
			select {
			case ev := <-watcher.Event:
				// A watcher event arrived - act on it, but only if
				// no other action is already happening
				if running == false {
					running = true
					go handle(ev, item)
				}

			case <-item:
				// The runner has returned! Let the others roam!
				running = false

			case err := <-watcher.Error:
				log.Println("watcher error: ", err)

			}
		}
	}()

	log.Printf("Watching %s...", *dir)
	err = watcher.Watch(*dir)
	if err != nil {
		log.Fatal("Directory watching failed: ", err)
	}

	<-done
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"

	"encoding/json"

	"log"
	"net/http"
	"strings"
	"time"

	"github.com/googollee/go-socket.io"
	"github.com/skratchdot/open-golang/open"
)

type ControlMsg struct {
	Type  string
	Value string
}

func String2Bytes(s string) ([]byte, error) {
	s = strings.Trim(s, " ")
	s = strings.Replace(s, " ", "", -1)
	bs, err := hex.DecodeString(s)
	return bs, err
}
func SendErr(so socketio.Socket, err error) {
	so.Emit("err", err.Error())
}

var solist map[string]socketio.Socket

func delayStartBrowser(t time.Duration) {
	strs := strings.Split(Config.HTTPAddr, ":")
	if len(strs) == 2 {
		time.Sleep(t)
		open.Run("http://localhost:" + strs[1])
	}

}

func main() {
	ReadConfiguration()
	FlogInit()
	solist = make(map[string]socketio.Socket)
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Fatal(err)
	}
	server.On("connection", func(so socketio.Socket) {
		solist[so.Id()] = so
		log.Println("on connection")
		files, err := ListDir(".", ".log")
		if err == nil && files != nil && len(files) > 0 {
			histjson, err := json.Marshal(files)
			if err == nil {
				so.Emit("historylist", string(histjson))
			}
		}
		so.On("cmd", func(cmd string) {
			switch cmd {
			case "TruncateLog":
				log.Printf("TruncateLog")
				TruncateLog()
			}
		})
		so.On("history", func(msg string) {
			bs, err := ReadLog(msg)
			if bs != nil && len(bs) > 0 {
				so.Emit("rx", string(bs))
			} else {
				checkReportError(err)
			}
		})
		so.On("tx", func(msg string) {
			bs, _ := String2Bytes(msg)
			_, err := Serial.Write(bs)
			checkReportError(err)
		})
		so.On("err", func(msg string) {
			log.Printf("!ERR: " + msg)
		})
		so.On("disconnection", func() {
			log.Println("on disconnect")
			for k, _ := range solist {
				if k == so.Id() {
					delete(solist, k)
					break
				}
			}
		})
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Println("error:", err)
	})
	http.Handle("/socket.io/", server)
	http.Handle("/", http.FileServer(http.Dir("./public")))
	err = SerialOpen()
	if err != nil {
		log.Fatalf("Cannot Open %s", Config.SerialPort)
	}
	go SerialReadThread()
	go delayStartBrowser(time.Second)
	log.Println("Serving at " + Config.HTTPAddr)
	log.Fatal(http.ListenAndServe(Config.HTTPAddr, nil))

}
<commit_msg>serial open fail retry<commit_after>package main

import (
	"encoding/hex"

	"encoding/json"

	"log"
	"net/http"
	"strings"
	"time"

	"github.com/googollee/go-socket.io"
	"github.com/skratchdot/open-golang/open"
)

type ControlMsg struct {
	Type  string
	Value string
}

func String2Bytes(s string) ([]byte, error) {
	s = strings.Trim(s, " ")
	s = strings.Replace(s, " ", "", -1)
	bs, err := hex.DecodeString(s)
	return bs, err
}
func SendErr(so socketio.Socket, err error) {
	so.Emit("err", err.Error())
}

var solist map[string]socketio.Socket

func delayStartBrowser(t time.Duration) {
	strs := strings.Split(Config.HTTPAddr, ":")
	if len(strs) == 2 {
		time.Sleep(t)
		open.Run("http://localhost:" + strs[1])
	}

}

func main() {
	ReadConfiguration()
	FlogInit()
	solist = make(map[string]socketio.Socket)
	server, err := socketio.NewServer(nil)
	if err != nil {
		log.Fatal(err)
	}
	server.On("connection", func(so socketio.Socket) {
		solist[so.Id()] = so
		log.Println("on connection")
		files, err := ListDir(".", ".log")
		if err == nil && files != nil && len(files) > 0 {
			histjson, err := json.Marshal(files)
			if err == nil {
				so.Emit("historylist", string(histjson))
			}
		}
		so.On("cmd", func(cmd string) {
			switch cmd {
			case "TruncateLog":
				log.Printf("TruncateLog")
				TruncateLog()
			}
		})
		so.On("history", func(msg string) {
			bs, err := ReadLog(msg)
			if bs != nil && len(bs) > 0 {
				so.Emit("rx", string(bs))
			} else {
				checkReportError(err)
			}
		})
		so.On("tx", func(msg string) {
			bs, _ := String2Bytes(msg)
			_, err := Serial.Write(bs)
			checkReportError(err)
		})
		so.On("err", func(msg string) {
			log.Printf("!ERR: " + msg)
		})
		so.On("disconnection", func() {
			log.Println("on disconnect")
			for k, _ := range solist {
				if k == so.Id() {
					delete(solist, k)
					break
				}
			}
		})
	})
	server.On("error", func(so socketio.Socket, err error) {
		log.Println("error:", err)
	})
	http.Handle("/socket.io/", server)
	http.Handle("/", http.FileServer(http.Dir("./public")))
	for{
		err = SerialOpen()
		if err == nil {
			break
		}
		log.Printf("Cannot Open %s\n", Config.SerialPort)
		time.Sleep(time.Second*2)
	}

	go SerialReadThread()
	go delayStartBrowser(time.Second)
	log.Println("Serving at " + Config.HTTPAddr)
	log.Fatal(http.ListenAndServe(Config.HTTPAddr, nil))

}
<|endoftext|>