<commit_before>package structs

import (
	"fmt"
	"github.com/hashicorp/go-multierror"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestJob_Validate(t *testing.T) {
	j := &Job{}
	err := j.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "job region") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "job ID") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "job name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[3].Error(), "job type") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[4].Error(), "priority") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[5].Error(), "datacenters") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[6].Error(), "task groups") {
		t.Fatalf("err: %s", err)
	}

	j = &Job{
		Region:      "global",
		ID:          GenerateUUID(),
		Name:        "my-job",
		Type:        JobTypeService,
		Priority:    50,
		Datacenters: []string{"dc1"},
		TaskGroups: []*TaskGroup{
			&TaskGroup{
				Name: "web",
				RestartPolicy: &RestartPolicy{
					Interval: 5 * time.Minute,
					Delay:    10 * time.Second,
					Attempts: 10,
				},
			},
			&TaskGroup{
				Name: "web",
				RestartPolicy: &RestartPolicy{
					Interval: 5 * time.Minute,
					Delay:    10 * time.Second,
					Attempts: 10,
				},
			},
			&TaskGroup{
				RestartPolicy: &RestartPolicy{
					Interval: 5 * time.Minute,
					Delay:    10 * time.Second,
					Attempts: 10,
				},
			},
		},
	}
	err = j.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from group 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "group 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task group 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTaskGroup_Validate(t *testing.T) {
	tg := &TaskGroup{
		RestartPolicy: &RestartPolicy{
			Interval: 5 * time.Minute,
			Delay:    10 * time.Second,
			Attempts: 10,
		},
	}
	err := tg.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "group name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "count must be positive") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Missing tasks") {
		t.Fatalf("err: %s", err)
	}

	tg = &TaskGroup{
		Name:  "web",
		Count: 1,
		Tasks: []*Task{
			&Task{Name: "web"},
			&Task{Name: "web"},
			&Task{},
		},
		RestartPolicy: &RestartPolicy{
			Interval: 5 * time.Minute,
			Delay:    10 * time.Second,
			Attempts: 10,
		},
	}
	err = tg.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from task 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "Task 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTask_Validate(t *testing.T) {
	task := &Task{}
	err := task.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "task name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "task driver") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "task resources") {
		t.Fatalf("err: %s", err)
	}

	task = &Task{
		Name:      "web",
		Driver:    "docker",
		Resources: &Resources{},
	}
	err = task.Validate()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestConstraint_Validate(t *testing.T) {
	c := &Constraint{}
	err := c.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "Missing constraint operand") {
		t.Fatalf("err: %s", err)
	}

	c = &Constraint{
		LTarget: "$attr.kernel.name",
		RTarget: "linux",
		Operand: "=",
	}
	err = c.Validate()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Perform additional regexp validation
	c.Operand = ConstraintRegex
	c.RTarget = "(foo"
	err = c.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "missing closing") {
		t.Fatalf("err: %s", err)
	}

	// Perform version validation
	c.Operand = ConstraintVersion
	c.RTarget = "~> foo"
	err = c.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "Malformed constraint") {
		t.Fatalf("err: %s", err)
	}
}

func TestResource_NetIndex(t *testing.T) {
	r := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{Device: "eth0"},
			&NetworkResource{Device: "lo0"},
			&NetworkResource{Device: ""},
		},
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth0"}); idx != 0 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "lo0"}); idx != 1 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth1"}); idx != -1 {
		t.Fatalf("Bad: %d", idx)
	}
}

func TestResource_Superset(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
	}

	if s, _ := r1.Superset(r1); !s {
		t.Fatalf("bad")
	}
	if s, _ := r1.Superset(r2); !s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r1); s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r2); !s {
		t.Fatalf("bad")
	}
}

func TestResource_Add(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         100,
				ReservedPorts: []Port{{"ssh", 22}},
			},
		},
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
		Networks: []*NetworkResource{
			&NetworkResource{
				IP:            "10.0.0.1",
				MBits:         50,
				ReservedPorts: []Port{{"web", 80}},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		CPU:      3000,
		MemoryMB: 3072,
		DiskMB:   15000,
		IOPS:     150,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         150,
				ReservedPorts: []Port{{"ssh", 22}, {"web", 80}},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect, r1)
	}
}

func TestResource_Add_Network(t *testing.T) {
	r1 := &Resources{}
	r2 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        50,
				DynamicPorts: []Port{{"http", 0}, {"https", 0}},
			},
		},
	}
	r3 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        25,
				DynamicPorts: []Port{{"admin", 0}},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}
	err = r1.Add(r3)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        75,
				DynamicPorts: []Port{{"http", 0}, {"https", 0}, {"admin", 0}},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect.Networks[0], r1.Networks[0])
	}
}

func TestEncodeDecode(t *testing.T) {
	type FooRequest struct {
		Foo string
		Bar int
		Baz bool
	}
	arg := &FooRequest{
		Foo: "test",
		Bar: 42,
		Baz: true,
	}
	buf, err := Encode(1, arg)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	var out FooRequest
	err = Decode(buf[1:], &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !reflect.DeepEqual(arg, &out) {
		t.Fatalf("bad: %#v %#v", arg, out)
	}
}

func TestInvalidServiceCheck(t *testing.T) {
	s := Service{
		Id:        "service-id",
		Name:      "service-name",
		PortLabel: "bar",
		Checks: []ServiceCheck{
			{

				Id:   "check-id",
				Name: "check-name",
				Type: "lol",
			},
		},
	}
	if err := s.Validate(); err == nil {
		t.Fatalf("Service should be invalid")
	}
}

func TestDistinctCheckId(t *testing.T) {
	c1 := ServiceCheck{
		Name:     "web-health",
		Type:     "http",
		Path:     "/health",
		Interval: 2 * time.Second,
		Timeout:  3 * time.Second,
	}
	c2 := ServiceCheck{
		Name:     "web-health",
		Type:     "http",
		Path:     "/health1",
		Interval: 2 * time.Second,
		Timeout:  3 * time.Second,
	}

	c3 := ServiceCheck{
		Name:     "web-health",
		Type:     "http",
		Path:     "/health",
		Interval: 4 * time.Second,
		Timeout:  3 * time.Second,
	}

	if c1.Hash() == c2.Hash() || c1.Hash() == c3.Hash() || c3.Hash() == c2.Hash() {
		t.Fatalf("Checks need to be uniq c1: %s, c2: %s, c3: %s", c1.Hash(), c2.Hash(), c3.Hash())
	}

}
<commit_msg>Removing un-used imports<commit_after>package structs

import (
	"github.com/hashicorp/go-multierror"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestJob_Validate(t *testing.T) {
	j := &Job{}
	err := j.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "job region") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "job ID") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "job name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[3].Error(), "job type") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[4].Error(), "priority") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[5].Error(), "datacenters") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[6].Error(), "task groups") {
		t.Fatalf("err: %s", err)
	}

	j = &Job{
		Region:      "global",
		ID:          GenerateUUID(),
		Name:        "my-job",
		Type:        JobTypeService,
		Priority:    50,
		Datacenters: []string{"dc1"},
		TaskGroups: []*TaskGroup{
			&TaskGroup{
				Name: "web",
				RestartPolicy: &RestartPolicy{
					Interval: 5 * time.Minute,
					Delay:    10 * time.Second,
					Attempts: 10,
				},
			},
			&TaskGroup{
				Name: "web",
				RestartPolicy: &RestartPolicy{
					Interval: 5 * time.Minute,
					Delay:    10 * time.Second,
					Attempts: 10,
				},
			},
			&TaskGroup{
				RestartPolicy: &RestartPolicy{
					Interval: 5 * time.Minute,
					Delay:    10 * time.Second,
					Attempts: 10,
				},
			},
		},
	}
	err = j.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from group 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "group 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task group 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTaskGroup_Validate(t *testing.T) {
	tg := &TaskGroup{
		RestartPolicy: &RestartPolicy{
			Interval: 5 * time.Minute,
			Delay:    10 * time.Second,
			Attempts: 10,
		},
	}
	err := tg.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "group name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "count must be positive") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Missing tasks") {
		t.Fatalf("err: %s", err)
	}

	tg = &TaskGroup{
		Name:  "web",
		Count: 1,
		Tasks: []*Task{
			&Task{Name: "web"},
			&Task{Name: "web"},
			&Task{},
		},
		RestartPolicy: &RestartPolicy{
			Interval: 5 * time.Minute,
			Delay:    10 * time.Second,
			Attempts: 10,
		},
	}
	err = tg.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from task 1") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "Task 3 missing name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "Task 1 validation failed") {
		t.Fatalf("err: %s", err)
	}
}

func TestTask_Validate(t *testing.T) {
	task := &Task{}
	err := task.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "task name") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[1].Error(), "task driver") {
		t.Fatalf("err: %s", err)
	}
	if !strings.Contains(mErr.Errors[2].Error(), "task resources") {
		t.Fatalf("err: %s", err)
	}

	task = &Task{
		Name:      "web",
		Driver:    "docker",
		Resources: &Resources{},
	}
	err = task.Validate()
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestConstraint_Validate(t *testing.T) {
	c := &Constraint{}
	err := c.Validate()
	mErr := err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "Missing constraint operand") {
		t.Fatalf("err: %s", err)
	}

	c = &Constraint{
		LTarget: "$attr.kernel.name",
		RTarget: "linux",
		Operand: "=",
	}
	err = c.Validate()
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Perform additional regexp validation
	c.Operand = ConstraintRegex
	c.RTarget = "(foo"
	err = c.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "missing closing") {
		t.Fatalf("err: %s", err)
	}

	// Perform version validation
	c.Operand = ConstraintVersion
	c.RTarget = "~> foo"
	err = c.Validate()
	mErr = err.(*multierror.Error)
	if !strings.Contains(mErr.Errors[0].Error(), "Malformed constraint") {
		t.Fatalf("err: %s", err)
	}
}

func TestResource_NetIndex(t *testing.T) {
	r := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{Device: "eth0"},
			&NetworkResource{Device: "lo0"},
			&NetworkResource{Device: ""},
		},
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth0"}); idx != 0 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "lo0"}); idx != 1 {
		t.Fatalf("Bad: %d", idx)
	}
	if idx := r.NetIndex(&NetworkResource{Device: "eth1"}); idx != -1 {
		t.Fatalf("Bad: %d", idx)
	}
}

func TestResource_Superset(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
	}

	if s, _ := r1.Superset(r1); !s {
		t.Fatalf("bad")
	}
	if s, _ := r1.Superset(r2); !s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r1); s {
		t.Fatalf("bad")
	}
	if s, _ := r2.Superset(r2); !s {
		t.Fatalf("bad")
	}
}

