<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",
                },
        }

        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"))
        }

        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) (*apiv1.Secret, error) {
        kubeClient, err := createApiserverClient()
        s, err := kubeClient.CoreV1().Secrets(secret.Namespace).Create(secret)
        if err != nil {
                if k8sErrors.IsAlreadyExists(err) {
                        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) (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"],
                                },
                        })

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

<commit_msg>INFRA-5578-add-verbose-key-to-s3sync<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
}

                                                                                422,0-1       Bot

<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"io/ioutil"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/ysugimoto/pecolify"
)

// CLI option value struct
type CLI struct {
	// Initial bucket name
	bucket string

	// Using profile name
	profile string

	// Determine region
	region string

	// Using profile from environment
	env bool

	// Show help
	help bool
}

var cli CLI = CLI{}
var service *s3.S3
var p = pecolify.New()
var mimeTypeList = map[string]struct{}{
	"text/plain":             struct{}{},
	"text/html":              struct{}{},
	"text/css":               struct{}{},
	"application/javascript": struct{}{},
}

// init() for parsing command line args
func init() {
	flag.StringVar(&cli.bucket, "bucket", "", "Using bucket name")
	flag.StringVar(&cli.profile, "profile", "", "Use profile name")
	flag.BoolVar(&cli.env, "env", false, "Use credentials from environment")
	flag.StringVar(&cli.region, "region", "", "region name")
	flag.BoolVar(&cli.help, "help", false, "show usage")
	flag.Parse()

	if cli.help {
		showUsage()
		os.Exit(0)
	}
}

// show usage
func showUsage() {
	help := `========================================================================
ls3 : AWS S3 file explorer on CLI
========================================================================
Usage:
  ls3 [options]

Options:
  -profile [profile name] : Use profile name which is written in ~/.aws/credentials
                            If not supplied, use default profile
  -env                    : Use credentials from environment variable
                            You need to export AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
  -bucket                 : Initial bucket name
  -region [region name]   : Determine region (default: ap-northeast-1)
  -help                   : Show this help
`
	fmt.Println(help)
}

// Main function
func main() {
	sess := session.Must(session.NewSession())
	var conf *aws.Config
	if cli.env {
		conf = configFromEnv(cli.region)
	} else {
		conf = configFromProfile(cli.profile, cli.region)
	}

	service = s3.New(sess, conf)
	var bucket string
	var err error
	if cli.bucket != "" {
		bucket = cli.bucket
	} else {
		bucket, err = chooseBuckets()
		if err != nil {
			return
		}
	}
	fmt.Printf("Retriving %s bucket...\n", bucket)
	chooseObjects(bucket, []string{})
}

// Create aws.Config from profile
func configFromProfile(profileName, region string) *aws.Config {
	if region == "" {
		region = "ap-northeast-1"
	}
	provider := &credentials.SharedCredentialsProvider{
		Profile: profileName,
	}
	creds := credentials.NewCredentials(provider)
	return aws.NewConfig().
		WithCredentials(creds).
		WithRegion(region)
}

// Create aws.Config from environment
func configFromEnv(region string) *aws.Config {
	if region == "" {
		region = "ap-northeast-1"
	}
	return aws.NewConfig().
		WithCredentials(credentials.NewEnvCredentials()).
		WithRegion(region)
}

// Choose bucket from list
func chooseBuckets() (string, error) {
	result, err := service.ListBuckets(&s3.ListBucketsInput{})
	if err != nil {
		panic(err)
	}
	buckets := []string{}
	for i := 0; i < len(result.Buckets); i++ {
		name := *result.Buckets[i].Name
		buckets = append(buckets, name)
	}

	selected, err := p.Transform(buckets)
	if err != nil {
		return "", err
	}
	return selected, nil
}

// Choose from object list
func chooseObjects(bucket string, prefix []string) {
	input := &s3.ListObjectsInput{
		Bucket: aws.String(bucket),
	}
	if len(prefix) > 0 {
		input = input.SetPrefix(strings.Join(prefix, "/") + "/")
	}
	result, err := service.ListObjects(input)
	if err != nil {
		fmt.Println(err)
	}

	selected, err := p.Transform(formatObjectList(result.Contents, prefix))
	if err != nil {
		return
	}

	if selected == "../" {
		if len(prefix) > 0 {
			prefix = prefix[0 : len(prefix)-1]
		} else {
			b, err := chooseBuckets()
			if err != nil {
				return
			}
			bucket = b
			prefix = []string{}
		}
	} else if strings.HasSuffix(selected, "/") {
		selected = strings.TrimRight(selected[32:], "/")
		prefix = append(prefix, selected)
	} else {
		selected = strings.TrimRight(selected[32:], "/")
		if isEnd := displayObjectActions(bucket, prefix, selected); isEnd {
			return
		}
	}
	chooseObjects(bucket, prefix)
}

// Display object info for slected object
func displayObjectActions(bucket string, prefix []string, object string) bool {
	dir := ""
	if len(prefix) > 0 {
		dir = strings.Join(prefix, "/") + "/"
	}

	result, err := service.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(fmt.Sprintf("%s%s", dir, object)),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(strings.Repeat("=", 60))
	fmt.Printf("%-16s: s3://%s/%s%s\n", "S3 Location", bucket, dir, object)
	fmt.Printf("%-16s: %s\n", "Content Type", *result.ContentType)
	fmt.Printf("%-16s: %d\n", "File Size", *result.ContentLength)
	fmt.Printf("%-16s: %s\n", "Last Modified", utcToJst(*result.LastModified))
	fmt.Println("")
	return objectAction(result, object)
}

// Display object info and select action
func objectAction(result *s3.GetObjectOutput, object string) bool {
	var cmd string
	line := "Actions? [d:Download"
	ct := *result.ContentType
	_, isTextMime := mimeTypeList[ct]
	if isTextMime {
		line += ", v:View"
	}
	line = line + ", b:Back to list]: "
	fmt.Print(line)
	fmt.Scanf("%s", &cmd)

	switch cmd {
	case "d":
		fmt.Printf("Downloading %s...", object)
		buf, err := ioutil.ReadAll(result.Body)
		if err != nil {
			fmt.Println(err)
			return false
		}
		var cwd string
		cwd, err = os.Getwd()
		if err != nil {
			fmt.Println(err)
			return false
		}
		if err := ioutil.WriteFile(fmt.Sprintf("%s/%s", cwd, object), buf, 0644); err != nil {
			fmt.Println(err)
			return true
		}
		fmt.Println("Download completed")
		return false
	case "v":
		if isTextMime {
			if buf, err := ioutil.ReadAll(result.Body); err != nil {
				fmt.Println(err)
			} else {
				fmt.Println(string(buf))
			}
			return false
		} else {
			fmt.Println("Invalid access v action")
		}
		return true
	case "b":
		return false
	default:
		fmt.Println("Invalid command")
		return objectAction(result, object)
	}
}

// Fromat object list from S3 response
func formatObjectList(result []*s3.Object, prefix []string) (objects []string) {
	var rep string
	if len(prefix) > 0 {
		rep = strings.Join(prefix, "/") + "/"
	}
	objects = append(objects, "../")
	unique := map[string]struct{}{}
	for i := 0; i < len(result); i++ {
		isDir := false
		key := *result[i].Key
		lastModified := *result[i].LastModified
		size := *result[i].Size
		key = strings.Replace(key, rep, "", 1)
		if strings.Contains(key, "/") {
			spl := strings.Split(key, "/")
			if _, exists := unique[spl[0]]; exists {
				continue
			}
			unique[spl[0]] = struct{}{}
			key = spl[0] + "/"
			isDir = true
		}
		jst := utcToJst(lastModified)
		if isDir {
			objects = append(objects, fmt.Sprintf("%s %10s  %s", jst, "-", key))
		} else {
			objects = append(objects, fmt.Sprintf("%s %10d  %s", jst, size, key))
		}
	}
	return
}

// We're living in Asia/Tokyo location :)
var JST = time.FixedZone("Asia/Tokyo", 9*60*60)

// Transform from UTC to JST
func utcToJst(utc time.Time) string {
	jst := utc.In(JST)
	return jst.Format("2006-01-2 15:03:04")
}
<commit_msg>Add bucket prefix<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"io/ioutil"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/ysugimoto/pecolify"
)

// CLI option value struct
type CLI struct {
	// Initial bucket name
	bucket string

	// Using profile name
	profile string

	// Determine region
	region string

	// Using profile from environment
	env bool

	// Show help
	help bool
}

var cli CLI = CLI{}
var service *s3.S3
var p = pecolify.New()
var mimeTypeList = map[string]struct{}{
	"text/plain":             struct{}{},
	"text/html":              struct{}{},
	"text/css":               struct{}{},
	"application/javascript": struct{}{},
}

// init() for parsing command line args
func init() {
	flag.StringVar(&cli.bucket, "bucket", "", "Using bucket name")
	flag.StringVar(&cli.profile, "profile", "", "Use profile name")
	flag.BoolVar(&cli.env, "env", false, "Use credentials from environment")
	flag.StringVar(&cli.region, "region", "", "region name")
	flag.BoolVar(&cli.help, "help", false, "show usage")
	flag.Parse()

	if cli.help {
		showUsage()
		os.Exit(0)
	}
}