func TestResource_Add(t *testing.T) {
	r1 := &Resources{
		CPU:      2000,
		MemoryMB: 2048,
		DiskMB:   10000,
		IOPS:     100,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         100,
				ReservedPorts: []Port{{"ssh", 22}},
			},
		},
	}
	r2 := &Resources{
		CPU:      2000,
		MemoryMB: 1024,
		DiskMB:   5000,
		IOPS:     50,
		Networks: []*NetworkResource{
			&NetworkResource{
				IP:            "10.0.0.1",
				MBits:         50,
				ReservedPorts: []Port{{"web", 80}},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		CPU:      3000,
		MemoryMB: 3072,
		DiskMB:   15000,
		IOPS:     150,
		Networks: []*NetworkResource{
			&NetworkResource{
				CIDR:          "10.0.0.0/8",
				MBits:         150,
				ReservedPorts: []Port{{"ssh", 22}, {"web", 80}},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect, r1)
	}
}

func TestResource_Add_Network(t *testing.T) {
	r1 := &Resources{}
	r2 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        50,
				DynamicPorts: []Port{{"http", 0}, {"https", 0}},
			},
		},
	}
	r3 := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        25,
				DynamicPorts: []Port{{"admin", 0}},
			},
		},
	}

	err := r1.Add(r2)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}
	err = r1.Add(r3)
	if err != nil {
		t.Fatalf("Err: %v", err)
	}

	expect := &Resources{
		Networks: []*NetworkResource{
			&NetworkResource{
				MBits:        75,
				DynamicPorts: []Port{{"http", 0}, {"https", 0}, {"admin", 0}},
			},
		},
	}

	if !reflect.DeepEqual(expect.Networks, r1.Networks) {
		t.Fatalf("bad: %#v %#v", expect.Networks[0], r1.Networks[0])
	}
}

func TestEncodeDecode(t *testing.T) {
	type FooRequest struct {
		Foo string
		Bar int
		Baz bool
	}
	arg := &FooRequest{
		Foo: "test",
		Bar: 42,
		Baz: true,
	}
	buf, err := Encode(1, arg)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	var out FooRequest
	err = Decode(buf[1:], &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if !reflect.DeepEqual(arg, &out) {
		t.Fatalf("bad: %#v %#v", arg, out)
	}
}

func TestInvalidServiceCheck(t *testing.T) {
	s := Service{
		Id:        "service-id",
		Name:      "service-name",
		PortLabel: "bar",
		Checks: []ServiceCheck{
			{

				Id:   "check-id",
				Name: "check-name",
				Type: "lol",
			},
		},
	}
	if err := s.Validate(); err == nil {
		t.Fatalf("Service should be invalid")
	}
}

func TestDistinctCheckId(t *testing.T) {
	c1 := ServiceCheck{
		Name:     "web-health",
		Type:     "http",
		Path:     "/health",
		Interval: 2 * time.Second,
		Timeout:  3 * time.Second,
	}
	c2 := ServiceCheck{
		Name:     "web-health",
		Type:     "http",
		Path:     "/health1",
		Interval: 2 * time.Second,
		Timeout:  3 * time.Second,
	}

	c3 := ServiceCheck{
		Name:     "web-health",
		Type:     "http",
		Path:     "/health",
		Interval: 4 * time.Second,
		Timeout:  3 * time.Second,
	}

	if c1.Hash() == c2.Hash() || c1.Hash() == c3.Hash() || c3.Hash() == c2.Hash() {
		t.Fatalf("Checks need to be uniq c1: %s, c2: %s, c3: %s", c1.Hash(), c2.Hash(), c3.Hash())
	}

}
<|endoftext|>


<commit_before>// Author: Yoshiyuki Koyanagi <moutend@gmail.com>
// License: mIT

package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	medium "github.com/moutend/go-medium"
	"github.com/skratchdot/open-golang/open"
)

type Token struct {
	ApplicationID     string
	ApplicationSecret string
	AccessToken       string
	ExpiresAt         int
}

var (
	version       = "0.1.0"
	revision      = "latest"
	tokenFilePath string
)

const tokenFileName = ".mediumctl"

func showPostedArticleInfo(p *medium.PostedArticle) {
	fmt.Println("Your article was successfully posted.\n")
	fmt.Printf("Title: %s\n", p.Title)
	fmt.Printf("Status: %s\n", p.PublishStatus)
	if len(p.Tags) > 0 {
		fmt.Printf("Tags: %s\n", strings.Join(p.Tags, " "))
	}
	fmt.Printf("URL: %s\n", p.URL)
	if p.CanonicalURL != "" {
		fmt.Printf("Canonical URL: %s\n", p.CanonicalURL)
	}
	return
}

func parseArticle(filename string) (article medium.Article, err error) {
	format := "markdown"
	if strings.HasSuffix(filename, "html") || strings.HasSuffix(filename, "htm") {
		format = "html"
	}
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	lines := strings.Split(string(b), "\n")
	if len(lines) < 1 {
		err = fmt.Errorf("%s is empty", filename)
		return
	}
	var (
		title        string
		tags         []string
		content      string
		license      string
		status       string
		canonicalURL string
		notify       bool
	)

	title = "untitled"
	status = "public"

	for i, line := range lines[1:] {
		if strings.HasPrefix(line, "---") {
			content = strings.Join(lines[i+1:], "\n")
			break
		}
		if strings.HasPrefix(line, "title: ") {
			title = line[len("title: "):]
		}
		if strings.HasPrefix(line, "notify: true") {
			notify = true
		}

		if strings.HasPrefix(line, "status: ") {
			status = line[len("status: "):]
		}
		if strings.HasPrefix(line, "license: ") {
			license = line[len("license: "):]
		}
		if strings.HasPrefix(line, "canonicalURL: ") {
			canonicalURL = line[len("canonicalURL: "):]
		}
		if strings.HasPrefix(line, "tags: ") {
			tags = strings.Split(line[len("tags: "):], " ")
		}
	}
	article = medium.Article{
		Title:           title,
		ContentFormat:   format,
		Content:         content,
		CanonicalURL:    canonicalURL,
		Tags:            tags,
		PublishStatus:   status,
		License:         license,
		NotifyFollowers: notify,
	}
	return
}
func getCode(clientID, redirectURI string) (code string, err error) {
	l, err := net.Listen("tcp", "192.168.1.107:4000")
	if err != nil {
		return
	}
	defer l.Close()

	type value struct {
		code  string
		error error
	}
	quit := make(chan value)
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
		w.(http.Flusher).Flush()
		c := req.FormValue("code")
		e := req.FormValue("error")
		v := value{
			code:  c,
			error: nil,
		}
		if e != "" {
			v.error = fmt.Errorf(e)
		}
		quit <- v
	}))
	stateBytes := make([]byte, 88)
	_, err = rand.Read(stateBytes)
	if err != nil {
		return
	}
	state := fmt.Sprintf("%x", stateBytes)
	scope := "basicProfile,listPublications,publishPost"
	redirectURI = url.QueryEscape(redirectURI)
	q := fmt.Sprintf("client_id=%s&scope=%s&state=%s&response_type=code&redirect_uri=%s", clientID, scope, state, redirectURI)
	p := "https://medium.com/m/oauth/authorize?" + q
	if err = open.Start(p); err != nil {
		return
	}
	select {
	case v := <-quit:
		if v.error != nil {
			return "", v.error
		}
		return v.code, nil
	case <-time.After(60 * time.Second):
		return "", fmt.Errorf("timeout")
	}
}

func saveToken(clientID, clientSecret string, token *medium.Token) (err error) {
	b, err := json.Marshal(Token{
		ApplicationID:     clientID,
		ApplicationSecret: clientSecret,
		AccessToken:       token.AccessToken,
		ExpiresAt:         token.ExpiresAt,
	})
	if err != nil {
		return
	}
	err = ioutil.WriteFile(tokenFilePath, b, 0644)
	return
}

func loadToken() (*Token, error) {
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("API token is not set. Please run 'auth' at first.")
	}
	var token Token
	err = json.Unmarshal(b, &token)
	return &token, err
}

func main() {
	err := Run(os.Args)

	if err != nil {
		log.New(os.Stderr, "error: ", 0).Fatal(err)
		os.Exit(1)
	}

	os.Exit(0)
}
func Run(args []string) (err error) {
	if len(args) < 2 {
		return Help(args)
	}
	u, err := user.Current()
	if err != nil {
		return
	}
	tokenFilePath = filepath.Join(u.HomeDir, tokenFileName)
	switch args[1] {
	case "auth":
		err = Auth(args)
	case "info":
		err = Info(args)
	case "p":
		err = Post(args, false)
	case "publication":
	case "u":
		err = Post(args, true)
	case "user":
		err = Post(args, true)
	case "version":
		err = Version(args)
	case "help":
		err = Help(args)
	default:
		fmt.Fprintf(os.Stderr, "%s: '%s' is not a %s subcommand.\n", args[0], args[1], args[0])
		err = Help(args)
	}
	return
}

func Auth(args []string) (err error) {
	var (
		clientIDFlag     string
		clientSecretFlag string
		debugFlag        bool
		redirectURIFlag  string
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.StringVar(&redirectURIFlag, "u", "", "Redirect URI for OAuth application.")
	f.StringVar(&clientIDFlag, "i", "", "Client ID of OAuth application.")
	f.StringVar(&clientSecretFlag, "s", "", "Client secret of OAuth application.")
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])
	if redirectURIFlag == "" {
		return fmt.Errorf("please specify redirect URI")
	}
	if clientIDFlag == "" {
		return fmt.Errorf("please specify client ID")
	}
	if clientSecretFlag == "" {
		return fmt.Errorf("please specify client secret")
	}

	code, err := getCode(clientIDFlag, redirectURIFlag)
	if err != nil {
		return
	}
	c := medium.NewClient(clientIDFlag, clientSecretFlag, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	token, err := c.Token(code, redirectURIFlag)
	if err != nil {
		return
	}
	if err = saveToken(clientIDFlag, clientSecretFlag, token); err != nil {
		return
	}
	fmt.Printf("Your API token was successfully saved in '%s'.\n", tokenFilePath)
	fmt.Println("Note: This file should be treated as the password and please do NOT expose it.")
	return
}

func Info(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	fmt.Printf("Name: %s\n", u.Name)
	fmt.Printf("Username: %s\n", u.Username)
	fmt.Printf("URL: %s", u.URL)
	fmt.Printf("\n")

	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		fmt.Println("You have no publications yet.")
		return
	}
	fmt.Println("You have publication(s) below:")
	for _, p := range ps {
		fmt.Printf("Name: %s\n", p.Name)
		fmt.Printf("Description: %s\n", p.Description)
		fmt.Printf("URL: %s\n", p.URL)
	}
	return
}

func Post(args []string, userFlag bool) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	article, err := parseArticle(f.Args()[0])
	if err != nil {
		return
	}
	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	if userFlag {
		p, err := u.Post(article)
		if err != nil {
			return err
		}
		showPostedArticleInfo(p)
		return nil
	}
	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		return fmt.Errorf("you have no publications yet")
	}
	p, err := ps[0].Post(article)
	if err != nil {
		return
	}
	showPostedArticleInfo(p)
	return
}

func Version(args []string) (err error) {
	fmt.Printf("%s-%s\n", version, revision)
	return
}

func Help(args []string) (err error) {
	fmt.Println(`usage: mediumctl [command] [options]

Command:
  auth
    Setting up API token for Medium with OAuth.
  info
    Show the information about current user and its publications.
  user, u
    Post HTML or Markdown file to current user profile.
  publication, p
    Post HTML or Markdown file to current user's publication.
  version
  Show version and revision information.
  help
    Show this message.

For more information, please see https://github.com/moutend/mediumctl.`)
	return
}
<commit_msg>Support publication property inside frontmatter and refactor over all<commit_after>// Author: Yoshiyuki Koyanagi <moutend@gmail.com>
// License: mIT