// show usage
func showUsage() {
	help := `========================================================================
ls3 : AWS S3 file explorer on CLI
========================================================================
Usage:
  ls3 [options]

Options:
  -profile [profile name] : Use profile name which is written in ~/.aws/credentials
                            If not supplied, use default profile
  -env                    : Use credentials from environment variable
                            You need to export AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
  -bucket                 : Initial bucket name
  -region [region name]   : Determine region (default: ap-northeast-1)
  -help                   : Show this help
`
	fmt.Println(help)
}

// Main function
func main() {
	sess := session.Must(session.NewSession())
	var conf *aws.Config
	if cli.env {
		conf = configFromEnv(cli.region)
	} else {
		conf = configFromProfile(cli.profile, cli.region)
	}

	service = s3.New(sess, conf)
	var bucket string
	var err error
	if cli.bucket != "" {
		bucket = cli.bucket
	} else {
		bucket, err = chooseBuckets()
		if err != nil {
			return
		}
	}
	fmt.Printf("Retriving %s bucket...\n", bucket)
	chooseObjects(bucket, []string{})
}

// Create aws.Config from profile
func configFromProfile(profileName, region string) *aws.Config {
	if region == "" {
		region = "ap-northeast-1"
	}
	provider := &credentials.SharedCredentialsProvider{
		Profile: profileName,
	}
	creds := credentials.NewCredentials(provider)
	return aws.NewConfig().
		WithCredentials(creds).
		WithRegion(region)
}

// Create aws.Config from environment
func configFromEnv(region string) *aws.Config {
	if region == "" {
		region = "ap-northeast-1"
	}
	return aws.NewConfig().
		WithCredentials(credentials.NewEnvCredentials()).
		WithRegion(region)
}

// Choose bucket from list
func chooseBuckets() (string, error) {
	result, err := service.ListBuckets(&s3.ListBucketsInput{})
	if err != nil {
		panic(err)
	}
	buckets := []string{}
	for i := 0; i < len(result.Buckets); i++ {
		name := *result.Buckets[i].Name
		buckets = append(buckets, fmt.Sprintf("[Bucket] %s", name))
	}

	selected, err := p.Transform(buckets)
	if err != nil {
		return "", err
	}
	return selected[9:], nil
}

// Choose from object list
func chooseObjects(bucket string, prefix []string) {
	input := &s3.ListObjectsInput{
		Bucket: aws.String(bucket),
	}
	if len(prefix) > 0 {
		input = input.SetPrefix(strings.Join(prefix, "/") + "/")
	}
	result, err := service.ListObjects(input)
	if err != nil {
		fmt.Println(err)
	}

	selected, err := p.Transform(formatObjectList(result.Contents, prefix))
	if err != nil {
		return
	}

	if selected == "../" {
		if len(prefix) > 0 {
			prefix = prefix[0 : len(prefix)-1]
		} else {
			b, err := chooseBuckets()
			if err != nil {
				return
			}
			bucket = b
			prefix = []string{}
		}
	} else if strings.HasSuffix(selected, "/") {
		selected = strings.TrimRight(selected[32:], "/")
		prefix = append(prefix, selected)
	} else {
		selected = strings.TrimRight(selected[32:], "/")
		if isEnd := displayObjectActions(bucket, prefix, selected); isEnd {
			return
		}
	}
	chooseObjects(bucket, prefix)
}

// Display object info for slected object
func displayObjectActions(bucket string, prefix []string, object string) bool {
	dir := ""
	if len(prefix) > 0 {
		dir = strings.Join(prefix, "/") + "/"
	}

	result, err := service.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(fmt.Sprintf("%s%s", dir, object)),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(strings.Repeat("=", 60))
	fmt.Printf("%-16s: s3://%s/%s%s\n", "S3 Location", bucket, dir, object)
	fmt.Printf("%-16s: %s\n", "Content Type", *result.ContentType)
	fmt.Printf("%-16s: %d\n", "File Size", *result.ContentLength)
	fmt.Printf("%-16s: %s\n", "Last Modified", utcToJst(*result.LastModified))
	fmt.Println("")
	return objectAction(result, object)
}

// Display object info and select action
func objectAction(result *s3.GetObjectOutput, object string) bool {
	var cmd string
	line := "Actions? [d:Download"
	ct := *result.ContentType
	_, isTextMime := mimeTypeList[ct]
	if isTextMime {
		line += ", v:View"
	}
	line = line + ", b:Back to list]: "
	fmt.Print(line)
	fmt.Scanf("%s", &cmd)

	switch cmd {
	case "d":
		fmt.Printf("Downloading %s...", object)
		buf, err := ioutil.ReadAll(result.Body)
		if err != nil {
			fmt.Println(err)
			return false
		}
		var cwd string
		cwd, err = os.Getwd()
		if err != nil {
			fmt.Println(err)
			return false
		}
		if err := ioutil.WriteFile(fmt.Sprintf("%s/%s", cwd, object), buf, 0644); err != nil {
			fmt.Println(err)
			return true
		}
		fmt.Println("Download completed")
		return false
	case "v":
		if isTextMime {
			if buf, err := ioutil.ReadAll(result.Body); err != nil {
				fmt.Println(err)
			} else {
				fmt.Println(string(buf))
			}
			return false
		} else {
			fmt.Println("Invalid access v action")
		}
		return true
	case "b":
		return false
	default:
		fmt.Println("Invalid command")
		return objectAction(result, object)
	}
}

// Fromat object list from S3 response
func formatObjectList(result []*s3.Object, prefix []string) (objects []string) {
	var rep string
	if len(prefix) > 0 {
		rep = strings.Join(prefix, "/") + "/"
	}
	objects = append(objects, "../")
	unique := map[string]struct{}{}
	for i := 0; i < len(result); i++ {
		isDir := false
		key := *result[i].Key
		lastModified := *result[i].LastModified
		size := *result[i].Size
		key = strings.Replace(key, rep, "", 1)
		if strings.Contains(key, "/") {
			spl := strings.Split(key, "/")
			if _, exists := unique[spl[0]]; exists {
				continue
			}
			unique[spl[0]] = struct{}{}
			key = spl[0] + "/"
			isDir = true
		}
		jst := utcToJst(lastModified)
		if isDir {
			objects = append(objects, fmt.Sprintf("%s %10s  %s", jst, "-", key))
		} else {
			objects = append(objects, fmt.Sprintf("%s %10d  %s", jst, size, key))
		}
	}
	return
}

// We're living in Asia/Tokyo location :)
var JST = time.FixedZone("Asia/Tokyo", 9*60*60)

// Transform from UTC to JST
func utcToJst(utc time.Time) string {
	jst := utc.In(JST)
	return jst.Format("2006-01-02 15:03:04")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime/debug"
	"runtime/pprof"

	"github.com/maxbrunsfeld/counterfeiter/arguments"
	"github.com/maxbrunsfeld/counterfeiter/generator"
)

func main() {
	debug.SetGCPercent(-1)
	profile := false
	if os.Getenv("COUNTERFEITER_PROFILE") != "" {
		profile = true
	}
	if profile {
		p, err := filepath.Abs(filepath.Join(".", "counterfeiter.profile"))
		if err != nil {
			fail("%v", err)
		}
		f, err := os.Create(p)
		if err != nil {
			fail("%v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			fail("%v", err)
		}
		fmt.Printf("Profile: %s\n", p)
		defer pprof.StopCPUProfile()
	}

	log.SetFlags(log.Lshortfile)
	if !isDebug() {
		log.SetOutput(ioutil.Discard)
	}
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		fail("%s", usage)
		return
	}

	argumentParser := arguments.NewArgumentParser(
		fail,
		cwd,
		filepath.EvalSymlinks,
		os.Stat,
	)
	parsedArgs := argumentParser.ParseArguments(args...)
	generate(cwd(), parsedArgs)
}

func isDebug() bool {
	return os.Getenv("COUNTERFEITER_DEBUG") != ""
}

func generate(workingDir string, args arguments.ParsedArguments) {
	reportStarting(args.PrintToStdOut, args.OutputPath, args.FakeImplName)

	b, err := doGenerate(workingDir, args)
	if err != nil {
		fail("%v", err)
	}

	printCode(string(b), args.OutputPath, args.PrintToStdOut)
	reportDoneSimple(args.PrintToStdOut)
}

func doGenerate(workingDir string, args arguments.ParsedArguments) ([]byte, error) {
	mode := generator.InterfaceOrFunction
	if args.GenerateInterfaceAndShimFromPackageDirectory {
		mode = generator.Package
	}
	f, err := generator.NewFake(mode, args.InterfaceName, args.PackagePath, args.FakeImplName, args.DestinationPackageName, workingDir)
	if err != nil {
		return nil, err
	}
	return f.Generate(true)
}