// Package main implements mediumctl.
package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	medium "github.com/moutend/go-medium"
	"github.com/skratchdot/open-golang/open"
)

type token struct {
	ApplicationID     string
	ApplicationSecret string
	AccessToken       string
	ExpiresAt         int
}

var (
	version       = "v0.1.1"
	revision      = "latest"
	tokenFilePath string
)

const tokenFileName = ".mediumctl"

func showPostedArticleInfo(p *medium.PostedArticle) {
	fmt.Println("Your article was successfully posted.")
	fmt.Printf("Title: %s\n", p.Title)
	fmt.Printf("Status: %s\n", p.PublishStatus)
	if len(p.Tags) > 0 {
		fmt.Printf("Tags: %s\n", strings.Join(p.Tags, " "))
	}
	fmt.Printf("URL: %s\n", p.URL)
	if p.CanonicalURL != "" {
		fmt.Printf("Canonical URL: %s\n", p.CanonicalURL)
	}
	return
}

func parseArticle(filename string) (article medium.Article, publicationNumber int, err error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("%s is empty", filename)
		return
	}

	var (
		title        string
		tags         []string
		content      string
		format       string
		license      string
		status       string
		canonicalURL string
		notify       bool
	)
	format = "markdown"
	if strings.HasSuffix(filename, "html") || strings.HasSuffix(filename, "htm") {
		format = "html"
	}
	title = "untitled"
	status = "public"
	lines := strings.Split(string(b), "\n")

	for i, line := range lines[1:] {
		if strings.HasPrefix(line, "---") {
			content = strings.Join(lines[i+1:], "\n")
			break
		}
		if strings.HasPrefix(line, "publication: ") {
			publicationNumber, err = strconv.Atoi(line[len("publication: "):])
			if err != nil {
				return
			}
		}
		if strings.HasPrefix(line, "title: ") {
			title = line[len("title: "):]
		}
		if strings.HasPrefix(line, "tags: ") {
			tags = strings.Split(line[len("tags: "):], " ")
		}
		if strings.HasPrefix(line, "notify: true") {
			notify = true
		}
		if strings.HasPrefix(line, "status: ") {
			status = line[len("status: "):]
		}
		if strings.HasPrefix(line, "license: ") {
			license = line[len("license: "):]
		}
		if strings.HasPrefix(line, "canonicalURL: ") {
			canonicalURL = line[len("canonicalURL: "):]
		}
	}
	article = medium.Article{
		Title:           title,
		ContentFormat:   format,
		Content:         content,
		CanonicalURL:    canonicalURL,
		Tags:            tags,
		PublishStatus:   status,
		License:         license,
		NotifyFollowers: notify,
	}
	return
}
func getCode(clientID, redirectURI string) (code string, err error) {
	l, err := net.Listen("tcp", "192.168.1.107:4000")
	if err != nil {
		return
	}
	defer l.Close()

	type value struct {
		code  string
		error error
	}
	quit := make(chan value)
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
		w.(http.Flusher).Flush()
		c := req.FormValue("code")
		e := req.FormValue("error")
		v := value{
			code:  c,
			error: nil,
		}
		if e != "" {
			v.error = fmt.Errorf(e)
		}
		quit <- v
	}))
	stateBytes := make([]byte, 88)
	_, err = rand.Read(stateBytes)
	if err != nil {
		return
	}
	state := fmt.Sprintf("%x", stateBytes)
	scope := "basicProfile,listPublications,publishPost"
	redirectURI = url.QueryEscape(redirectURI)
	q := fmt.Sprintf("client_id=%s&scope=%s&state=%s&response_type=code&redirect_uri=%s", clientID, scope, state, redirectURI)
	p := "https://medium.com/m/oauth/authorize?" + q
	if err = open.Start(p); err != nil {
		return
	}
	select {
	case v := <-quit:
		if v.error != nil {
			return "", v.error
		}
		return v.code, nil
	case <-time.After(60 * time.Second):
		return "", fmt.Errorf("timeout")
	}
}

func saveToken(clientID, clientSecret string, t *medium.Token) (err error) {
	b, err := json.Marshal(token{
		ApplicationID:     clientID,
		ApplicationSecret: clientSecret,
		AccessToken:       t.AccessToken,
		ExpiresAt:         t.ExpiresAt,
	})
	if err != nil {
		return
	}
	err = ioutil.WriteFile(tokenFilePath, b, 0644)
	return
}

func loadToken() (*token, error) {
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("API token is not set. Please run 'auth' at first")
	}
	var t token
	err = json.Unmarshal(b, &t)
	return &t, err
}

func main() {
	err := run(os.Args)

	if err != nil {
		log.New(os.Stderr, "error: ", 0).Fatal(err)
		os.Exit(1)
	}

	os.Exit(0)
}
func run(args []string) (err error) {
	if len(args) < 2 {
		return helpCommand(args)
	}
	u, err := user.Current()
	if err != nil {
		return
	}
	tokenFilePath = filepath.Join(u.HomeDir, tokenFileName)
	switch args[1] {
	case "auth":
		err = authCommand(args)
	case "info":
		err = infoCommand(args)
	case "p":
		err = postCommand(args, false)
	case "publication":
		err = postCommand(args, false)
	case "u":
		err = postCommand(args, true)
	case "user":
		err = postCommand(args, true)
	case "version":
		err = versionCommand(args)
	case "help":
		err = helpCommand(args)
	default:
		fmt.Fprintf(os.Stderr, "%s: '%s' is not a %s subcommand.\n", args[0], args[1], args[0])
		err = helpCommand(args)
	}
	return
}

func authCommand(args []string) (err error) {
	var (
		clientIDFlag     string
		clientSecretFlag string
		debugFlag        bool
		redirectURIFlag  string
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.StringVar(&redirectURIFlag, "u", "", "Redirect URI for OAuth application.")
	f.StringVar(&clientIDFlag, "i", "", "Client ID of OAuth application.")
	f.StringVar(&clientSecretFlag, "s", "", "Client secret of OAuth application.")
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])
	if redirectURIFlag == "" {
		return fmt.Errorf("please specify redirect URI")
	}
	if clientIDFlag == "" {
		return fmt.Errorf("please specify client ID")
	}
	if clientSecretFlag == "" {
		return fmt.Errorf("please specify client secret")
	}

	code, err := getCode(clientIDFlag, redirectURIFlag)
	if err != nil {
		return
	}
	c := medium.NewClient(clientIDFlag, clientSecretFlag, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	token, err := c.Token(code, redirectURIFlag)
	if err != nil {
		return
	}
	if err = saveToken(clientIDFlag, clientSecretFlag, token); err != nil {
		return
	}
	fmt.Printf("Your API token was successfully saved in '%s'.\n", tokenFilePath)
	fmt.Println("Note: This file should be treated as the password and please do NOT expose it.")
	return
}

func infoCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	fmt.Printf("Name: %s\n", u.Name)
	fmt.Printf("Username: %s\n", u.Username)
	fmt.Printf("URL: %s", u.URL)
	fmt.Printf("\n")

	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		fmt.Println("You have no publications yet.")
		return
	}
	fmt.Println("You have publication(s) below:")
	for _, p := range ps {
		fmt.Printf("Name: %s\n", p.Name)
		fmt.Printf("Description: %s\n", p.Description)
		fmt.Printf("URL: %s\n", p.URL)
	}
	return
}

func postCommand(args []string, userFlag bool) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	article, publicationNumber, err := parseArticle(f.Args()[0])
	if err != nil {
		return
	}
	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	if userFlag {
		p, err := u.Post(article)
		if err != nil {
			return err
		}
		showPostedArticleInfo(p)
		return nil
	}
	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		return fmt.Errorf("you have no publications yet")
	}
	if publicationNumber < 0 || publicationNumber > len(ps)-1 {
		err = fmt.Errorf("publication number '%d' is invalid", publicationNumber)
		return
	}
	p, err := ps[publicationNumber].Post(article)
	if err != nil {
		return
	}
	showPostedArticleInfo(p)
	return
}

func versionCommand(args []string) (err error) {
	fmt.Printf("%s-%s\n", version, revision)
	return
}

func helpCommand(args []string) (err error) {
	fmt.Println(`usage: mediumctl [command] [options]

Command:
  auth
    Setting up API token for Medium with OAuth.
  info
    Show the information about current user and its publications.
  user, u
    Post HTML or Markdown file to current user profile.
  publication, p
    Post HTML or Markdown file to current user's publication.
  version
  Show version and revision information.
  help
    Show this message.

For more information, please see https://github.com/moutend/mediumctl.`)
	return
}
<|endoftext|>


<commit_before>package main

import (
	// "encoding/hex"
	"fmt"
	"os/exec"
	"bytes"
	// "time"
	// "math/big"
	// "github.com/PointCoin/btcutil"
	// "github.com/PointCoin/btcwire"
	// "github.com/PointCoin/btcrpcclient"
	// "github.com/PointCoin/btcjson"
	"strconv"
	"strings"
	"encoding/json"
	// "regexp"
	// "math/rand"
	"log"
)

func main() {

	print := fmt.Println
	print("\n\n")
	var txdetails string
	for {
		var inputTx string
		fmt.Printf("%s", "Enter tx hash: ")
		fmt.Scanf("%s", &inputTx)

		txdetails = getTransactionDetails(inputTx)
		if txdetails != "no" {
			break
		}
	}
	for {
		txdetailsbytes := []byte(txdetails)
		var f interface{}
		_ = json.Unmarshal(txdetailsbytes, &f)
		m := f.(map[string]interface{})
		txidreturned  := m["txid"]
		print("\n\nTransaction ID:", txidreturned)

		vinList := getVinList(m)
		voutList := getVoutList(m)

		prevOutputs := make([]string, 0)

		fmt.Println("From:")
		for i, x := range vinList {
			index := strconv.Itoa(i)

			if x.coinbase == true{
				print ("\t[" + string(index) + "] Coinbase Transaction (10 PTC)")
				prevOutputs = append(prevOutputs, "coinbase")
				continue
			}
			tx := getTransactionDetails(x.txid)
			prevOutputs = append(prevOutputs, tx)
			txjs := getTransactionJson(tx)
			txvouts := getVoutList(txjs)
			for _, y := range txvouts {

				if y.n == x.vout {
					fmt.Println("\t[" + string(index) + "]",y.addresses[0], "(" + FloatToString(y.value) + " PTC)")
					break
				}
			}
		}

		fmt.Println("To:")
		for i, x := range voutList {
			index := strconv.Itoa(i)
			fmt.Println("\t[" + string(index) + "] " + x.addresses[0] + " (" + FloatToString(x.value) + " PTC)" )
		}

		nextIndex := getIndexInput(len(prevOutputs),"\nEnter index of \"From\" address to see output tx:")
		savedDetails := txdetails
		txdetails = prevOutputs[nextIndex]
		if txdetails == "coinbase" {
			fmt.Println("Oh you think Pointcoin is your ally, but you merely adopted the pointcoin. This coinbase transaction was born in it. Molded by it.")
			txdetails = savedDetails
		}

	}

}