func printCode(code, outputPath string, printToStdOut bool) {
	newCode, err := format.Source([]byte(code))
	if err != nil {
		fail("%v", err)
	}

	code = string(newCode)

	if printToStdOut {
		fmt.Println(code)
	} else {
		os.MkdirAll(filepath.Dir(outputPath), 0777)
		file, err := os.Create(outputPath)
		if err != nil {
			fail("Couldn't create fake file - %v", err)
		}

		_, err = file.WriteString(code)
		if err != nil {
			fail("Couldn't write to fake file - %v", err)
		}
	}
}

func reportStarting(printToStdOut bool, outputPath, fakeName string) {
	rel, err := filepath.Rel(cwd(), outputPath)
	if err != nil {
		fail("%v", err)
	}

	var writer io.Writer
	if printToStdOut {
		writer = os.Stderr
	} else {
		writer = os.Stdout
	}

	msg := fmt.Sprintf("Writing `%s` to `%s`... ", fakeName, rel)
	if isDebug() {
		msg = msg + "\n"
	}
	fmt.Fprint(writer, msg)
}

func reportDoneSimple(printToStdOut bool) {
	var writer io.Writer
	if printToStdOut {
		writer = os.Stderr
	} else {
		writer = os.Stdout
	}

	fmt.Fprint(writer, "Done\n")
}

func cwd() string {
	dir, err := os.Getwd()
	if err != nil {
		fail("Error - couldn't determine current working directory")
	}
	return dir
}

func fail(s string, args ...interface{}) {
	fmt.Printf("\n"+s+"\n", args...)
	os.Exit(1)
}

var usage = `
USAGE
	counterfeiter
		[-o <output-path>] [-p] [--fake-name <fake-name>]
		[<source-path>] <interface> [-]

ARGUMENTS
	source-path
		Path to the file or directory containing the interface to fake.
		In package mode (-p), source-path should instead specify the path
		of the input package; alternatively you can use the package name
		(e.g. "os") and the path will be inferred from your GOROOT.

	interface
		If source-path is specified: Name of the interface to fake.
		If no source-path is specified: Fully qualified interface path of the interface to fake.
    If -p is specified, this will be the name of the interface to generate.

	example:
		# writes "FakeStdInterface" to ./packagefakes/fake_std_interface.go
		counterfeiter package/subpackage.StdInterface

	'-' argument
		Write code to standard out instead of to a file

OPTIONS
	-o
		Path to the file or directory for the generated fakes.
		This also determines the package name that will be used.
		By default, the generated fakes will be generated in
		the package "xyzfakes" which is nested in package "xyz",
		where "xyz" is the name of referenced package.

	example:
		# writes "FakeMyInterface" to ./mySpecialFakesDir/specialFake.go
		counterfeiter -o ./mySpecialFakesDir/specialFake.go ./mypackage MyInterface

	-p
		Package mode:  When invoked in package mode, counterfeiter
		will generate an interface and shim implementation from a
		package in your GOPATH.  Counterfeiter finds the public methods
		in the package <source-path> and adds those method signatures
		to the generated interface <interface-name>.

	example:
		# generates os.go (interface) and osshim.go (shim) in ${PWD}/osshim
		counterfeiter -p os
		# now generate fake in ${PWD}/osshim/os_fake (fake_os.go)
		go generate osshim/...

	--fake-name
		Name of the fake struct to generate. By default, 'Fake' will
		be prepended to the name of the original interface. (ignored in
		-p mode)

	example:
		# writes "CoolThing" to ./mypackagefakes/cool_thing.go
		counterfeiter --fake-name CoolThing ./mypackage MyInterface
`
<commit_msg>Always print information messages to stderr<commit_after>package main

import (
	"flag"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime/debug"
	"runtime/pprof"

	"github.com/maxbrunsfeld/counterfeiter/arguments"
	"github.com/maxbrunsfeld/counterfeiter/generator"
)