type vin struct {
	coinbase bool
    txid string
    vout  int
}

type vout struct {
	value float64
	n int
	addresses []string
}

func getIndexInput(size int, msg string)(int) {
	for {
		fmt.Printf("%s", msg)
		var nextIndex int
		fmt.Scanf("%d", &nextIndex)
		// fmt.Println(nextIndex)
		if nextIndex < 0 || nextIndex >= size {
			fmt.Println("Invalid index")
			continue
		}
		return nextIndex
	}
}
func getVinList(m map[string]interface{}) ([]vin) {
	vinList := make([]vin,0)
	vinJsonList := m["vin"]

	switch vv := vinJsonList.(type) {
	case []interface{}:
		for _, u := range vv {
			j := u.(map[string]interface{})
			var newVin vin
			if _,ok := j["coinbase"]; ok {
				// this is a coinbase transaction w/ coinbase input
				newVin = vin{coinbase:true, txid:"null", vout:0}
			} else {
				vinTxid := j["txid"].(string)
				vinVout := int(j["vout"].(float64))
				newVin = vin{coinbase:false, txid: vinTxid, vout: vinVout}
	            // fmt.Println(i, u)
			}
			vinList = append(vinList, newVin)

        }
		// print("yes matches")
	default:
		print("nope getVinList didn't work")
	}

	// fmt.Println("vins:")
	// for _,x := range vinList {
	// 	fmt.Println(x)
	// }
	return vinList

}

func getVoutList(m map[string] interface{}) ([]vout) {
	voutList := make([]vout,0)
	voutJsonList := m["vout"]

	switch oo := voutJsonList.(type) {
	case []interface{}:
		for _,u := range oo {
			j := u.(map[string]interface{})
			voutVal := j["value"].(float64)
			voutN := int(j["n"].(float64))

			vScriptPubKey := j["scriptPubKey"].(map[string]interface{})
			vAddresses := vScriptPubKey["addresses"].([]interface{})
			vAddressesStrings := make([]string, 0)
			for _,u := range vAddresses {
				addr := u.(string)
				vAddressesStrings = append(vAddressesStrings, addr)
			}

			newVout := vout{value: voutVal, n: voutN, addresses: vAddressesStrings}
			voutList = append(voutList, newVout)
		}
	}

	// fmt.Println("vouts:")
	// for _,x := range voutList {
	// 	fmt.Println(x)
	// }
	return voutList
}

func getTransactionJson(txdetails string) (map[string]interface{}){
	txdetailsbytes := []byte(txdetails)

	var f interface{}
	_ = json.Unmarshal(txdetailsbytes, &f)
	m := f.(map[string]interface{})
	return m
}

func getTransactionDetails(txhash string) (string){
	// command = pointctl getrawtransaction d2011b19dea6e98ec8bf78bd224856e76b6a9c460bbb347e49adb3dcf457e548
	cmd := exec.Command("pointctl", "getrawtransaction", txhash)

	var out bytes.Buffer
    cmd.Stdout = &out
    err := cmd.Run()
    if err != nil {
    	fmt.Println("Invalid ash or no Connection to pointcoind. Try again.")
    	return "no"
    	// log.Fatal(err)
    }

	cmd2 := exec.Command("xargs", "pointctl", "decoderawtransaction")
	cmd2.Stdin = strings.NewReader(out.String())
	var out2 bytes.Buffer
    cmd2.Stdout = &out2
    err2 := cmd2.Run()
    if err2 != nil {
    	log.Fatal(err2)
    }
    // fmt.Println(out2.String())

    return out2.String()
}

func FloatToString(input_num float64) string {
    // to convert a float number to a string
    return strconv.FormatFloat(input_num, 'f', -1, 64)
}

<commit_msg>Final commit<commit_after>// This must be run on a computer with pointcoind running and pointctl installed

package main

import (
	// "encoding/hex"
	"fmt"
	"os/exec"
	"bytes"
	// "time"
	// "math/big"
	// "github.com/PointCoin/btcutil"
	// "github.com/PointCoin/btcwire"
	// "github.com/PointCoin/btcrpcclient"
	// "github.com/PointCoin/btcjson"
	"strconv"
	"strings"
	"encoding/json"
	// "regexp"
	// "math/rand"
	"log"
)

func main() {

	print := fmt.Println
	print("\n\n")
	var txdetails string
	for {
		var inputTx string
		fmt.Printf("%s", "Enter tx hash: ")
		fmt.Scanf("%s", &inputTx)

		txdetails = getTransactionDetails(inputTx)
		if txdetails != "no" {
			break
		}
	}
	for {
		txdetailsbytes := []byte(txdetails)
		var f interface{}
		_ = json.Unmarshal(txdetailsbytes, &f)
		m := f.(map[string]interface{})
		txidreturned  := m["txid"]
		print("\n\nTransaction ID:", txidreturned)

		vinList := getVinList(m)
		voutList := getVoutList(m)

		prevOutputs := make([]string, 0)

		fmt.Println("From:")
		for i, x := range vinList {
			index := strconv.Itoa(i)

			if x.coinbase == true{
				print ("\t[" + string(index) + "] Coinbase Transaction (10 PTC)")
				prevOutputs = append(prevOutputs, "coinbase")
				continue
			}
			tx := getTransactionDetails(x.txid)
			prevOutputs = append(prevOutputs, tx)
			txjs := getTransactionJson(tx)
			txvouts := getVoutList(txjs)
			for _, y := range txvouts {

				if y.n == x.vout {
					fmt.Println("\t[" + string(index) + "]",y.addresses[0], "(" + FloatToString(y.value) + " PTC)")
					break
				}
			}
		}

		fmt.Println("To:")
		for i, x := range voutList {
			index := strconv.Itoa(i)
			fmt.Println("\t[" + string(index) + "] " + x.addresses[0] + " (" + FloatToString(x.value) + " PTC)" )
		}

		nextIndex := getIndexInput(len(prevOutputs),"\nEnter index of \"From\" address to see output tx:")
		savedDetails := txdetails
		txdetails = prevOutputs[nextIndex]
		if txdetails == "coinbase" {
			fmt.Println("Oh you think Pointcoin is your ally, but you merely adopted the pointcoin. This coinbase transaction was born in it. Molded by it.")
			txdetails = savedDetails
		}

	}

}

type vin struct {
	coinbase bool
    txid string
    vout  int
}

type vout struct {
	value float64
	n int
	addresses []string
}

func getIndexInput(size int, msg string)(int) {
	for {
		fmt.Printf("%s", msg)
		var nextIndex int
		fmt.Scanf("%d", &nextIndex)
		// fmt.Println(nextIndex)
		if nextIndex < 0 || nextIndex >= size {
			fmt.Println("Invalid index")
			continue
		}
		return nextIndex
	}
}
func getVinList(m map[string]interface{}) ([]vin) {
	vinList := make([]vin,0)
	vinJsonList := m["vin"]

	switch vv := vinJsonList.(type) {
	case []interface{}:
		for _, u := range vv {
			j := u.(map[string]interface{})
			var newVin vin
			if _,ok := j["coinbase"]; ok {
				// this is a coinbase transaction w/ coinbase input
				newVin = vin{coinbase:true, txid:"null", vout:0}
			} else {
				vinTxid := j["txid"].(string)
				vinVout := int(j["vout"].(float64))
				newVin = vin{coinbase:false, txid: vinTxid, vout: vinVout}
	            // fmt.Println(i, u)
			}
			vinList = append(vinList, newVin)

        }
		// print("yes matches")
	default:
		print("nope getVinList didn't work")
	}

	// fmt.Println("vins:")
	// for _,x := range vinList {
	// 	fmt.Println(x)
	// }
	return vinList

}

func getVoutList(m map[string] interface{}) ([]vout) {
	voutList := make([]vout,0)
	voutJsonList := m["vout"]

	switch oo := voutJsonList.(type) {
	case []interface{}:
		for _,u := range oo {
			j := u.(map[string]interface{})
			voutVal := j["value"].(float64)
			voutN := int(j["n"].(float64))

			vScriptPubKey := j["scriptPubKey"].(map[string]interface{})
			vAddresses := vScriptPubKey["addresses"].([]interface{})
			vAddressesStrings := make([]string, 0)
			for _,u := range vAddresses {
				addr := u.(string)
				vAddressesStrings = append(vAddressesStrings, addr)
			}

			newVout := vout{value: voutVal, n: voutN, addresses: vAddressesStrings}
			voutList = append(voutList, newVout)
		}
	}

	// fmt.Println("vouts:")
	// for _,x := range voutList {
	// 	fmt.Println(x)
	// }
	return voutList
}

func getTransactionJson(txdetails string) (map[string]interface{}){
	txdetailsbytes := []byte(txdetails)

	var f interface{}
	_ = json.Unmarshal(txdetailsbytes, &f)
	m := f.(map[string]interface{})
	return m
}

func getTransactionDetails(txhash string) (string){
	// command = pointctl getrawtransaction d2011b19dea6e98ec8bf78bd224856e76b6a9c460bbb347e49adb3dcf457e548
	cmd := exec.Command("pointctl", "getrawtransaction", txhash)

	var out bytes.Buffer
    cmd.Stdout = &out
    err := cmd.Run()
    if err != nil {
    	fmt.Println("Invalid ash or no Connection to pointcoind. Try again.")
    	return "no"
    	// log.Fatal(err)
    }

	cmd2 := exec.Command("xargs", "pointctl", "decoderawtransaction")
	cmd2.Stdin = strings.NewReader(out.String())
	var out2 bytes.Buffer
    cmd2.Stdout = &out2
    err2 := cmd2.Run()
    if err2 != nil {
    	log.Fatal(err2)
    }
    // fmt.Println(out2.String())

    return out2.String()
}

func FloatToString(input_num float64) string {
    // to convert a float number to a string
    return strconv.FormatFloat(input_num, 'f', -1, 64)
}

<|endoftext|>


<commit_before>package locket

import (
	"code.cloudfoundry.org/cfhttp"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/locket/models"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type ClientLocketConfig struct {
	LocketAddress        string `json:"locket_address,omitempty" yaml:"locket_address,omitempty"`
	LocketCACertFile     string `json:"locket_ca_cert_file,omitempty" yaml:"locket_ca_cert_file,omitempty"`
	LocketClientCertFile string `json:"locket_ca_cert_file,omitempty" yaml:"locket_client_cert_file,omitempty"`
	LocketClientKeyFile  string `json:"locket_client_key_file,omitempty" yaml:"locket_client_key_file,omitempty"`
}

func NewClient(logger lager.Logger, config ClientLocketConfig) (models.LocketClient, error) {
	locketTLSConfig, err := cfhttp.NewTLSConfig(config.LocketClientCertFile, config.LocketClientKeyFile, config.LocketCACertFile)
	if err != nil {
		return nil, err
	}

	conn, err := grpc.Dial(config.LocketAddress, grpc.WithTransportCredentials(credentials.NewTLS(locketTLSConfig)))
	if err != nil {
		return nil, err
	}
	return models.NewLocketClient(conn), nil
}
<commit_msg>Fix LocketClientCertFile json property<commit_after>package locket