func main() {
	debug.SetGCPercent(-1)
	profile := false
	if os.Getenv("COUNTERFEITER_PROFILE") != "" {
		profile = true
	}
	if profile {
		p, err := filepath.Abs(filepath.Join(".", "counterfeiter.profile"))
		if err != nil {
			fail("%v", err)
		}
		f, err := os.Create(p)
		if err != nil {
			fail("%v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			fail("%v", err)
		}
		fmt.Printf("Profile: %s\n", p)
		defer pprof.StopCPUProfile()
	}

	log.SetFlags(log.Lshortfile)
	if !isDebug() {
		log.SetOutput(ioutil.Discard)
	}
	flag.Parse()
	args := flag.Args()

	if len(args) < 1 {
		fail("%s", usage)
		return
	}

	argumentParser := arguments.NewArgumentParser(
		fail,
		cwd,
		filepath.EvalSymlinks,
		os.Stat,
	)
	parsedArgs := argumentParser.ParseArguments(args...)
	generate(cwd(), parsedArgs)
}

func isDebug() bool {
	return os.Getenv("COUNTERFEITER_DEBUG") != ""
}

func generate(workingDir string, args arguments.ParsedArguments) {
	reportStarting(args.OutputPath, args.FakeImplName)

	b, err := doGenerate(workingDir, args)
	if err != nil {
		fail("%v", err)
	}

	printCode(string(b), args.OutputPath, args.PrintToStdOut)
	fmt.Fprint(os.Stderr, "Done\n")
}

func doGenerate(workingDir string, args arguments.ParsedArguments) ([]byte, error) {
	mode := generator.InterfaceOrFunction
	if args.GenerateInterfaceAndShimFromPackageDirectory {
		mode = generator.Package
	}
	f, err := generator.NewFake(mode, args.InterfaceName, args.PackagePath, args.FakeImplName, args.DestinationPackageName, workingDir)
	if err != nil {
		return nil, err
	}
	return f.Generate(true)
}

func printCode(code, outputPath string, printToStdOut bool) {
	newCode, err := format.Source([]byte(code))
	if err != nil {
		fail("%v", err)
	}

	code = string(newCode)

	if printToStdOut {
		fmt.Println(code)
	} else {
		os.MkdirAll(filepath.Dir(outputPath), 0777)
		file, err := os.Create(outputPath)
		if err != nil {
			fail("Couldn't create fake file - %v", err)
		}

		_, err = file.WriteString(code)
		if err != nil {
			fail("Couldn't write to fake file - %v", err)
		}
	}
}

func reportStarting(outputPath, fakeName string) {
	rel, err := filepath.Rel(cwd(), outputPath)
	if err != nil {
		fail("%v", err)
	}

	msg := fmt.Sprintf("Writing `%s` to `%s`... ", fakeName, rel)
	if isDebug() {
		msg = msg + "\n"
	}
	fmt.Fprint(os.Stderr, msg)
}

func cwd() string {
	dir, err := os.Getwd()
	if err != nil {
		fail("Error - couldn't determine current working directory")
	}
	return dir
}

func fail(s string, args ...interface{}) {
	fmt.Printf("\n"+s+"\n", args...)
	os.Exit(1)
}

var usage = `
USAGE
	counterfeiter
		[-o <output-path>] [-p] [--fake-name <fake-name>]
		[<source-path>] <interface> [-]

ARGUMENTS
	source-path
		Path to the file or directory containing the interface to fake.
		In package mode (-p), source-path should instead specify the path
		of the input package; alternatively you can use the package name
		(e.g. "os") and the path will be inferred from your GOROOT.

	interface
		If source-path is specified: Name of the interface to fake.
		If no source-path is specified: Fully qualified interface path of the interface to fake.
    If -p is specified, this will be the name of the interface to generate.

	example:
		# writes "FakeStdInterface" to ./packagefakes/fake_std_interface.go
		counterfeiter package/subpackage.StdInterface

	'-' argument
		Write code to standard out instead of to a file

OPTIONS
	-o
		Path to the file or directory for the generated fakes.
		This also determines the package name that will be used.
		By default, the generated fakes will be generated in
		the package "xyzfakes" which is nested in package "xyz",
		where "xyz" is the name of referenced package.

	example:
		# writes "FakeMyInterface" to ./mySpecialFakesDir/specialFake.go
		counterfeiter -o ./mySpecialFakesDir/specialFake.go ./mypackage MyInterface

	-p
		Package mode:  When invoked in package mode, counterfeiter
		will generate an interface and shim implementation from a
		package in your GOPATH.  Counterfeiter finds the public methods
		in the package <source-path> and adds those method signatures
		to the generated interface <interface-name>.

	example:
		# generates os.go (interface) and osshim.go (shim) in ${PWD}/osshim
		counterfeiter -p os
		# now generate fake in ${PWD}/osshim/os_fake (fake_os.go)
		go generate osshim/...

	--fake-name
		Name of the fake struct to generate. By default, 'Fake' will
		be prepended to the name of the original interface. (ignored in
		-p mode)

	example:
		# writes "CoolThing" to ./mypackagefakes/cool_thing.go
		counterfeiter --fake-name CoolThing ./mypackage MyInterface
`
<|endoftext|>


<commit_before>// Simple implementation of the raft concensus algorithm using golang rpc
// Cody Malick and Jacob Broderick
package main

import (
  "fmt"
  "net"
  "net/rpc"
  "strconv"
  "log"
  "net/http"
)

type Log struct {
	Item  int
	Epoch int
}

type Message struct {
	Source      int
	Destination int
  Value       int
}

type Server struct {
	Id    int
	Epoch int
	Log   []Log
  Port  int
}

func (t *Server) Respond(message *Message, reply *Message) error {
  message.Value += 1
  *reply = *message
  return nil
}

const (
	numServers = 5
)
// Reference: https://golang.org/pkg/net/rpc/
func spawnServer(server Server) {
  fmt.Println(server.Id)
  fmt.Println(server.Port)
  rpc.Register(server.Id)
  rpc.HandleHTTP()
  listen, err := net.Listen("tcp", ":" + strconv.Itoa(server.Port))
  if err != nil {
	   log.Fatal("listen error:", err)
   }
   fmt.Println("Test")
   go http.Serve(listen, nil)
}

func main() {
	server0 := Server{0, 0, make([]Log, numServers), 50000}
	server1 := Server{1, 0, make([]Log, numServers), 50001}
	server2 := Server{2, 0, make([]Log, numServers), 50002}
	server3 := Server{3, 0, make([]Log, numServers), 50003}
	server4 := Server{4, 0, make([]Log, numServers), 50004}
	servers := []Server{server0, server1, server2, server3, server4}

	for i := 0; i < numServers; i++ {
		go spawnServer(servers[i])
	}
  //
  // client, err := rpc.DialHTTP("tcp", "localhost:50004")
  // if err != nil {
  //   log.Fatal("dialing:", err)
  // }
  //
  // testMessage := Message{1,1,2}
  // var reply Message
  // //server6 := Server{5, 0, make([]Log, 6), 50005}
  // mesCall := client.Go("Server.Respond", testMessage, reply, nil)
  // replyCall := <-mesCall.Done
  //
  // fmt.Println("%v\n", replyCall.Reply)

}
<commit_msg>removed bad networking code, added timeouts<commit_after>// Simple implementation of the raft concensus algorithm using golang rpc
// Cody Malick and Jacob Broderick
package main

import (
  "fmt"
  // "net"
  // "net/rpc"
  // "strconv"
  // "log"
  // "net/http"
  "time"
  "math/rand"
)

type Log struct {
	Item  int
	Epoch int
}

type Message struct {
	Source      int
	Destination int
  Value       int
}

type Server struct {
	Id    int
	Epoch int
	Log   []Log
  Port  int
}

func (t *Server) Respond(message *Message, reply *Message) error {
  message.Value += 1
  *reply = *message
  return nil
}

const (
	numServers = 5
)

// Timeout function
func randomTimeout() {
  sleep := rand.Intn(5)
  fmt.Printf("sleeping for %v seconds\n", sleep)
  time.Sleep(time.Second * time.Duration(sleep))
}

// Reference: https://golang.org/pkg/net/rpc/
func spawnServer(server Server) {
  fmt.Printf("Server %v is running\n", server.Id)
  for i := 1; i > 0; i++ {
    randomTimeout()
  }
}

func main() {
	server0 := Server{0, 0, make([]Log, numServers), 50000}
	server1 := Server{1, 0, make([]Log, numServers), 50001}
	server2 := Server{2, 0, make([]Log, numServers), 50002}
	server3 := Server{3, 0, make([]Log, numServers), 50003}
	server4 := Server{4, 0, make([]Log, numServers), 50004}
	servers := []Server{server0, server1, server2, server3, server4}

	for i := 0; i < numServers; i++ {
		go spawnServer(servers[i])
    fmt.Printf("Thread %v spawned\n", servers[i].Id)
	}

  // for i := 0; i < 999999999999; i++ {
  time.Sleep(time.Second * 10)
  // }
  //
  // client, err := rpc.DialHTTP("tcp", "localhost:50004")
  // if err != nil {
  //   log.Fatal("dialing:", err)
  // }
  //
  // testMessage := Message{1,1,2}
  // var reply Message
  // //server6 := Server{5, 0, make([]Log, 6), 50005}
  // mesCall := client.Go("Server.Respond", testMessage, reply, nil)
  // replyCall := <-mesCall.Done
  //
  // fmt.Println("%v\n", replyCall.Reply)

}
<|endoftext|>


<commit_before>package main

import "time"
import "github.com/heysquirrel/tribe/app"
import "github.com/heysquirrel/tribe/git"

func main() {
	a := app.New()
	defer a.Close()

	a.UpdateChanges(git.Changes())
	// go update(a)

	a.Loop()
}

func update(a *app.App) {
	for {
		select {
		case <-a.Done:
			return
		case <-time.After(10 * time.Second):
			a.UpdateChanges(git.Changes())
		}
	}

}
<commit_msg>Turn autoupdate back on<commit_after>package main

import "time"
import "github.com/heysquirrel/tribe/app"
import "github.com/heysquirrel/tribe/git"

func main() {
	a := app.New()
	defer a.Close()

	go update(a)

	a.Loop()
}

func update(a *app.App) {
	a.UpdateChanges(git.Changes())
	for {
		select {
		case <-a.Done:
			return
		case <-time.After(10 * time.Second):
			a.UpdateChanges(git.Changes())
		}
	}

}
<|endoftext|>


<commit_before>// Copyright 2017 Gavin Chun Jin. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.
package main

import (
	"fmt"
	"github.com/erriapo/goping/core"
	"github.com/erriapo/goping/thirdparty"
	"github.com/erriapo/stats"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"log"
	"net"
	"os"
	"os/signal"
	"time"
)

// A quote by Epictetus
const payload = "First learn the meaning of what you say, and then speak."
const payloadLen = len(payload)
const ipheader = 20
const icmpheader = 8
const payloadAndHeader = payloadLen + ipheader + icmpheader

// milliseconds to pause between sending each ICMP request
const pause = 1

var accountant = stats.NewSink()
var cache = core.NewCache()
var counter = core.NewCounter()
var pingHeading bool

// return the first non empty arg or "unknown"
func choose(option1 string, option2 net.Addr) string {
	if len(option1) != 0 {
		return option1
	}
	if option2 != nil {
		return option2.String()
	}
	return "Unknown"
}

func heading(node string) string {
	return fmt.Sprintf("\n--- %[1]s ping statistics ---", node)
}

func nanoToMilli(d time.Duration) float64 {
	return float64(d.Nanoseconds()) / float64(1000000)
}

func main() {
	help, verbose, count, host, cname, iface, err := core.ParseOption(os.Args[1:])
	if help {
		fmt.Fprintf(os.Stderr, "%s", core.Usage)
		os.Exit(2)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "Aborted: %v\n", err)
		fmt.Fprintf(os.Stderr, "%s", core.Usage)
		os.Exit(2)
	}

	// It is safe to ignore the error as we will fallback
	// to the supplied Host
	suppliedFQDN, suppliedErr := cache.Reverse(host)
	if verbose {
		fmt.Printf("Reversed lookup of supplied host = %v\n", suppliedFQDN)
		fmt.Printf("CNAME = %v\n", cname)
	}

	// trap CTRL+C
	exitchan := make(chan os.Signal, 1)
	signal.Notify(exitchan, os.Interrupt) // SIGINT
	go func() {
		<-exitchan
		counter.Render(os.Stdout, heading(choose(cname, host)))
		if counter.NeedStatistics() {
			fmt.Printf("%s\n", thirdparty.Format(accountant))
		}
		os.Exit(1)
	}()

	var ifacetarget = net.IPv4zero
	allinterfaces, ifaceerr := core.ScanInterfaces()
	if ifaceerr == nil {
		if verbose {
			fmt.Printf("Scanning interfaces: %v\n", allinterfaces)
		}
		if iface != "0.0.0.0" {
			ip, ok := allinterfaces[iface]
			if ok {
				ifacetarget = ip
			}
		}
	}

	c, err := icmp.ListenPacket("ip4:icmp", ifacetarget.String())
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	var wm icmp.Message
	var wb []byte

	rb := make([]byte, 1500)

	var t1 time.Time
	var peer2 net.Addr
	var peer2FQDN string
	var peer2err error

	//g := core.ChoosePeer("", nil, nil, "", nil, nil)
	//fmt.Printf("g: %v %T\n", g, g)

nn:
	for i := 1; i <= int(count); i++ {
		wm = core.NewEcho(payload, i)
		wb, err = wm.Marshal(nil)
		if err != nil {
			log.Fatal(err)
		}
		//t1, _ := time.Parse(time.RFC3339, "2017-06-28T19:55:50+00:00")
		t1 = time.Now().Add(time.Second * 6)
		if err := c.SetDeadline(t1); err != nil {
			fmt.Fprintf(os.Stderr, "Unable to set read & write Deadline.")
			log.Fatal("Unable to continue. Halted")
		}
		time.Sleep(pause * time.Second)
		start := time.Now()
		if _, err := c.WriteTo(wb, host); err != nil {
			fmt.Fprintf(os.Stderr, "%d connect: Network is unreachable\n", i)
			continue nn
		}
		counter.OnSent()

		// TODO we need to loop until we receive an echo reply
		//hh := c.IPv4PacketConn()
		//k, cm, _, errgg := hh.ReadFrom(rb2)
		//fmt.Printf("!!! %v --- %v %v\n", cm, errgg, k)

		n, peer, err := c.ReadFrom(rb)
		if verbose {
			fmt.Printf("peer %v vs host %v\n", peer, host)
		}
		if peer != nil {
			peer2 = peer
		}

		if !pingHeading {
			fmt.Printf("PING %v (%v) %v(%v) bytes of data.\n", choose(cname, peer), host, payloadLen, payloadAndHeader)
			pingHeading = true
		}

		if err != nil {
			fmt.Printf("%v bytes from %v (%v): icmp_seq=%v No response\n", 0, choose(suppliedFQDN, host), host, i)
			if verbose {
				fmt.Fprintf(os.Stderr, "\t%+v\n", err)
			}
			continue nn
		}
		elapsed := time.Since(start)

		peer2FQDN, peer2err = cache.Reverse(peer2)
		h := core.ChoosePeer(suppliedFQDN, host, suppliedErr, peer2FQDN, peer2, peer2err)
		if verbose {
			fmt.Printf("ChoosePeer() returned %v\n", h)
		}
		fmt.Printf("%v bytes from %v (%v): icmp_seq=%v time=%v\n", n, h.FQDN, h.IP, i, elapsed)
		if verbose {
			fmt.Printf("RTT %d ns\n", elapsed.Nanoseconds())
		}

		rm, err := icmp.ParseMessage(1, rb[:n])
		//hder, _ := icmp.ParseIPv4Header(rb[:n])
		//fmt.Printf("REPLY %v -> %v\n", rm, hder)
		if err != nil {
			log.Fatal(err)
		}
		switch rm.Type {
		case ipv4.ICMPTypeEcho:
			if verbose {
				log.Printf("\t%+v; echo", rm)
			}
		case ipv4.ICMPTypeEchoReply:
			counter.OnReception()
			accountant.Push(nanoToMilli(elapsed))
			if verbose {
				log.Printf("\t%+v; echo reply", rm)
			}
		case ipv4.ICMPTypeDestinationUnreachable:
			counter.NoteAnError()
			fmt.Fprintf(os.Stderr, "\tDestination unreachable.\n")
			if verbose {
				log.Printf("%+v;", rm)
			}
		case ipv4.ICMPTypeTimeExceeded:
			counter.NoteAnError()
			fmt.Fprintf(os.Stderr, "\tTime to live exceeded.\n")
			if verbose {
				log.Printf("%+v;", rm)
			}
		default:
			if verbose {
				log.Printf("\tunexpected %+v;", rm)
			}
		}
	}
	counter.Render(os.Stdout, heading(choose(cname, peer2)))
	if counter.NeedStatistics() {
		fmt.Printf("%s\n", thirdparty.Format(accountant))
	}
	os.Exit(0)
}
<commit_msg>Fixes #20<commit_after>// Copyright 2017 Gavin Chun Jin. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