import (
	"code.cloudfoundry.org/cfhttp"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/locket/models"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type ClientLocketConfig struct {
	LocketAddress        string `json:"locket_address,omitempty" yaml:"locket_address,omitempty"`
	LocketCACertFile     string `json:"locket_ca_cert_file,omitempty" yaml:"locket_ca_cert_file,omitempty"`
	LocketClientCertFile string `json:"locket_client_cert_file,omitempty" yaml:"locket_client_cert_file,omitempty"`
	LocketClientKeyFile  string `json:"locket_client_key_file,omitempty" yaml:"locket_client_key_file,omitempty"`
}

func NewClient(logger lager.Logger, config ClientLocketConfig) (models.LocketClient, error) {
	locketTLSConfig, err := cfhttp.NewTLSConfig(config.LocketClientCertFile, config.LocketClientKeyFile, config.LocketCACertFile)
	if err != nil {
		logger.Error("failed-to-open-tls-config", err, lager.Data{"keypath": config.LocketClientKeyFile, "certpath": config.LocketClientCertFile, "capath": config.LocketCACertFile})
		return nil, err
	}

	conn, err := grpc.Dial(config.LocketAddress, grpc.WithTransportCredentials(credentials.NewTLS(locketTLSConfig)))
	if err != nil {
		return nil, err
	}
	return models.NewLocketClient(conn), nil
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "log"
    "time"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    _ "net/http/pprof"
)

var DEBUG bool
var CLIENT_BROAD bool

func main() {
    defer func() {
        if err := recover(); err != nil {
            log.Println("FATAL: ", err)

            log.Println("clearing redis memory...")
            log.Println("Shutting down...")
        }
    }()

    conf  := initConfig()
    store := initStore(&conf)
    initLogger(conf)

    go func() {
        for {
            log.Println(store.memory.clientCount)
            time.Sleep(20 * time.Second)
        }
    }()

    signals := make(chan os.Signal, 1)
    signal.Notify(signals, syscall.SIGINT, syscall.SIGUSR1)
    InstallSignalHandlers(signals)

    CLIENT_BROAD = conf.GetBool("client_broadcasts")
    server := createServer(&conf, &store)

    go server.initAppListener()
    go server.initSocketListener()
    go server.initPingListener()

    listenAddr := fmt.Sprintf(":%s", conf.Get("listening_port"))
    err := http.ListenAndServe(listenAddr, nil)
    if err != nil {
        log.Fatal(err)
    }
}

func InstallSignalHandlers(signals chan os.Signal) {
    go func() {
        sig := <-signals
        switch sig {
        case syscall.SIGINT:
            log.Println("\nCtrl-C signalled\n")
            os.Exit(0)
        }
    }()
}

func initLogger(conf Configuration) {
    fi, err := os.OpenFile("/var/log/incus.log", os.O_RDWR|os.O_APPEND, 0660);
    if err != nil {
        log.Fatalf("Error: %v", err.Error());
    }

    log.SetOutput(fi)

    DEBUG = false
    if conf.Get("log_level") == "debug" {
        DEBUG = true
    }
}<commit_msg>added sigkill into the signal handler<commit_after>package main

import (
    "net/http"
    "log"
    "time"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    _ "net/http/pprof"
)

var DEBUG bool
var CLIENT_BROAD bool

func main() {
    defer func() {
        if err := recover(); err != nil {
            log.Println("FATAL: ", err)

            log.Println("clearing redis memory...")
            log.Println("Shutting down...")
        }
    }()

    conf  := initConfig()
    store := initStore(&conf)
    initLogger(conf)

    go func() {
        for {
            log.Println(store.memory.clientCount)
            time.Sleep(20 * time.Second)
        }
    }()

    signals := make(chan os.Signal, 1)
    signal.Notify(signals, syscall.SIGINT, syscall.SIGKILL)
    InstallSignalHandlers(signals)

    CLIENT_BROAD = conf.GetBool("client_broadcasts")
    server := createServer(&conf, &store)

    go server.initAppListener()
    go server.initSocketListener()
    go server.initPingListener()

    listenAddr := fmt.Sprintf(":%s", conf.Get("listening_port"))
    err := http.ListenAndServe(listenAddr, nil)
    if err != nil {
        log.Fatal(err)
    }
}

func InstallSignalHandlers(signals chan os.Signal) {
    go func() {
        sig := <-signals
        switch sig {
        case syscall.SIGKILL: fallthrough;
        case syscall.SIGINT:
            log.Println("\nCtrl-C signalled\n")
            os.Exit(0)
        }
    }()
}

func initLogger(conf Configuration) {
    fi, err := os.OpenFile("/var/log/incus.log", os.O_RDWR|os.O_APPEND, 0660);
    if err != nil {
        log.Fatalf("Error: %v", err.Error());
    }

    log.SetOutput(fi)

    DEBUG = false
    if conf.Get("log_level") == "debug" {
        DEBUG = true
    }
}<|endoftext|>