// +build darwin freebsd linux netbsd openbsd

package main

import (
	"fmt"
	"github.com/erriapo/goping/core"
	"github.com/erriapo/goping/thirdparty"
	"github.com/erriapo/stats"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"log"
	"net"
	"os"
	"os/signal"
	"time"
)

// A quote by Epictetus
const payload = "First learn the meaning of what you say, and then speak."
const payloadLen = len(payload)
const ipheader = 20
const icmpheader = 8
const payloadAndHeader = payloadLen + ipheader + icmpheader

// milliseconds to pause between sending each ICMP request
const pause = 1

var accountant = stats.NewSink()
var cache = core.NewCache()
var counter = core.NewCounter()
var pingHeading bool

// return the first non empty arg or "unknown"
func choose(option1 string, option2 net.Addr) string {
	if len(option1) != 0 {
		return option1
	}
	if option2 != nil {
		return option2.String()
	}
	return "Unknown"
}

func heading(node string) string {
	return fmt.Sprintf("\n--- %[1]s ping statistics ---", node)
}

func nanoToMilli(d time.Duration) float64 {
	return float64(d.Nanoseconds()) / float64(1000000)
}

func main() {
	help, verbose, count, host, cname, iface, err := core.ParseOption(os.Args[1:])
	if help {
		fmt.Fprintf(os.Stderr, "%s", core.Usage)
		os.Exit(2)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "Aborted: %v\n", err)
		fmt.Fprintf(os.Stderr, "%s", core.Usage)
		os.Exit(2)
	}

	// It is safe to ignore the error as we will fallback
	// to the supplied Host
	suppliedFQDN, suppliedErr := cache.Reverse(host)
	if verbose {
		fmt.Printf("Reversed lookup of supplied host = %v\n", suppliedFQDN)
		fmt.Printf("CNAME = %v\n", cname)
	}

	// trap CTRL+C
	exitchan := make(chan os.Signal, 1)
	signal.Notify(exitchan, os.Interrupt) // SIGINT
	go func() {
		<-exitchan
		counter.Render(os.Stdout, heading(choose(cname, host)))
		if counter.NeedStatistics() {
			fmt.Printf("%s\n", thirdparty.Format(accountant))
		}
		os.Exit(1)
	}()

	var ifacetarget = net.IPv4zero
	allinterfaces, ifaceerr := core.ScanInterfaces()
	if ifaceerr == nil {
		if verbose {
			fmt.Printf("Scanning interfaces: %v\n", allinterfaces)
		}
		if iface != "0.0.0.0" {
			ip, ok := allinterfaces[iface]
			if ok {
				ifacetarget = ip
			}
		}
	}

	c, err := icmp.ListenPacket("ip4:icmp", ifacetarget.String())
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	var wm icmp.Message
	var wb []byte

	rb := make([]byte, 1500)

	var t1 time.Time
	var peer2 net.Addr
	var peer2FQDN string
	var peer2err error

	//g := core.ChoosePeer("", nil, nil, "", nil, nil)
	//fmt.Printf("g: %v %T\n", g, g)

nn:
	for i := 1; i <= int(count); i++ {
		wm = core.NewEcho(payload, i)
		wb, err = wm.Marshal(nil)
		if err != nil {
			log.Fatal(err)
		}
		//t1, _ := time.Parse(time.RFC3339, "2017-06-28T19:55:50+00:00")
		t1 = time.Now().Add(time.Second * 6)
		if err := c.SetDeadline(t1); err != nil {
			fmt.Fprintf(os.Stderr, "Unable to set read & write Deadline.")
			log.Fatal("Unable to continue. Halted")
		}
		time.Sleep(pause * time.Second)
		start := time.Now()
		if _, err := c.WriteTo(wb, host); err != nil {
			fmt.Fprintf(os.Stderr, "%d connect: Network is unreachable\n", i)
			continue nn
		}
		counter.OnSent()

		// TODO we need to loop until we receive an echo reply
		//hh := c.IPv4PacketConn()
		//k, cm, _, errgg := hh.ReadFrom(rb2)
		//fmt.Printf("!!! %v --- %v %v\n", cm, errgg, k)

		n, peer, err := c.ReadFrom(rb)
		if verbose {
			fmt.Printf("peer %v vs host %v\n", peer, host)
		}
		if peer != nil {
			peer2 = peer
		}

		if !pingHeading {
			fmt.Printf("PING %v (%v) %v(%v) bytes of data.\n", choose(cname, peer), host, payloadLen, payloadAndHeader)
			pingHeading = true
		}

		if err != nil {
			fmt.Printf("%v bytes from %v (%v): icmp_seq=%v No response\n", 0, choose(suppliedFQDN, host), host, i)
			if verbose {
				fmt.Fprintf(os.Stderr, "\t%+v\n", err)
			}
			continue nn
		}
		elapsed := time.Since(start)

		peer2FQDN, peer2err = cache.Reverse(peer2)
		h := core.ChoosePeer(suppliedFQDN, host, suppliedErr, peer2FQDN, peer2, peer2err)
		if verbose {
			fmt.Printf("ChoosePeer() returned %v\n", h)
		}
		fmt.Printf("%v bytes from %v (%v): icmp_seq=%v time=%v\n", n, h.FQDN, h.IP, i, elapsed)
		if verbose {
			fmt.Printf("RTT %d ns\n", elapsed.Nanoseconds())
		}

		rm, err := icmp.ParseMessage(1, rb[:n])
		//hder, _ := icmp.ParseIPv4Header(rb[:n])
		//fmt.Printf("REPLY %v -> %v\n", rm, hder)
		if err != nil {
			log.Fatal(err)
		}
		switch rm.Type {
		case ipv4.ICMPTypeEcho:
			if verbose {
				log.Printf("\t%+v; echo", rm)
			}
		case ipv4.ICMPTypeEchoReply:
			counter.OnReception()
			accountant.Push(nanoToMilli(elapsed))
			if verbose {
				log.Printf("\t%+v; echo reply", rm)
			}
		case ipv4.ICMPTypeDestinationUnreachable:
			counter.NoteAnError()
			fmt.Fprintf(os.Stderr, "\tDestination unreachable.\n")
			if verbose {
				log.Printf("%+v;", rm)
			}
		case ipv4.ICMPTypeTimeExceeded:
			counter.NoteAnError()
			fmt.Fprintf(os.Stderr, "\tTime to live exceeded.\n")
			if verbose {
				log.Printf("%+v;", rm)
			}
		default:
			if verbose {
				log.Printf("\tunexpected %+v;", rm)
			}
		}
	}
	counter.Render(os.Stdout, heading(choose(cname, peer2)))
	if counter.NeedStatistics() {
		fmt.Printf("%s\n", thirdparty.Format(accountant))
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/danielwelch/mednotes/Godeps/_workspace/src/github.com/gorilla/handlers"
	"log"
	"net/http"
	"os"
)