<commit_before>/*
Copyright 2020 The Ceph-CSI Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package persistentvolume

import (
	"context"
	"errors"
	"fmt"

	ctrl "github.com/ceph/ceph-csi/internal/controller"
	"github.com/ceph/ceph-csi/internal/rbd"
	"github.com/ceph/ceph-csi/internal/util"

	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"sigs.k8s.io/controller-runtime/pkg/source"
)

// ReconcilePersistentVolume reconciles a PersistentVolume object.
type ReconcilePersistentVolume struct {
	client client.Client
	config ctrl.Config
}

var _ reconcile.Reconciler = &ReconcilePersistentVolume{}
var _ ctrl.ContollerManager = &ReconcilePersistentVolume{}

// Init will add the ReconcilePersistentVolume to the list.
func Init() {
	// add ReconcilePersistentVolume to the list
	ctrl.ControllerList = append(ctrl.ControllerList, ReconcilePersistentVolume{})
}

// Add adds the newPVReconciler.
func (r ReconcilePersistentVolume) Add(mgr manager.Manager, config ctrl.Config) error {
	return add(mgr, newPVReconciler(mgr, config))
}

// newReconciler returns a ReconcilePersistentVolume.
func newPVReconciler(mgr manager.Manager, config ctrl.Config) reconcile.Reconciler {
	r := &ReconcilePersistentVolume{
		client: mgr.GetClient(),
		config: config,
	}
	return r
}

func add(mgr manager.Manager, r reconcile.Reconciler) error {
	// Create a new controller
	c, err := controller.New("persistentvolume-controller", mgr, controller.Options{MaxConcurrentReconciles: 1, Reconciler: r})
	if err != nil {
		return err
	}

	// Watch for changes to PersistentVolumes
	err = c.Watch(&source.Kind{Type: &corev1.PersistentVolume{}}, &handler.EnqueueRequestForObject{})
	if err != nil {
		return err
	}
	return nil
}

func (r *ReconcilePersistentVolume) getCredentials(name, namespace string) (map[string]string, error) {
	secret := &corev1.Secret{}
	err := r.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, secret)
	if err != nil {
		return nil, fmt.Errorf("error getting secret %s in namespace %s: %v", name, namespace, err)
	}

	credentials := map[string]string{}
	for key, value := range secret.Data {
		credentials[key] = string(value)
	}
	return credentials, nil
}

// reconcilePV will extract the image details from the pv spec and regenerates
// the omap data.
func (r ReconcilePersistentVolume) reconcilePV(obj runtime.Object) error {
	pv, ok := obj.(*corev1.PersistentVolume)
	if !ok {
		return nil
	}
	if pv.Spec.CSI != nil && pv.Spec.CSI.Driver == r.config.DriverName {
		pool := pv.Spec.CSI.VolumeAttributes["pool"]
		journalPool := pv.Spec.CSI.VolumeAttributes["journalPool"]
		requestName := pv.Name
		imageName := pv.Spec.CSI.VolumeAttributes["imageName"]
		volumeHandler := pv.Spec.CSI.VolumeHandle
		secretName := ""
		secretNamespace := ""

		if pv.Spec.CSI.ControllerExpandSecretRef != nil {
			secretName = pv.Spec.CSI.ControllerExpandSecretRef.Name
			secretNamespace = pv.Spec.CSI.ControllerExpandSecretRef.Namespace
		} else if pv.Spec.CSI.NodeStageSecretRef != nil {
			secretName = pv.Spec.CSI.NodeStageSecretRef.Name
			secretNamespace = pv.Spec.CSI.NodeStageSecretRef.Namespace
		}
		if secretName == "" || secretNamespace == "" {
			errStr := "secretname or secret namespace is empty"
			util.ErrorLogMsg(errStr)
			return errors.New(errStr)
		}

		secrets, err := r.getCredentials(secretName, secretNamespace)
		if err != nil {
			util.ErrorLogMsg("failed to get secrets %s", err)
			return err
		}
		cr, err := util.NewUserCredentials(secrets)
		if err != nil {
			util.ErrorLogMsg("failed to get user credentials %s", err)
			return err
		}
		defer cr.DeleteCredentials()
		err = rbd.RegenerateJournal(imageName, volumeHandler, pool, journalPool, requestName, cr)
		if err != nil {
			util.ErrorLogMsg("failed to regenerate journal %s", err)
			return err
		}
	}
	return nil
}

// Reconcile reconciles the PersitentVolume object and creates a new omap entries
// for the volume.
func (r *ReconcilePersistentVolume) Reconcile(request reconcile.Request) (reconcile.Result, error) {
	pv := &corev1.PersistentVolume{}
	err := r.client.Get(context.TODO(), request.NamespacedName, pv)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return reconcile.Result{}, nil
		}
		return reconcile.Result{}, err
	}
	// Check if the object is under deletion
	if !pv.GetDeletionTimestamp().IsZero() {
		return reconcile.Result{}, nil
	}

	err = r.reconcilePV(pv)
	if err != nil {
		return reconcile.Result{}, err
	}
	return reconcile.Result{}, nil
}
<commit_msg>rbd: dont generate OMAP data for static volume<commit_after>/*
Copyright 2020 The Ceph-CSI Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package persistentvolume

import (
	"context"
	"errors"
	"fmt"
	"strconv"

	ctrl "github.com/ceph/ceph-csi/internal/controller"
	"github.com/ceph/ceph-csi/internal/rbd"
	"github.com/ceph/ceph-csi/internal/util"

	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"sigs.k8s.io/controller-runtime/pkg/source"
)

// ReconcilePersistentVolume reconciles a PersistentVolume object.
type ReconcilePersistentVolume struct {
	client client.Client
	config ctrl.Config
}

var _ reconcile.Reconciler = &ReconcilePersistentVolume{}
var _ ctrl.ContollerManager = &ReconcilePersistentVolume{}

// Init will add the ReconcilePersistentVolume to the list.
func Init() {
	// add ReconcilePersistentVolume to the list
	ctrl.ControllerList = append(ctrl.ControllerList, ReconcilePersistentVolume{})
}

// Add adds the newPVReconciler.
func (r ReconcilePersistentVolume) Add(mgr manager.Manager, config ctrl.Config) error {
	return add(mgr, newPVReconciler(mgr, config))
}

// newReconciler returns a ReconcilePersistentVolume.
func newPVReconciler(mgr manager.Manager, config ctrl.Config) reconcile.Reconciler {
	r := &ReconcilePersistentVolume{
		client: mgr.GetClient(),
		config: config,
	}
	return r
}

func add(mgr manager.Manager, r reconcile.Reconciler) error {
	// Create a new controller
	c, err := controller.New("persistentvolume-controller", mgr, controller.Options{MaxConcurrentReconciles: 1, Reconciler: r})
	if err != nil {
		return err
	}

	// Watch for changes to PersistentVolumes
	err = c.Watch(&source.Kind{Type: &corev1.PersistentVolume{}}, &handler.EnqueueRequestForObject{})
	if err != nil {
		return err
	}
	return nil
}

func (r *ReconcilePersistentVolume) getCredentials(name, namespace string) (map[string]string, error) {
	secret := &corev1.Secret{}
	err := r.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, secret)
	if err != nil {
		return nil, fmt.Errorf("error getting secret %s in namespace %s: %v", name, namespace, err)
	}

	credentials := map[string]string{}
	for key, value := range secret.Data {
		credentials[key] = string(value)
	}
	return credentials, nil
}

func checkStaticVolume(staticVol string) (bool, error) {
	static := false
	var err error
	if staticVol != "" {
		static, err = strconv.ParseBool(staticVol)
		if err != nil {
			return false, fmt.Errorf("failed to parse preProvisionedVolume: %w", err)
		}
	}
	return static, nil
}

// reconcilePV will extract the image details from the pv spec and regenerates
// the omap data.
func (r ReconcilePersistentVolume) reconcilePV(obj runtime.Object) error {
	pv, ok := obj.(*corev1.PersistentVolume)
	if !ok {
		return nil
	}
	if pv.Spec.CSI != nil && pv.Spec.CSI.Driver == r.config.DriverName {
		pool := pv.Spec.CSI.VolumeAttributes["pool"]
		journalPool := pv.Spec.CSI.VolumeAttributes["journalPool"]
		requestName := pv.Name
		imageName := pv.Spec.CSI.VolumeAttributes["imageName"]
		volumeHandler := pv.Spec.CSI.VolumeHandle
		secretName := ""
		secretNamespace := ""
		// check static volume
		static, err := checkStaticVolume(pv.Spec.CSI.VolumeAttributes["staticVolume"])
		if err != nil {
			return err
		}
		// if the volume is static, dont generate OMAP data
		if static {
			return nil
		}
		if pv.Spec.CSI.ControllerExpandSecretRef != nil {
			secretName = pv.Spec.CSI.ControllerExpandSecretRef.Name
			secretNamespace = pv.Spec.CSI.ControllerExpandSecretRef.Namespace
		} else if pv.Spec.CSI.NodeStageSecretRef != nil {
			secretName = pv.Spec.CSI.NodeStageSecretRef.Name
			secretNamespace = pv.Spec.CSI.NodeStageSecretRef.Namespace
		}
		if secretName == "" || secretNamespace == "" {
			errStr := "secretname or secret namespace is empty"
			util.ErrorLogMsg(errStr)
			return errors.New(errStr)
		}

		secrets, err := r.getCredentials(secretName, secretNamespace)
		if err != nil {
			util.ErrorLogMsg("failed to get secrets %s", err)
			return err
		}
		cr, err := util.NewUserCredentials(secrets)
		if err != nil {
			util.ErrorLogMsg("failed to get user credentials %s", err)
			return err
		}
		defer cr.DeleteCredentials()
		err = rbd.RegenerateJournal(imageName, volumeHandler, pool, journalPool, requestName, cr)
		if err != nil {
			util.ErrorLogMsg("failed to regenerate journal %s", err)
			return err
		}
	}
	return nil
}

// Reconcile reconciles the PersitentVolume object and creates a new omap entries
// for the volume.
func (r *ReconcilePersistentVolume) Reconcile(request reconcile.Request) (reconcile.Result, error) {
	pv := &corev1.PersistentVolume{}
	err := r.client.Get(context.TODO(), request.NamespacedName, pv)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return reconcile.Result{}, nil
		}
		return reconcile.Result{}, err
	}
	// Check if the object is under deletion
	if !pv.GetDeletionTimestamp().IsZero() {
		return reconcile.Result{}, nil
	}

	err = r.reconcilePV(pv)
	if err != nil {
		return reconcile.Result{}, err
	}
	return reconcile.Result{}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"regexp"
	"sync"
	"path/filepath"
	"strconv"
	"bytes"
	"strings"
	"bufio"
	"os"
	"os/exec"
	"log"
	"encoding/json"
	"time"

	"github.com/bamchoh/nagome-talk/polly"
)

var (
	logger *log.Logger
	save_dir string = "mp3"
	wg *sync.WaitGroup = new(sync.WaitGroup)
	m *sync.Mutex = new(sync.Mutex)
)

type Message struct {
	Domain  string          `json:"domain"`
	Command string          `json:"command"`
	Content json.RawMessage `json:"content,omitempty"` // The structure of Content is depend on the Command (and Domain).

	prgno int
}

type CtCommentGot struct {
	No      int       `json:"no"`
	Date    time.Time `json:"date"`
	Raw     string    `json:"raw"`
	Comment string    `json:"comment"`

	UserID           string `json:"user_id"`
	UserName         string `json:"user_name"`
	UserThumbnailURL string `json:"user_thumbnail_url,omitempty"`
	Score            int    `json:"score,omitempty"`
	IsPremium        bool   `json:"is_premium"`
	IsBroadcaster    bool   `json:"is_broadcaster"`
	IsStaff          bool   `json:"is_staff"`
	IsAnonymity      bool   `json:"is_anonymity"`
}

type CtNagomeBroadOpen struct {
	BroadID     string `json:"broad_id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	CommunityID string `json:"community_id"`
	OwnerID     string `json:"owner_id"`
	OwnerName   string `json:"owner_name"`
	OwnerBroad  bool   `json:"owner_broad"`

	OpenTime  time.Time `json:"open_time"`
	StartTime time.Time `json:"start_time"`
	EndTime   time.Time `json:"end_time"`
}

func set_log() *log.Logger {
	f,_ := os.Create("test.log")
	return log.New(f, "nagome-talk:", 0)
}

func play(save_file string, m *sync.Mutex) {
	m.Lock()
	defer m.Unlock()
	var err error

	err = polly.Play(save_file)
	if err != nil {
		logger.Println(err)
		return
	}

	err = os.Remove(save_file)
	if err != nil {
		logger.Println(err)
		return
	}

	return
}

func send_aws(msg, file string) (err error) {
	packed_msg := `<speak><prosody rate="100%"><![CDATA[`+msg+`]]></prosody></speak>`
	cmd := exec.Command(
		"aws",
		"polly",
		"synthesize-speech",
		"--region", "us-west-2",
		"--output-format", "mp3",
		"--voice-id", "Mizuki",
		"--text-type", "ssml",
		"--text", packed_msg,
		file,
	)

	err = cmd.Run()

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

func read_aloud(broad_id string, content []byte) (err error) {
	dec := json.NewDecoder(bytes.NewReader(content))
	com := new(CtCommentGot)
	err = dec.Decode(com)
	if err != nil {
		logger.Println(err)
		return
	}

	matched,err := regexp.MatchString("^/hb", com.Raw)
	if err != nil {
		logger.Println(err)
		return
	}

	if matched {
		return
	}

	no := strconv.Itoa(com.No)
	save_file := filepath.Join(save_dir, broad_id+"_"+no+".mp3")

	go func(msg, file string, m *sync.Mutex) {
		send_aws(msg,file)
		wg.Add(1)
		play(file, m)
		wg.Done()
	}(string(com.Raw), save_file, m)

	return
}

func init_plugin() (err error) {
	if _,err = os.Stat(save_dir); err == nil {
		if err = os.RemoveAll(save_dir); err != nil {
			return
		}
	}

	if _,err = os.Stat(save_dir); err != nil {
		logger.Println(err)
		err = os.MkdirAll(save_dir, 0755)
		return
	}

	return
}

func pick_broad_id(content []byte) (broad_id string, err error) {
	dec := json.NewDecoder(bytes.NewReader(content))
	cnbo := new(CtNagomeBroadOpen)
	err = dec.Decode(cnbo)
	if err != nil {
		logger.Println(err)
		return
	}
	broad_id = cnbo.BroadID
	return
}

func main() {
	var broad_id string
	logger = set_log()

	err := init_plugin()
	if err != nil {
		logger.Println(err)
	}

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		txt := scanner.Text()
		dec := json.NewDecoder(strings.NewReader(txt))
		msg := new(Message)
		err := dec.Decode(msg)
		if err != nil {
			logger.Println(err)
		}
		switch msg.Command {
		case "Broad.Open":
			pick_broad_id(msg.Content)
		case "Got":
			read_aloud(broad_id, msg.Content)
		default:
			logger.Println(msg.Command)
		}
	}

	if err := scanner.Err(); err != nil {
		logger.Println("reading standard input:", err)
	}

	wg.Wait()
	// scanner := bufio.NewScanner(os.Stdin)
	// for scanner.Scan() {
	// 	fmt.Println("--- got a comment ---")
	// }
}
<commit_msg>change save_dir<commit_after>package main

import (
	"regexp"
	"sync"
	"path/filepath"
	"strconv"
	"bytes"
	"strings"
	"bufio"
	"os"
	"os/exec"
	"log"
	"encoding/json"
	"time"

	"github.com/bamchoh/nagome-talk/polly"
)

var (
	logger *log.Logger
	save_dir string = "mp3"
	wg *sync.WaitGroup = new(sync.WaitGroup)
	m *sync.Mutex = new(sync.Mutex)
)

type Message struct {
	Domain  string          `json:"domain"`
	Command string          `json:"command"`
	Content json.RawMessage `json:"content,omitempty"` // The structure of Content is depend on the Command (and Domain).

	prgno int
}

type CtCommentGot struct {
	No      int       `json:"no"`
	Date    time.Time `json:"date"`
	Raw     string    `json:"raw"`
	Comment string    `json:"comment"`

	UserID           string `json:"user_id"`
	UserName         string `json:"user_name"`
	UserThumbnailURL string `json:"user_thumbnail_url,omitempty"`
	Score            int    `json:"score,omitempty"`
	IsPremium        bool   `json:"is_premium"`
	IsBroadcaster    bool   `json:"is_broadcaster"`
	IsStaff          bool   `json:"is_staff"`
	IsAnonymity      bool   `json:"is_anonymity"`
}

type CtNagomeBroadOpen struct {
	BroadID     string `json:"broad_id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	CommunityID string `json:"community_id"`
	OwnerID     string `json:"owner_id"`
	OwnerName   string `json:"owner_name"`
	OwnerBroad  bool   `json:"owner_broad"`

	OpenTime  time.Time `json:"open_time"`
	StartTime time.Time `json:"start_time"`
	EndTime   time.Time `json:"end_time"`
}

func set_log() *log.Logger {
	f,_ := os.Create("test.log")
	return log.New(f, "nagome-talk:", 0)
}

func play(save_file string, m *sync.Mutex) {
	m.Lock()
	defer m.Unlock()
	var err error

	err = polly.Play(save_file)
	if err != nil {
		logger.Println(err)
		return
	}

	err = os.Remove(save_file)
	if err != nil {
		logger.Println(err)
		return
	}

	return
}

func send_aws(msg, file string) (err error) {
	packed_msg := `<speak><prosody rate="100%"><![CDATA[`+msg+`]]></prosody></speak>`
	cmd := exec.Command(
		"aws",
		"polly",
		"synthesize-speech",
		"--region", "us-west-2",
		"--output-format", "mp3",
		"--voice-id", "Mizuki",
		"--text-type", "ssml",
		"--text", packed_msg,
		file,
	)

	err = cmd.Run()

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

func read_aloud(broad_id string, content []byte) (err error) {
	dec := json.NewDecoder(bytes.NewReader(content))
	com := new(CtCommentGot)
	err = dec.Decode(com)
	if err != nil {
		logger.Println(err)
		return
	}

	matched,err := regexp.MatchString("^/hb", com.Raw)
	if err != nil {
		logger.Println(err)
		return
	}

	if matched {
		return
	}

	no := strconv.Itoa(com.No)
	save_file := filepath.Join(save_dir, broad_id+"_"+no+".mp3")

	go func(msg, file string, m *sync.Mutex) {
		send_aws(msg,file)
		wg.Add(1)
		play(file, m)
		wg.Done()
	}(string(com.Raw), save_file, m)

	return
}

func init_plugin() (err error) {
	save_dir = filepath.Join(filepath.Dir(os.Args[0]), save_dir)
	logger.Println(save_dir)
	if _,err = os.Stat(save_dir); err == nil {
		if err = os.RemoveAll(save_dir); err != nil {
			return
		}
	}

	if _,err = os.Stat(save_dir); err != nil {
		logger.Println(err)
		err = os.MkdirAll(save_dir, 0755)
		return
	}

	return
}

func pick_broad_id(content []byte) (broad_id string, err error) {
	dec := json.NewDecoder(bytes.NewReader(content))
	cnbo := new(CtNagomeBroadOpen)
	err = dec.Decode(cnbo)
	if err != nil {
		logger.Println(err)
		return
	}
	broad_id = cnbo.BroadID
	return
}

func main() {
	var broad_id string
	logger = set_log()

	err := init_plugin()
	if err != nil {
		logger.Println(err)
	}

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		txt := scanner.Text()
		dec := json.NewDecoder(strings.NewReader(txt))
		msg := new(Message)
		err := dec.Decode(msg)
		if err != nil {
			logger.Println(err)
		}
		switch msg.Command {
		case "Broad.Open":
			pick_broad_id(msg.Content)
		case "Got":
			read_aloud(broad_id, msg.Content)
		default:
			logger.Println(msg.Command)
		}
	}

	if err := scanner.Err(); err != nil {
		logger.Println("reading standard input:", err)
	}

	wg.Wait()
	// scanner := bufio.NewScanner(os.Stdin)
	// for scanner.Scan() {
	// 	fmt.Println("--- got a comment ---")
	// }
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/crowdmob/goamz/aws"
	"github.com/crowdmob/goamz/s3"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
	"time"
)

type LogFilePuller struct {
	auth          aws.Auth
	bucket        string
	marker        string
	prefix        string
	tmpDir        string
	fileChannel   chan string
	statefile     string
	lastDate      time.Time
	processedKeys map[string]time.Time
}

func NewLogFilePuller(fileChannel chan string, config map[string]interface{}) *LogFilePuller {

	access_key := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["access_key"].(string)
	secret_key := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["secret_key"].(string)
	bucket := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["bucket"].(string)
	prefix := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["prefix"].(string)
	tmpdir := config["source"].(map[string]interface{})["tmpdir"].(string)

	os.MkdirAll(tmpdir, 0700)

	return &LogFilePuller{
		auth:          aws.Auth{AccessKey: access_key, SecretKey: secret_key},
		marker:        "",
		prefix:        prefix,
		bucket:        bucket,
		tmpDir:        tmpdir,
		statefile:     "marker.txt",
		fileChannel:   fileChannel,
		processedKeys: make(map[string]time.Time)}
}

func (puller *LogFilePuller) RestoreState() {
	if _, err := os.Stat(puller.statefile); err == nil {
		content, err := ioutil.ReadFile(puller.statefile)
		if err != nil {
			panic(err)
		}

		var state map[string]interface{}
		json.Unmarshal([]byte(content), &state)

		tmp, err := time.Parse(time.RFC3339, state["time"].(string))
		if err != nil {
			panic(err)
		}
		puller.lastDate = tmp
		puller.marker = state["marker"].(string)
	} else {
		errLogger.Printf("%v", err)
	}
}

func (puller *LogFilePuller) StoreState() {
	f, err := os.OpenFile(puller.statefile, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		panic(err)
	}
	state := map[string]interface{}{
		"marker": puller.marker,
		"time":   puller.lastDate,
	}
	b, _ := json.Marshal(state)
	if _, err = f.WriteString(string(b)); err != nil {
		panic(err)
	}
	f.Close()
}

func (puller *LogFilePuller) Run() {

	puller.RestoreState()

	keyDateRegexp := regexp.MustCompile("^/?nginx/access/(?P<date>[0-9-]+)/.+$")

	for {
		puller.StoreState()
		infoLogger.Printf("listing files. marker: %s", puller.marker)
		s3client := s3.New(puller.auth, aws.USEast)
		bucket := s3client.Bucket(puller.bucket)
		bucket.ReadTimeout = time.Second * 5
		bucket.ConnectTimeout = time.Second * 2
		//result, err := bucket.List(puller.prefix, "", puller.marker, 1000)
		result, err := bucket.List(puller.prefix, "", puller.marker, 1000)
		if err != nil {
			errLogger.Printf("%v", err)
			continue
		}

		downloaders := make(chan int, 8)
		for _, value := range result.Contents {

			puller.marker = value.Key

			keyDateMatch := keyDateRegexp.FindStringSubmatch(value.Key)
			infoLogger.Printf("%v -> %v", value.Key, keyDateMatch)
			result := make(map[string]string)
			for i, name := range keyDateRegexp.SubexpNames() {
				result[name] = keyDateMatch[i]
			}
			keyDate, err := time.Parse("2006-01-02", result["date"])
			if err != nil {
				errLogger.Printf("skipping file: %s, modified: %s, err: %v", value.Key, value.LastModified, err)
				continue
			}

			if keyDate.Unix() < puller.lastDate.AddDate(0, 0, -1).Unix() {
				infoLogger.Printf("skipping file: %s, modified: %s, too old", value.Key, value.LastModified)
				continue
			}

			if keyDate.Unix() > puller.lastDate.Unix() {
				puller.lastDate = keyDate
			}

			if _, exists := puller.processedKeys[value.Key]; exists {
				infoLogger.Printf("skipping file: %s, modified: %s, already processed", value.Key, value.LastModified)
				continue
			} else {
				puller.processedKeys[value.Key] = time.Now()
			}

			/*
				fileTime, _ := time.Parse(time.RFC3339, value.LastModified)
				if fileTime.Unix() < sevenDaysAgo.Unix() {
					infoLogger.Printf("skipping file: %s, modified: %s", value.Key, value.LastModified)
					continue
				}
			*/

			downloaders <- 1
			go func(done chan int) {
				defer func() {
					<-done
				}()
				file, err := puller.Download(value.Key)
				if err == nil {
					infoLogger.Printf("sending file %s to queue. %v", file, len(puller.fileChannel))
					puller.fileChannel <- file
				} else {
					delete(puller.processedKeys, value.Key)
					errLogger.Printf("%v", err)
				}
			}(downloaders)
		}

		infoLogger.Printf("result.NextMarker: %s", result.NextMarker)
		infoLogger.Printf("puller.lastDate: %v, puller.marker: %v, puller.processedKeys: %v", puller.lastDate, puller.marker, len(puller.processedKeys))

		puller.marker = ""

		if result.IsTruncated {
			infoLogger.Printf("listing more: %s", result.NextMarker)
			puller.marker = result.NextMarker
		} else {
			infoLogger.Printf("no more data, sleeping for 5 minutes and starting again")
			time.Sleep(5 * time.Minute)
		}
	}
}