func main() {

	r := handlers.LoggingHandler(os.Stdout, Router())

	log.Println("Listening on port 8080")
	log.Fatal(http.ListenAndServe(":8080", r))

}
<commit_msg>fixed port for heroku<commit_after>package main

import (
	"github.com/danielwelch/mednotes/Godeps/_workspace/src/github.com/gorilla/handlers"
	"log"
	"net/http"
	"os"
)

func main() {

	r := handlers.LoggingHandler(os.Stdout, Router())

	log.Println("Listening on port 8080")
	log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), r))

}
<|endoftext|>


<commit_before>// htwtxt – hosted twtxt server; see README for copyright and license info

package main

import "bufio"
import "errors"
import "flag"
import "github.com/gorilla/mux"
import "golang.org/x/crypto/bcrypt"
import "html/template"
import "io/ioutil"
import "log"
import "net"
import "net/http"
import "os"
import "strconv"
import "strings"
import "time"

const loginsFile = "logins.txt"
const feedsDir = "feeds"
const ipDelaysFile = "ip_delays.txt"
const legalUrlChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
	"0123456789_"

var dataDir string
var loginsPath string
var ipDelaysPath string
var feedsPath string
var templPath string
var templ *template.Template
var contactString string
var signupOpen bool

func createFileIfNotExists(path string) {
	if _, err := os.Stat(path); err != nil {
		file, err := os.Create(path)
		if err != nil {
			log.Fatal("Can't create file: ", err)
		}
		file.Close()
	}
}

func openFile(path string) *os.File {
	file, err := os.Open(path)
	if err != nil {
		file.Close()
		log.Fatal("Can't open file for reading", err)
	}
	return file
}

func linesFromFile(path string) []string {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal("Can't read file", err)
	}
	if string(text) == "" {
		return []string{}
	}
	return strings.Split(string(text), "\n")
}

func writeAtomic(path, text string) {
	tmpFile := path + "_tmp"
	if err := ioutil.WriteFile(tmpFile, []byte(text), 0600); err != nil {
		log.Fatal("Trouble writing file", err)
	}
	if err := os.Rename(path, path+"_"); err != nil {
		log.Fatal("Trouble moving file", err)
	}
	if err := os.Rename(tmpFile, path); err != nil {
		log.Fatal("Trouble moving file", err)
	}
	if err := os.Remove(path + "_"); err != nil {
		log.Fatal("Trouble removing file", err)
	}
}

func writeLinesAtomic(path string, lines []string) {
	writeAtomic(path, strings.Join(lines, "\n"))
}

func appendToFile(path string, msg string) {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal("Can't read file", err)
	}
	writeAtomic(path, string(text)+msg+"\n")
}

func removeLineStartingWith(path, token string) {
	lines := linesFromFile(path)
	lineNumber := -1
	for lineCount := 0; lineCount < len(lines); lineCount += 1 {
		line := lines[lineCount]
		tokens := strings.Split(line, " ")
		if 0 == strings.Compare(token, tokens[0]) {
			lineNumber = lineCount
			break
		}
	}
	lines = append(lines[:lineNumber], lines[lineNumber+1:]...)
	writeLinesAtomic(path, lines)
}

func removeLineFromFile(path string, lineNumber int) {
	lines := linesFromFile(ipDelaysPath)
	lines = append(lines[:lineNumber], lines[lineNumber+1:]...)
	writeLinesAtomic(path, lines)
}

func replaceLineStartingWith(path, token, newLine string) {
	lines := linesFromFile(path)
	for i, line := range lines {
		tokens := strings.Split(line, " ")
		if 0 == strings.Compare(token, tokens[0]) {
			lines[i] = newLine
			break
		}
	}
	writeLinesAtomic(path, lines)
}

func tokensFromLine(scanner *bufio.Scanner, nTokensExpected int) []string {
	if !scanner.Scan() {
		return []string{}
	}
	line := scanner.Text()
	tokens := strings.Split(line, " ")
	if len(tokens) != nTokensExpected {
		log.Fatal("Line in file had unexpected number of tokens")
	}
	return tokens
}

func execTemplate(w http.ResponseWriter, file string, input string) {
	type data struct{ Msg string }
	err := templ.ExecuteTemplate(w, file, data{Msg: input})
	if err != nil {
		log.Fatal("Trouble executing template", err)
	}
}

func onlyLegalRunes(str string) bool {
	for _, ru := range str {
		if !(strings.ContainsRune(legalUrlChars, ru)) {
			return false
		}
	}
	return true
}

func checkDelay(w http.ResponseWriter, ip string) (int, error) {
	var err error
	fileIpDelays := openFile(ipDelaysPath)
	defer fileIpDelays.Close()
	scanner := bufio.NewScanner(bufio.NewReader(fileIpDelays))
	tokens := tokensFromLine(scanner, 3)
	delay := -1
	var openTime int
	for 3 == len(tokens) {
		if 0 == strings.Compare(tokens[0], ip) {
			openTime, err = strconv.Atoi(tokens[1])
			if err != nil {
				log.Fatal("Can't parse IP delays file", err)
			}
			delay, err = strconv.Atoi(tokens[2])
			if err != nil {
				log.Fatal("Can't parse IP delays file", err)
			}
			if int(time.Now().Unix()) < openTime {
				execTemplate(w, "error.html",
					"This IP must wait a while for its "+
						"next login attempt.")
				err = errors.New("")
			}
			break
		}
		tokens = tokensFromLine(scanner, 3)
	}
	return delay, err
}

func login(w http.ResponseWriter, r *http.Request) (string, error) {
	ip, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Fatal("Can't parse ip from request", err)
	}
	delay, err := checkDelay(w, ip)
	if err != nil {
		return "", err
	}
	name := r.FormValue("name")
	pw := r.FormValue("password")
	loginValid := false
	fileLogins := openFile(loginsPath)
	defer fileLogins.Close()
	scanner := bufio.NewScanner(bufio.NewReader(fileLogins))
	tokens := tokensFromLine(scanner, 3)
	for 0 != len(tokens) {
		if 0 == strings.Compare(tokens[0], name) &&
			nil == bcrypt.CompareHashAndPassword([]byte(tokens[1]),
				[]byte(pw)) {
			loginValid = true
			if 0 <= delay {
				removeLineStartingWith(ipDelaysPath, ip)
			}
			break
		}
		tokens = tokensFromLine(scanner, 3)
	}
	if !loginValid {
		newLine := delay == -1
		delay = 2 * delay
		if -2 == delay {
			delay = 1
		}
		strOpenTime := strconv.Itoa(int(time.Now().Unix()) + delay)
		strDelay := strconv.Itoa(delay)
		line := ip + " " + strOpenTime + " " + strDelay
		if newLine {
			appendToFile(ipDelaysPath, line)
		} else {
			replaceLineStartingWith(ipDelaysPath, ip, line)
		}
		execTemplate(w, "error.html", "Bad login.")
		return name, errors.New("")
	}
	return name, nil
}

func accountLine(w http.ResponseWriter, r *http.Request,
	checkDupl bool) (string, error) {
	name := r.FormValue("name")
	pw := r.FormValue("new_password")
	pw2 := r.FormValue("new_password2")
	mail := r.FormValue("mail")
	if 0 != strings.Compare(pw, pw2) || 0 == strings.Compare("name", "") ||
		0 == strings.Compare(pw, "") || !onlyLegalRunes(name) ||
		len(name) > 140 || len(mail) > 140 ||
		strings.ContainsRune(mail, '\n') {
		execTemplate(w, "error.html", "Invalid values.")
		return "", errors.New("")
	}
	if checkDupl {
		fileRead := openFile(loginsPath)
		defer fileRead.Close()
		scanner := bufio.NewScanner(bufio.NewReader(fileRead))
		tokens := tokensFromLine(scanner, 3)
		for 0 != len(tokens) {
			if 0 == strings.Compare(name, tokens[0]) {
				execTemplate(w, "error.html", "Username taken.")
				return "", errors.New("")
			}
			tokens = tokensFromLine(scanner, 3)
		}
	}
	hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
	if err != nil {
		log.Fatal("Can't generate password hash", err)
	}
	return name + " " + string(hash) + " " + mail, nil
}

func cssHandler(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, templPath+"/style.css")
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	execTemplate(w, "index.html", "")
}

func infoHandler(w http.ResponseWriter, r *http.Request) {
	execTemplate(w, "info.html", contactString)
}

func signUpFormHandler(w http.ResponseWriter, r *http.Request) {
	if !signupOpen {
		execTemplate(w, "nosignup.html", "")
		return
	}
	execTemplate(w, "signupform.html", "")
}

func signUpHandler(w http.ResponseWriter, r *http.Request) {
	if !signupOpen {
		execTemplate(w, "error.html",
			"Account creation currently not allowed.")
		return
	}
	newLine, err := accountLine(w, r, true)
	if err != nil {
		return
	}
	appendToFile(loginsPath, newLine)
	execTemplate(w, "feedset.html", "")
}

func accountFormHandler(w http.ResponseWriter, r *http.Request) {
	execTemplate(w, "accountform.html", "")
}

func accountPostHandler(w http.ResponseWriter, r *http.Request) {
	name, err := login(w, r)
	if err != nil {
		return
	}
	newLine, err := accountLine(w, r, false)
	if err != nil {
		return
	}
	replaceLineStartingWith(loginsPath, name, newLine)
	execTemplate(w, "feedset.html", "")
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	file := openFile(loginsPath)
	defer file.Close()
	scanner := bufio.NewScanner(bufio.NewReader(file))
	var dir []string
	tokens := tokensFromLine(scanner, 3)
	for 0 != len(tokens) {
		dir = append(dir, tokens[0])
		tokens = tokensFromLine(scanner, 3)
	}
	type data struct{ Dir []string }
	err := templ.ExecuteTemplate(w, "list.html", data{Dir: dir})
	if err != nil {
		log.Fatal("Trouble executing template", err)
	}
}

func twtxtPostHandler(w http.ResponseWriter, r *http.Request) {
	name, err := login(w, r)
	if err != nil {
		return
	}
	text := r.FormValue("twt")
	twtsFile := feedsPath + "/" + name
	createFileIfNotExists(twtsFile)
	text = strings.Replace(text, "\n", " ", -1)
	appendToFile(twtsFile, time.Now().Format(time.RFC3339)+"\t"+text)
	http.Redirect(w, r, "/"+feedsDir+"/"+name, 302)
}

func twtxtHandler(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	if !onlyLegalRunes(name) {
		execTemplate(w, "error.html", "Bad path.")
		return
	}
	path := feedsPath + "/" + name
	if _, err := os.Stat(path); err != nil {
		execTemplate(w, "error.html", "Empty twtxt for user.")
		return
	}
	http.ServeFile(w, r, path)
}

func main() {
	var err error
	portPtr := flag.Int("port", 8000, "port to serve")
	keyPtr := flag.String("key", "", "SSL key file")
	certPtr := flag.String("cert", "", "SSL certificate file")
	flag.StringVar(&templPath, "templates",
		os.Getenv("GOPATH")+"/src/htwtxt/templates",
		"directory where to expect HTML templates")
	flag.StringVar(&dataDir, "dir", os.Getenv("HOME")+"/htwtxt",
		"directory to store feeds and login data")
	flag.StringVar(&contactString, "contact",
		"[operator passed no contact info to server]",
		"operator contact info to display on info page")
	flag.BoolVar(&signupOpen, "signup", false,
		"enable on-site account creation")
	flag.Parse()
	log.Println("Using as templates dir:", templPath)
	log.Println("Using as data dir:", dataDir)
	loginsPath = dataDir + "/" + loginsFile
	feedsPath = dataDir + "/" + feedsDir
	ipDelaysPath = dataDir + "/" + ipDelaysFile
	if ("" == *keyPtr && "" != *certPtr) ||
		("" != *keyPtr && "" == *certPtr) {
		log.Fatal("Expect either both key and certificate or none.")
	}
	if "" != *keyPtr {
		log.Println("Using TLS.")
		if _, err := os.Stat(*certPtr); err != nil {
			log.Fatal("No certificate file found.")
		}
		if _, err := os.Stat(*keyPtr); err != nil {
			log.Fatal("No server key file found.")
		}
	}
	createFileIfNotExists(loginsPath)
	createFileIfNotExists(ipDelaysPath)
	// TODO: Handle err here.
	_ = os.Mkdir(feedsPath, 0700)
	templ, err = template.New("main").ParseGlob(templPath + "/*.html")
	if err != nil {
		log.Fatal("Can't set up new template: ", err)
	}
	router := mux.NewRouter()
	router.HandleFunc("/", indexHandler)
	router.HandleFunc("/feeds", listHandler).Methods("GET")
	router.HandleFunc("/feeds/", listHandler)
	router.HandleFunc("/account", accountFormHandler).Methods("GET")
	router.HandleFunc("/account", accountPostHandler).Methods("POST")
	router.HandleFunc("/signup", signUpFormHandler).Methods("GET")
	router.HandleFunc("/signup", signUpHandler).Methods("POST")
	router.HandleFunc("/feeds", twtxtPostHandler).Methods("POST")
	router.HandleFunc("/feeds/{name}", twtxtHandler)
	router.HandleFunc("/info", infoHandler)
	router.HandleFunc("/style.css", cssHandler)
	http.Handle("/", router)
	log.Println("serving at port", *portPtr)
	if "" != *keyPtr {
		err = http.ListenAndServeTLS(":"+strconv.Itoa(*portPtr),
			*certPtr, *keyPtr, nil)
	} else {
		err = http.ListenAndServe(":"+strconv.Itoa(*portPtr), nil)
	}
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Further refactor file reading.<commit_after>// htwtxt – hosted twtxt server; see README for copyright and license info

package main

import "bufio"
import "errors"
import "flag"
import "github.com/gorilla/mux"
import "golang.org/x/crypto/bcrypt"
import "html/template"
import "io/ioutil"
import "log"
import "net"
import "net/http"
import "os"
import "strconv"
import "strings"
import "time"

const loginsFile = "logins.txt"
const feedsDir = "feeds"
const ipDelaysFile = "ip_delays.txt"
const legalUrlChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
	"0123456789_"

var dataDir string
var loginsPath string
var ipDelaysPath string
var feedsPath string
var templPath string
var templ *template.Template
var contactString string
var signupOpen bool

func createFileIfNotExists(path string) {
	if _, err := os.Stat(path); err != nil {
		file, err := os.Create(path)
		if err != nil {
			log.Fatal("Can't create file: ", err)
		}
		file.Close()
	}
}

func openFile(path string) *os.File {
	file, err := os.Open(path)
	if err != nil {
		file.Close()
		log.Fatal("Can't open file for reading", err)
	}
	return file
}

func linesFromFile(path string) []string {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal("Can't read file", err)
	}
	if string(text) == "" {
		return []string{}
	}
	return strings.Split(string(text), "\n")
}

func writeAtomic(path, text string) {
	tmpFile := path + "_tmp"
	if err := ioutil.WriteFile(tmpFile, []byte(text), 0600); err != nil {
		log.Fatal("Trouble writing file", err)
	}
	if err := os.Rename(path, path+"_"); err != nil {
		log.Fatal("Trouble moving file", err)
	}
	if err := os.Rename(tmpFile, path); err != nil {
		log.Fatal("Trouble moving file", err)
	}
	if err := os.Remove(path + "_"); err != nil {
		log.Fatal("Trouble removing file", err)
	}
}

func writeLinesAtomic(path string, lines []string) {
	writeAtomic(path, strings.Join(lines, "\n"))
}

func appendToFile(path string, msg string) {
	text, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal("Can't read file", err)
	}
	writeAtomic(path, string(text)+msg+"\n")
}

func removeLineStartingWith(path, token string) {
	lines := linesFromFile(path)
	lineNumber := -1
	for lineCount := 0; lineCount < len(lines); lineCount += 1 {
		line := lines[lineCount]
		tokens := strings.Split(line, " ")
		if 0 == strings.Compare(token, tokens[0]) {
			lineNumber = lineCount
			break
		}
	}
	lines = append(lines[:lineNumber], lines[lineNumber+1:]...)
	writeLinesAtomic(path, lines)
}

func removeLineFromFile(path string, lineNumber int) {
	lines := linesFromFile(ipDelaysPath)
	lines = append(lines[:lineNumber], lines[lineNumber+1:]...)
	writeLinesAtomic(path, lines)
}

func replaceLineStartingWith(path, token, newLine string) {
	lines := linesFromFile(path)
	for i, line := range lines {
		tokens := strings.Split(line, " ")
		if 0 == strings.Compare(token, tokens[0]) {
			lines[i] = newLine
			break
		}
	}
	writeLinesAtomic(path, lines)
}

func tokensFromLine(scanner *bufio.Scanner, nTokensExpected int) []string {
	if !scanner.Scan() {
		return []string{}
	}
	line := scanner.Text()
	tokens := strings.Split(line, " ")
	if len(tokens) != nTokensExpected {
		log.Fatal("Line in file had unexpected number of tokens")
	}
	return tokens
}

func getFromFileEntryFor(path, token string,
	numberTokensExpected int) ([]string, error) {
	file := openFile(path)
	defer file.Close()
	scanner := bufio.NewScanner(bufio.NewReader(file))
	tokens := tokensFromLine(scanner, numberTokensExpected)
	for 0 != len(tokens) {
		if 0 == strings.Compare(tokens[0], token) {
			return tokens[1:], nil
		}
		tokens = tokensFromLine(scanner, 3)
	}
	return []string{}, errors.New("")
}