func (puller *LogFilePuller) Download(key string) (string, error) {

	s3client := s3.New(puller.auth, aws.USEast)
	bucket := s3client.Bucket(puller.bucket)

	reader, err := bucket.GetReader(key)
	if err != nil {
		return "", err
	}
	defer reader.Close()

	localFilePath := path.Join(puller.tmpDir, key)
	if strings.LastIndex(localFilePath, "/") > -1 {
		localFilePath = localFilePath[0:strings.LastIndex(localFilePath, "/")]
	}
	os.MkdirAll(localFilePath, 0700)
	localFilePath = path.Join(puller.tmpDir, key)

	infoLogger.Printf("downloading '%s' -> '%s'", key, localFilePath)

	writer, err := os.Create(localFilePath)
	if err != nil {
		return "", err
	}
	defer writer.Close()

	if num, err := io.Copy(writer, reader); err != nil {
		return "", err
	} else {
		infoLogger.Printf("downloaded '%s', bytes: %v", localFilePath, num)
	}

	return localFilePath, nil
}
<commit_msg>fsdfsf<commit_after>package main

import (
	"encoding/json"
	"github.com/crowdmob/goamz/aws"
	"github.com/crowdmob/goamz/s3"
	"io"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
	"time"
)

type LogFilePuller struct {
	auth          aws.Auth
	bucket        string
	marker        string
	prefix        string
	tmpDir        string
	fileChannel   chan string
	statefile     string
	lastDate      time.Time
	processedKeys map[string]time.Time
}

func NewLogFilePuller(fileChannel chan string, config map[string]interface{}) *LogFilePuller {

	access_key := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["access_key"].(string)
	secret_key := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["secret_key"].(string)
	bucket := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["bucket"].(string)
	prefix := config["source"].(map[string]interface{})["s3"].(map[string]interface{})["prefix"].(string)
	tmpdir := config["source"].(map[string]interface{})["tmpdir"].(string)

	os.MkdirAll(tmpdir, 0700)

	return &LogFilePuller{
		auth:          aws.Auth{AccessKey: access_key, SecretKey: secret_key},
		marker:        "",
		prefix:        prefix,
		bucket:        bucket,
		tmpDir:        tmpdir,
		statefile:     "marker.txt",
		fileChannel:   fileChannel,
		processedKeys: make(map[string]time.Time)}
}

func (puller *LogFilePuller) RestoreState() {
	if _, err := os.Stat(puller.statefile); err == nil {
		content, err := ioutil.ReadFile(puller.statefile)
		if err != nil {
			panic(err)
		}

		var state map[string]interface{}
		json.Unmarshal([]byte(content), &state)

		tmp, err := time.Parse(time.RFC3339, state["time"].(string))
		if err != nil {
			panic(err)
		}
		puller.lastDate = tmp
		puller.marker = state["marker"].(string)
	} else {
		errLogger.Printf("%v", err)
	}
}

func (puller *LogFilePuller) StoreState() {
	f, err := os.OpenFile(puller.statefile, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		panic(err)
	}
	state := map[string]interface{}{
		"marker": puller.marker,
		"time":   puller.lastDate,
	}
	b, _ := json.Marshal(state)
	if _, err = f.WriteString(string(b)); err != nil {
		panic(err)
	}
	f.Close()
}

func (puller *LogFilePuller) Run() {

	puller.RestoreState()

	keyDateRegexp := regexp.MustCompile("^/?nginx/access/(?P<date>[0-9-]+)/.+$")

	for {
		puller.StoreState()
		infoLogger.Printf("listing files. marker: %s", puller.marker)
		s3client := s3.New(puller.auth, aws.USEast)
		bucket := s3client.Bucket(puller.bucket)
		bucket.ReadTimeout = time.Second * 5
		bucket.ConnectTimeout = time.Second * 2
		//result, err := bucket.List(puller.prefix, "", puller.marker, 1000)
		result, err := bucket.List(puller.prefix, "", puller.marker, 1000)
		if err != nil {
			errLogger.Printf("%v", err)
			continue
		}

		downloaders := make(chan int, 8)
		for _, value := range result.Contents {

			puller.marker = value.Key

			keyDateMatch := keyDateRegexp.FindStringSubmatch(value.Key)
			infoLogger.Printf("%v -> %v", value.Key, keyDateMatch)
			result := make(map[string]string)
			for i, name := range keyDateRegexp.SubexpNames() {
				result[name] = keyDateMatch[i]
			}
			keyDate, err := time.Parse("2006-01-02", result["date"])
			if err != nil {
				errLogger.Printf("skipping file: %s, modified: %s, err: %v", value.Key, value.LastModified, err)
				continue
			}

			if keyDate.Unix() < puller.lastDate.AddDate(0, 0, -1).Unix() {
				infoLogger.Printf("skipping file: %s, modified: %s, too old", value.Key, value.LastModified)
				continue
			}

			if keyDate.Unix() > puller.lastDate.Unix() {
				puller.lastDate = keyDate
			}

			if _, exists := puller.processedKeys[value.Key]; exists {
				infoLogger.Printf("skipping file: %s, modified: %s, already processed", value.Key, value.LastModified)
				continue
			} else {
				puller.processedKeys[value.Key] = time.Now()
			}

			/*
				fileTime, _ := time.Parse(time.RFC3339, value.LastModified)
				if fileTime.Unix() < sevenDaysAgo.Unix() {
					infoLogger.Printf("skipping file: %s, modified: %s", value.Key, value.LastModified)
					continue
				}
			*/

			downloaders <- 1
			go func(done chan int, key string, p *LogFilePuller) {
				defer func() {
					<-done
				}()
				file, err := p.Download(key)
				if err == nil {
					infoLogger.Printf("sending file %s to queue. %v", file, len(p.fileChannel))
					p.fileChannel <- file
				} else {
					delete(p.processedKeys, key)
					errLogger.Printf("%v", err)
				}
			}(downloaders, value.Key, puller)
		}

		infoLogger.Printf("result.NextMarker: %s", result.NextMarker)
		infoLogger.Printf("puller.lastDate: %v, puller.marker: %v, puller.processedKeys: %v", puller.lastDate, puller.marker, len(puller.processedKeys))

		puller.marker = ""

		if result.IsTruncated {
			infoLogger.Printf("listing more: %s", result.NextMarker)
			puller.marker = result.NextMarker
		} else {
			infoLogger.Printf("no more data, sleeping for 5 minutes and starting again")
			time.Sleep(5 * time.Minute)
		}
	}
}

func (puller *LogFilePuller) Download(key string) (string, error) {

	s3client := s3.New(puller.auth, aws.USEast)
	bucket := s3client.Bucket(puller.bucket)

	reader, err := bucket.GetReader(key)
	if err != nil {
		return "", err
	}
	defer reader.Close()

	localFilePath := path.Join(puller.tmpDir, key)
	if strings.LastIndex(localFilePath, "/") > -1 {
		localFilePath = localFilePath[0:strings.LastIndex(localFilePath, "/")]
	}
	os.MkdirAll(localFilePath, 0700)
	localFilePath = path.Join(puller.tmpDir, key)

	infoLogger.Printf("downloading '%s' -> '%s'", key, localFilePath)

	writer, err := os.Create(localFilePath)
	if err != nil {
		return "", err
	}
	defer writer.Close()

	if num, err := io.Copy(writer, reader); err != nil {
		return "", err
	} else {
		infoLogger.Printf("downloaded '%s', bytes: %v", localFilePath, num)
	}

	return localFilePath, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/facebookgo/grace/gracehttp"
	"github.com/gin-gonic/gin"

	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/cors"
	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/redis"
	"github.com/eirka/eirka-libs/status"
	"github.com/eirka/eirka-libs/user"
	"github.com/eirka/eirka-libs/validate"

	local "github.com/eirka/eirka-get/config"
	c "github.com/eirka/eirka-get/controllers"
	m "github.com/eirka/eirka-get/middleware"
)

func init() {
	// Database connection settings
	dbase := db.Database{

		User:           local.Settings.Database.User,
		Password:       local.Settings.Database.Password,
		Proto:          local.Settings.Database.Protocol,
		Host:           local.Settings.Database.Host,
		Database:       local.Settings.Database.Database,
		MaxIdle:        local.Settings.Get.DatabaseMaxIdle,
		MaxConnections: local.Settings.Get.DatabaseMaxConnections,
	}

	// Set up DB connection
	dbase.NewDb()

	// Get limits and stuff from database
	config.GetDatabaseSettings()

	// redis settings
	r := redis.Redis{
		// Redis address and max pool connections
		Protocol:       local.Settings.Redis.Protocol,
		Address:        local.Settings.Redis.Host,
		MaxIdle:        local.Settings.Get.RedisMaxIdle,
		MaxConnections: local.Settings.Get.RedisMaxConnections,
	}

	// Set up Redis connection
	r.NewRedisCache()

	// set auth middleware secret
	user.Secret = local.Settings.Session.Secret

	// set cors domains
	cors.SetDomains(local.Settings.CORS.Sites, strings.Split("GET", ","))

}

func main() {
	r := gin.Default()

	// add CORS headers
	r.Use(cors.CORS())
	// validate all route parameters
	r.Use(validate.ValidateParams())

	r.GET("/status", status.StatusController)
	r.NoRoute(c.ErrorController)

	// public cached pages
	public := r.Group("/")
	public.Use(user.Auth(false))
	public.Use(m.Analytics())
	public.Use(m.Cache())

	public.GET("/index/:ib/:page", c.IndexController)
	public.GET("/thread/:ib/:thread/:page", c.ThreadController)
	public.GET("/tag/:ib/:tag/:page", c.TagController)
	public.GET("/image/:ib/:id", c.ImageController)
	public.GET("/post/:ib/:thread/:id", c.PostController)
	public.GET("/tags/:ib/:page", c.TagsController)
	public.GET("/tagsearch/:ib", c.TagSearchController)
	public.GET("/threadsearch/:ib", c.ThreadSearchController)
	public.GET("/directory/:ib/:page", c.DirectoryController)
	public.GET("/popular/:ib", c.PopularController)
	public.GET("/new/:ib", c.NewController)
	public.GET("/favorited/:ib", c.FavoritedController)
	public.GET("/tagtypes", c.TagTypesController)
	public.GET("/imageboards", c.ImageboardsController)

	// user pages
	users := r.Group("/user")
	users.Use(user.Auth(true))

	users.GET("/whoami/:ib", c.UserController)
	users.GET("/favorite/:id", c.FavoriteController)
	users.GET("/favorites/:ib/:page", c.FavoritesController)

	s := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", local.Settings.Get.Host, local.Settings.Get.Port),
		Handler: r,
	}

	gracehttp.Serve(s)

}
<commit_msg>add pid file for systemd graceful reloading<commit_after>package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/facebookgo/grace/gracehttp"
	"github.com/facebookgo/pidfile"
	"github.com/gin-gonic/gin"

	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/cors"
	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/redis"
	"github.com/eirka/eirka-libs/status"
	"github.com/eirka/eirka-libs/user"
	"github.com/eirka/eirka-libs/validate"

	local "github.com/eirka/eirka-get/config"
	c "github.com/eirka/eirka-get/controllers"
	m "github.com/eirka/eirka-get/middleware"
)

func init() {

	// create pid file
	pidfile.SetPidfilePath("/run/eirka/eirka-get.pid")

	err := pidfile.Write()
	if err != nil {
		panic("Could not write pid file")
	}

	// Database connection settings
	dbase := db.Database{

		User:           local.Settings.Database.User,
		Password:       local.Settings.Database.Password,
		Proto:          local.Settings.Database.Protocol,
		Host:           local.Settings.Database.Host,
		Database:       local.Settings.Database.Database,
		MaxIdle:        local.Settings.Get.DatabaseMaxIdle,
		MaxConnections: local.Settings.Get.DatabaseMaxConnections,
	}

	// Set up DB connection
	dbase.NewDb()

	// Get limits and stuff from database
	config.GetDatabaseSettings()

	// redis settings
	r := redis.Redis{
		// Redis address and max pool connections
		Protocol:       local.Settings.Redis.Protocol,
		Address:        local.Settings.Redis.Host,
		MaxIdle:        local.Settings.Get.RedisMaxIdle,
		MaxConnections: local.Settings.Get.RedisMaxConnections,
	}

	// Set up Redis connection
	r.NewRedisCache()

	// set auth middleware secret
	user.Secret = local.Settings.Session.Secret

	// set cors domains
	cors.SetDomains(local.Settings.CORS.Sites, strings.Split("GET", ","))

}

func main() {
	r := gin.Default()

	// add CORS headers
	r.Use(cors.CORS())
	// validate all route parameters
	r.Use(validate.ValidateParams())

	r.GET("/status", status.StatusController)
	r.NoRoute(c.ErrorController)

	// public cached pages
	public := r.Group("/")
	public.Use(user.Auth(false))
	public.Use(m.Analytics())
	public.Use(m.Cache())

	public.GET("/index/:ib/:page", c.IndexController)
	public.GET("/thread/:ib/:thread/:page", c.ThreadController)
	public.GET("/tag/:ib/:tag/:page", c.TagController)
	public.GET("/image/:ib/:id", c.ImageController)
	public.GET("/post/:ib/:thread/:id", c.PostController)
	public.GET("/tags/:ib/:page", c.TagsController)
	public.GET("/tagsearch/:ib", c.TagSearchController)
	public.GET("/threadsearch/:ib", c.ThreadSearchController)
	public.GET("/directory/:ib/:page", c.DirectoryController)
	public.GET("/popular/:ib", c.PopularController)
	public.GET("/new/:ib", c.NewController)
	public.GET("/favorited/:ib", c.FavoritedController)
	public.GET("/tagtypes", c.TagTypesController)
	public.GET("/imageboards", c.ImageboardsController)

	// user pages
	users := r.Group("/user")
	users.Use(user.Auth(true))

	users.GET("/whoami/:ib", c.UserController)
	users.GET("/favorite/:id", c.FavoriteController)
	users.GET("/favorites/:ib/:page", c.FavoritesController)

	s := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", local.Settings.Get.Host, local.Settings.Get.Port),
		Handler: r,
	}

	gracehttp.Serve(s)

}
<|endoftext|>