func execTemplate(w http.ResponseWriter, file string, input string) {
	type data struct{ Msg string }
	err := templ.ExecuteTemplate(w, file, data{Msg: input})
	if err != nil {
		log.Fatal("Trouble executing template", err)
	}
}

func onlyLegalRunes(str string) bool {
	for _, ru := range str {
		if !(strings.ContainsRune(legalUrlChars, ru)) {
			return false
		}
	}
	return true
}

func checkDelay(w http.ResponseWriter, ip string) (int, error) {
	var err error
	var openTime int
	delay := -1
	tokens, errGet := getFromFileEntryFor(ipDelaysPath, ip, 3)
	if errGet == nil {
		openTime, err = strconv.Atoi(tokens[0])
		if err != nil {
			log.Fatal("Can't parse IP delays file", err)
		}
		delay, err = strconv.Atoi(tokens[1])
		if err != nil {
			log.Fatal("Can't parse IP delays file", err)
		}
		if int(time.Now().Unix()) < openTime {
			execTemplate(w, "error.html",
				"This IP must wait a while for its "+
					"next login attempt.")
			err = errors.New("")
		}
	}
	return delay, err
}

func login(w http.ResponseWriter, r *http.Request) (string, error) {
	ip, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Fatal("Can't parse ip from request", err)
	}
	delay, err := checkDelay(w, ip)
	if err != nil {
		return "", err
	}
	name := r.FormValue("name")
	pw := r.FormValue("password")
	loginValid := false
	tokens, err := getFromFileEntryFor(loginsPath, name, 3)
	if err == nil && nil == bcrypt.CompareHashAndPassword([]byte(tokens[0]),
		[]byte(pw)) {
		loginValid = true
		if 0 <= delay {
			removeLineStartingWith(ipDelaysPath, ip)
		}
	}
	if !loginValid {
		newLine := delay == -1
		delay = 2 * delay
		if -2 == delay {
			delay = 1
		}
		strOpenTime := strconv.Itoa(int(time.Now().Unix()) + delay)
		strDelay := strconv.Itoa(delay)
		line := ip + " " + strOpenTime + " " + strDelay
		if newLine {
			appendToFile(ipDelaysPath, line)
		} else {
			replaceLineStartingWith(ipDelaysPath, ip, line)
		}
		execTemplate(w, "error.html", "Bad login.")
		return name, errors.New("")
	}
	return name, nil
}

func accountLine(w http.ResponseWriter, r *http.Request,
	checkDupl bool) (string, error) {
	name := r.FormValue("name")
	pw := r.FormValue("new_password")
	pw2 := r.FormValue("new_password2")
	mail := r.FormValue("mail")
	if 0 != strings.Compare(pw, pw2) || 0 == strings.Compare("name", "") ||
		0 == strings.Compare(pw, "") || !onlyLegalRunes(name) ||
		len(name) > 140 || len(mail) > 140 ||
		strings.ContainsRune(mail, '\n') {
		execTemplate(w, "error.html", "Invalid values.")
		return "", errors.New("")
	}
	if checkDupl {
		_, err := getFromFileEntryFor(loginsPath, name, 3)
		if err == nil {
			execTemplate(w, "error.html", "Username taken.")
			return "", errors.New("")
		}
	}
	hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
	if err != nil {
		log.Fatal("Can't generate password hash", err)
	}
	return name + " " + string(hash) + " " + mail, nil
}

func cssHandler(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, templPath+"/style.css")
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	execTemplate(w, "index.html", "")
}

func infoHandler(w http.ResponseWriter, r *http.Request) {
	execTemplate(w, "info.html", contactString)
}

func signUpFormHandler(w http.ResponseWriter, r *http.Request) {
	if !signupOpen {
		execTemplate(w, "nosignup.html", "")
		return
	}
	execTemplate(w, "signupform.html", "")
}

func signUpHandler(w http.ResponseWriter, r *http.Request) {
	if !signupOpen {
		execTemplate(w, "error.html",
			"Account creation currently not allowed.")
		return
	}
	newLine, err := accountLine(w, r, true)
	if err != nil {
		return
	}
	appendToFile(loginsPath, newLine)
	execTemplate(w, "feedset.html", "")
}

func accountFormHandler(w http.ResponseWriter, r *http.Request) {
	execTemplate(w, "accountform.html", "")
}

func accountPostHandler(w http.ResponseWriter, r *http.Request) {
	name, err := login(w, r)
	if err != nil {
		return
	}
	newLine, err := accountLine(w, r, false)
	if err != nil {
		return
	}
	replaceLineStartingWith(loginsPath, name, newLine)
	execTemplate(w, "feedset.html", "")
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	file := openFile(loginsPath)
	defer file.Close()
	scanner := bufio.NewScanner(bufio.NewReader(file))
	var dir []string
	tokens := tokensFromLine(scanner, 3)
	for 0 != len(tokens) {
		dir = append(dir, tokens[0])
		tokens = tokensFromLine(scanner, 3)
	}
	type data struct{ Dir []string }
	err := templ.ExecuteTemplate(w, "list.html", data{Dir: dir})
	if err != nil {
		log.Fatal("Trouble executing template", err)
	}
}

func twtxtPostHandler(w http.ResponseWriter, r *http.Request) {
	name, err := login(w, r)
	if err != nil {
		return
	}
	text := r.FormValue("twt")
	twtsFile := feedsPath + "/" + name
	createFileIfNotExists(twtsFile)
	text = strings.Replace(text, "\n", " ", -1)
	appendToFile(twtsFile, time.Now().Format(time.RFC3339)+"\t"+text)
	http.Redirect(w, r, "/"+feedsDir+"/"+name, 302)
}

func twtxtHandler(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	if !onlyLegalRunes(name) {
		execTemplate(w, "error.html", "Bad path.")
		return
	}
	path := feedsPath + "/" + name
	if _, err := os.Stat(path); err != nil {
		execTemplate(w, "error.html", "Empty twtxt for user.")
		return
	}
	http.ServeFile(w, r, path)
}

func main() {
	var err error
	portPtr := flag.Int("port", 8000, "port to serve")
	keyPtr := flag.String("key", "", "SSL key file")
	certPtr := flag.String("cert", "", "SSL certificate file")
	flag.StringVar(&templPath, "templates",
		os.Getenv("GOPATH")+"/src/htwtxt/templates",
		"directory where to expect HTML templates")
	flag.StringVar(&dataDir, "dir", os.Getenv("HOME")+"/htwtxt",
		"directory to store feeds and login data")
	flag.StringVar(&contactString, "contact",
		"[operator passed no contact info to server]",
		"operator contact info to display on info page")
	flag.BoolVar(&signupOpen, "signup", false,
		"enable on-site account creation")
	flag.Parse()
	log.Println("Using as templates dir:", templPath)
	log.Println("Using as data dir:", dataDir)
	loginsPath = dataDir + "/" + loginsFile
	feedsPath = dataDir + "/" + feedsDir
	ipDelaysPath = dataDir + "/" + ipDelaysFile
	if ("" == *keyPtr && "" != *certPtr) ||
		("" != *keyPtr && "" == *certPtr) {
		log.Fatal("Expect either both key and certificate or none.")
	}
	if "" != *keyPtr {
		log.Println("Using TLS.")
		if _, err := os.Stat(*certPtr); err != nil {
			log.Fatal("No certificate file found.")
		}
		if _, err := os.Stat(*keyPtr); err != nil {
			log.Fatal("No server key file found.")
		}
	}
	createFileIfNotExists(loginsPath)
	createFileIfNotExists(ipDelaysPath)
	// TODO: Handle err here.
	_ = os.Mkdir(feedsPath, 0700)
	templ, err = template.New("main").ParseGlob(templPath + "/*.html")
	if err != nil {
		log.Fatal("Can't set up new template: ", err)
	}
	router := mux.NewRouter()
	router.HandleFunc("/", indexHandler)
	router.HandleFunc("/feeds", listHandler).Methods("GET")
	router.HandleFunc("/feeds/", listHandler)
	router.HandleFunc("/account", accountFormHandler).Methods("GET")
	router.HandleFunc("/account", accountPostHandler).Methods("POST")
	router.HandleFunc("/signup", signUpFormHandler).Methods("GET")
	router.HandleFunc("/signup", signUpHandler).Methods("POST")
	router.HandleFunc("/feeds", twtxtPostHandler).Methods("POST")
	router.HandleFunc("/feeds/{name}", twtxtHandler)
	router.HandleFunc("/info", infoHandler)
	router.HandleFunc("/style.css", cssHandler)
	http.Handle("/", router)
	log.Println("serving at port", *portPtr)
	if "" != *keyPtr {
		err = http.ListenAndServeTLS(":"+strconv.Itoa(*portPtr),
			*certPtr, *keyPtr, nil)
	} else {
		err = http.ListenAndServe(":"+strconv.Itoa(*portPtr), nil)
	}
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>