<commit_before>package jk

import (
	"reflect"
	"testing"
)

func TestMorpheme(t *testing.T) {
	line := "探して さがして 探す 動詞 2 * 0 子音動詞サ行 5 タ系連用テ形 14 \"代表表記:探す/さがす\""
	m, err := NewMorpheme(line)

	if err != nil {
		t.Fatal(err)
	}

	if m.Surface != "探して" {
		t.Errorf("Midashi Error\n")
	} else if m.CFormID != 14 {
		t.Errorf("Katsuyou2ID Error\n")
	} else if m.Seminfo != "代表表記:探す/さがす" {
		t.Errorf("Seminfo_id Error\n")
	} else if m.Rep != "探す/さがす" {
		t.Errorf("Rep Error\n")
	}
}

func TestMorpheme2(t *testing.T) {
	line := `を を を 助詞 9 格助詞 1 * 0 * 0 NIL <かな漢字><ひらがな><付属>`
	m, err := NewMorpheme(line)

	if err != nil {
		t.Fatal(err)
	}

	if m.Pos0 != "助詞" {
		t.Fatal("Pos0 error")
	}
	if len(m.Features) != 3 {
		t.Errorf("Expected the number of features is 3 but got %v", m.Features)
	} else if _, ok := m.Features["かな漢字"]; !ok {
		t.Errorf("Feautre かな漢字 not found")
	} else if _, ok := m.Features["ひらがな"]; !ok {
		t.Errorf("Feautre ひらがな not found")
	} else if _, ok := m.Features["付属"]; !ok {
		t.Errorf("Feautre 付属 not found")
	}
}

func TestMorphemeKNP(t *testing.T) {
	line := "構文 こうぶん 構文 名詞 6 普通名詞 1 * 0 * 0 \"代表表記:構文/こうぶん カテゴリ:抽象物\" " + sampleFeature
	m, err := NewMorpheme(line)

	if err != nil {
		t.Fatal(err)
	}

	gf := getFeatures(sampleFeature, '>', 1)
	if m.Surface != "構文" {
		t.Errorf("Midashi Error\n")
	} else if m.CFormID != 0 {
		t.Errorf("Katsuyou2ID Error\n")
	} else if m.Rep != "構文/こうぶん" {
		t.Errorf("Rep Error\n")
	} else if !reflect.DeepEqual(m.Features, gf) {
		t.Errorf("Features Error [%v] != [%v]\n", m.Features, gf)
	}
}
<commit_msg>Improved test<commit_after>package jk

import (
	"reflect"
	"testing"
)

func TestMorpheme(t *testing.T) {
	tests := []struct {
		line string
		gold Morpheme
	}{
		{
			line: "探して さがして 探す 動詞 2 * 0 子音動詞サ行 5 タ系連用テ形 14 \"代表表記:探す/さがす\"",
			gold: Morpheme{
				Surface:       "探して",
				Pronunciation: "さがして",
				RootForm:      "探す",
				Pos0:          "動詞",
				Pos0ID:        2,
				Pos1:          "*",
				Pos1ID:        0,
				CType:         "子音動詞サ行",
				CTypeID:       5,
				CForm:         "タ系連用テ形",
				CFormID:       14,
				Seminfo:       "代表表記:探す/さがす",
				Rep:           "探す/さがす",
			},
		},
		{
			line: `を を を 助詞 9 格助詞 1 * 0 * 0 NIL <かな漢字><ひらがな><付属>`,
			gold: Morpheme{
				Surface:       "を",
				Pronunciation: "を",
				RootForm:      "を",
				Pos0:          "助詞",
				Pos0ID:        9,
				Pos1:          "格助詞",
				Pos1ID:        1,
				CType:         "*",
				CTypeID:       0,
				CForm:         "*",
				CFormID:       0,
				Seminfo:       "",
				Rep:           "を/を",
				Features:      Features{"かな漢字": "", "ひらがな": "", "付属": ""},
			},
		},
		{ // KNP style
			line: "構文 こうぶん 構文 名詞 6 普通名詞 1 * 0 * 0 \"代表表記:構文/こうぶん カテゴリ:抽象物\" " + sampleFeature,
			gold: Morpheme{
				Surface:       "構文",
				Pronunciation: "こうぶん",
				RootForm:      "構文",
				Pos0:          "名詞",
				Pos0ID:        6,
				Pos1:          "普通名詞",
				Pos1ID:        1,
				CType:         "*",
				CTypeID:       0,
				CForm:         "*",
				CFormID:       0,
				Seminfo:       "代表表記:構文/こうぶん カテゴリ:抽象物",
				Rep:           "構文/こうぶん",
				Features:      Features{`代表表記`: `構文/こうぶん`, `カテゴリ`: `抽象物`, `正規化代表表記`: `構文/こうぶん`, `漢字`: ``},
			},
		},
	}

	for _, test := range tests {
		m, err := NewMorpheme(test.line)

		if err != nil {
			t.Fatal(err)
		}

		if m.Surface != test.gold.Surface {
			t.Errorf("Surface Error: expected %s but got %s", test.gold.Surface, m.Surface)
		}
		if m.Pronunciation != test.gold.Pronunciation {
			t.Errorf("Pronunciation Error: expected %s but got %s", test.gold.Pronunciation, m.Pronunciation)
		}
		if m.RootForm != test.gold.RootForm {
			t.Errorf("RootForm Error: expected %s but got %s", test.gold.RootForm, m.RootForm)
		}
		if m.Pos0 != test.gold.Pos0 {
			t.Errorf("Pos0 Error: expected %s but got %s", test.gold.Pos0, m.Pos0)
		}
		if m.Pos0ID != test.gold.Pos0ID {
			t.Errorf("Pos0ID Error: expected %d but got %d", test.gold.Pos0ID, m.Pos0ID)
		}
		if m.Pos1 != test.gold.Pos1 {
			t.Errorf("Pos1 Error: expected %s but got %s", test.gold.Pos1, m.Pos1)
		}
		if m.Pos1ID != test.gold.Pos1ID {
			t.Errorf("Pos1ID Error: expected %d but got %d", test.gold.Pos1ID, m.Pos1ID)
		}
		if m.CType != test.gold.CType {
			t.Errorf("CType Error: expected %s but got %s", test.gold.CType, m.CType)
		}
		if m.CTypeID != test.gold.CTypeID {
			t.Errorf("CTypeID Error: expected %d but got %d", test.gold.CTypeID, m.CTypeID)
		}
		if m.CForm != test.gold.CForm {
			t.Errorf("CForm Error: expected %s but got %s", test.gold.CForm, m.CForm)
		}
		if m.CFormID != test.gold.CFormID {
			t.Errorf("CFormID Error: expected %d but got %d", test.gold.CFormID, m.CFormID)
		}
		if m.Seminfo != test.gold.Seminfo {
			t.Errorf("Seminfo Error: expected %s but got %s", test.gold.Seminfo, m.Seminfo)
		}
		if m.Rep != test.gold.Rep {
			t.Errorf("Rep Error: expected %s but got %s", test.gold.Rep, m.Rep)
		}
		if !reflect.DeepEqual(m.Doukeis, test.gold.Doukeis) {
			t.Errorf("Doukeis Error: expected %v but got %v", test.gold.Doukeis, m.Doukeis)
		}
		if !reflect.DeepEqual(m.Features, test.gold.Features) {
			t.Errorf("Features Error: expected %v but got %v", test.gold.Features, m.Features)
		}

	}
}
<|endoftext|>


<commit_before>package cloud

import (
	"bytes"
	"strings"
	"text/template"

	api "github.com/appscode/pharmer/apis/v1alpha1"
	"github.com/ghodss/yaml"
	"github.com/hashicorp/go-version"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)

type TemplateData struct {
	ClusterName        string
	KubeletVersion     string
	KubeadmVersion     string
	KubeadmToken       string
	CAKey              string
	FrontProxyKey      string
	APIServerAddress   string
	ExtraDomains       string
	NetworkProvider    string
	CloudConfig        string
	Provider           string
	ExternalProvider   bool
	KubeadmTokenLoader string

	MasterConfiguration *kubeadmapi.MasterConfiguration
	KubeletExtraArgs    map[string]string
}

func (td TemplateData) MasterConfigurationYAML() (string, error) {
	if td.MasterConfiguration == nil {
		return "", nil
	}
	cb, err := yaml.Marshal(td.MasterConfiguration)
	return string(cb), err
}

func (td TemplateData) IsPreReleaseVersion() bool {
	if v, err := version.NewVersion(td.KubeadmVersion); err == nil && v.Prerelease() != "" {
		return true
	}
	return false
}

func (td TemplateData) KubeletExtraArgsStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) KubeletExtraArgsEmptyCloudProviderStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		if k == "cloud-config" {
			continue
		}
		if k == "cloud-provider" {
			v = ""
		}
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) PackageList() string {
	pkgs := []string{
		"cron",
		"docker.io",
		"ebtables",
		"git",
		"glusterfs-client",
		"haveged",
		"nfs-common",
		"socat",
	}
	if !td.IsPreReleaseVersion() {
		if td.KubeletVersion == "" {
			pkgs = append(pkgs, "kubelet", "kubectl")
		} else {
			pkgs = append(pkgs, "kubelet="+td.KubeletVersion+"*", "kubectl="+td.KubeletVersion+"*")
		}
		if td.KubeadmVersion == "" {
			pkgs = append(pkgs, "kubeadm")
		} else {
			pkgs = append(pkgs, "kubeadm="+td.KubeadmVersion+"*")
		}
	}
	if td.Provider != "gce" && td.Provider != "gke" {
		pkgs = append(pkgs, "ntp")
	}
	return strings.Join(pkgs, " ")
}

var (
	StartupScriptTemplate = template.Must(template.New(api.RoleMaster).Parse(`#!/bin/bash
set -euxo pipefail
# log to /var/log/pharmer.log
exec > >(tee -a /var/log/pharmer.log)
exec 2>&1

export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "init-os" . }}

# https://major.io/2016/05/05/preventing-ubuntu-16-04-starting-daemons-package-installed/
echo -e '#!/bin/bash\nexit 101' > /usr/sbin/policy-rc.d
chmod +x /usr/sbin/policy-rc.d

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates software-properties-common tzdata
curl -fsSL --retry 5 https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list
add-apt-repository -y ppa:gluster/glusterfs-3.10
apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -fsSL --retry 5 -o kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}
curl -fsSL --retry 5 -o pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.9/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

timedatectl set-timezone Etc/UTC
{{ template "prepare-host" . }}

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ if .ExternalProvider }}{{ .KubeletExtraArgsEmptyCloudProviderStr }}{{ else }}{{ .KubeletExtraArgsStr }}{{ end }}"
EOF
systemctl daemon-reload
rm -rf /usr/sbin/policy-rc.d
systemctl enable docker kubelet nfs-utils
systemctl start docker kubelet nfs-utils

kubeadm reset

{{ template "setup-certs" . }}

{{ if .CloudConfig }}
cat > /etc/kubernetes/cloud-config <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

mkdir -p /etc/kubernetes/kubeadm

{{ if .MasterConfiguration }}
cat > /etc/kubernetes/kubeadm/base.yaml <<EOF
{{ .MasterConfigurationYAML }}
EOF
{{ end }}

pre-k merge master-config \
	--config=/etc/kubernetes/kubeadm/base.yaml \
	--apiserver-advertise-address=$(pre-k get public-ips --all=false) \
	--apiserver-cert-extra-sans=$(pre-k get public-ips --routable) \
	--apiserver-cert-extra-sans=$(pre-k get private-ips) \
	--apiserver-cert-extra-sans={{ .ExtraDomains }} \
	> /etc/kubernetes/kubeadm/config.yaml
kubeadm init --config=/etc/kubernetes/kubeadm/config.yaml --skip-token-print

{{ if eq .NetworkProvider "flannel" }}
{{ template "flannel" . }}
{{ else if eq .NetworkProvider "calico" }}
{{ template "calico" . }}
{{ end }}

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/kubeadm-probe/installer.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

{{ if .ExternalProvider }}
{{ template "ccm" . }}
{{end}}

{{ template "prepare-cluster" . }}
`))

	_ = template.Must(StartupScriptTemplate.New(api.RoleNode).Parse(`#!/bin/bash
set -euxo pipefail
# log to /var/log/pharmer.log
exec > >(tee -a /var/log/pharmer.log)
exec 2>&1

export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "init-os" . }}

# https://major.io/2016/05/05/preventing-ubuntu-16-04-starting-daemons-package-installed/
echo -e '#!/bin/bash\nexit 101' > /usr/sbin/policy-rc.d
chmod +x /usr/sbin/policy-rc.d

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates software-properties-common tzdata
curl -fsSL --retry 5 https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list
add-apt-repository -y ppa:gluster/glusterfs-3.10
apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -fsSL --retry 5 -o kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}
curl -fsSL --retry 5 -o pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.8/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

timedatectl set-timezone Etc/UTC
{{ template "prepare-host" . }}

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF
systemctl daemon-reload
rm -rf /usr/sbin/policy-rc.d
systemctl enable docker kubelet nfs-utils
systemctl start docker kubelet nfs-utils

kubeadm reset
{{ .KubeadmTokenLoader  }}
KUBEADM_TOKEN=${KUBEADM_TOKEN:-{{ .KubeadmToken }}}
kubeadm join --token=${KUBEADM_TOKEN} {{ .APIServerAddress }}
`))

	_ = template.Must(StartupScriptTemplate.New("init-os").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("prepare-host").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("prepare-cluster").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("setup-certs").Parse(`
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca.key <<EOF
{{ .CAKey }}
EOF
pre-k get ca-cert --common-name=ca < /etc/kubernetes/pki/ca.key > /etc/kubernetes/pki/ca.crt

cat > /etc/kubernetes/pki/front-proxy-ca.key <<EOF
{{ .FrontProxyKey }}
EOF
pre-k get ca-cert --common-name=front-proxy-ca < /etc/kubernetes/pki/front-proxy-ca.key > /etc/kubernetes/pki/front-proxy-ca.crt
chmod 600 /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key
`))

	_ = template.Must(StartupScriptTemplate.New("ccm").Parse(`
until [ $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/cloud-controller-manager/rbac.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/cloud-controller-manager/{{ .Provider }}/installer.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

until [ $(kubectl get pods -n kube-system -l app=cloud-controller-manager -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

kubectl taint nodes $(uname -n) node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule --kubeconfig /etc/kubernetes/admin.conf

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF
systemctl daemon-reload
systemctl restart kubelet
systemctl restart docker
`))

	_ = template.Must(StartupScriptTemplate.New("calico").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/calico/2.6/calico.yaml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("flannel").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel.yml \
  --kubeconfig /etc/kubernetes/admin.conf
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel-rbac.yml \
  --kubeconfig /etc/kubernetes/admin.conf
`))
)
<commit_msg>Retry add-apt-repository (#219)<commit_after>package cloud

import (
	"bytes"
	"strings"
	"text/template"

	api "github.com/appscode/pharmer/apis/v1alpha1"
	"github.com/ghodss/yaml"
	"github.com/hashicorp/go-version"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)

type TemplateData struct {
	ClusterName        string
	KubeletVersion     string
	KubeadmVersion     string
	KubeadmToken       string
	CAKey              string
	FrontProxyKey      string
	APIServerAddress   string
	ExtraDomains       string
	NetworkProvider    string
	CloudConfig        string
	Provider           string
	ExternalProvider   bool
	KubeadmTokenLoader string

	MasterConfiguration *kubeadmapi.MasterConfiguration
	KubeletExtraArgs    map[string]string
}

func (td TemplateData) MasterConfigurationYAML() (string, error) {
	if td.MasterConfiguration == nil {
		return "", nil
	}
	cb, err := yaml.Marshal(td.MasterConfiguration)
	return string(cb), err
}

func (td TemplateData) IsPreReleaseVersion() bool {
	if v, err := version.NewVersion(td.KubeadmVersion); err == nil && v.Prerelease() != "" {
		return true
	}
	return false
}

func (td TemplateData) KubeletExtraArgsStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) KubeletExtraArgsEmptyCloudProviderStr() string {
	var buf bytes.Buffer
	for k, v := range td.KubeletExtraArgs {
		if k == "cloud-config" {
			continue
		}
		if k == "cloud-provider" {
			v = ""
		}
		buf.WriteString("--")
		buf.WriteString(k)
		buf.WriteRune('=')
		buf.WriteString(v)
		buf.WriteRune(' ')
	}
	return buf.String()
}

func (td TemplateData) PackageList() string {
	pkgs := []string{
		"cron",
		"docker.io",
		"ebtables",
		"git",
		"glusterfs-client",
		"haveged",
		"nfs-common",
		"socat",
	}
	if !td.IsPreReleaseVersion() {
		if td.KubeletVersion == "" {
			pkgs = append(pkgs, "kubelet", "kubectl")
		} else {
			pkgs = append(pkgs, "kubelet="+td.KubeletVersion+"*", "kubectl="+td.KubeletVersion+"*")
		}
		if td.KubeadmVersion == "" {
			pkgs = append(pkgs, "kubeadm")
		} else {
			pkgs = append(pkgs, "kubeadm="+td.KubeadmVersion+"*")
		}
	}
	if td.Provider != "gce" && td.Provider != "gke" {
		pkgs = append(pkgs, "ntp")
	}
	return strings.Join(pkgs, " ")
}

var (
	StartupScriptTemplate = template.Must(template.New(api.RoleMaster).Parse(`#!/bin/bash
set -euxo pipefail
# log to /var/log/pharmer.log
exec > >(tee -a /var/log/pharmer.log)
exec 2>&1

export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "init-os" . }}
{{ template "init-script" }}

# https://major.io/2016/05/05/preventing-ubuntu-16-04-starting-daemons-package-installed/
echo -e '#!/bin/bash\nexit 101' > /usr/sbin/policy-rc.d
chmod +x /usr/sbin/policy-rc.d

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates software-properties-common tzdata
curl -fsSL --retry 5 https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list
add-repo gluster/glusterfs-3.10
apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -fsSL --retry 5 -o kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}
curl -fsSL --retry 5 -o pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.9/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

timedatectl set-timezone Etc/UTC
{{ template "prepare-host" . }}

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ if .ExternalProvider }}{{ .KubeletExtraArgsEmptyCloudProviderStr }}{{ else }}{{ .KubeletExtraArgsStr }}{{ end }}"
EOF
systemctl daemon-reload
rm -rf /usr/sbin/policy-rc.d
systemctl enable docker kubelet nfs-utils
systemctl start docker kubelet nfs-utils

kubeadm reset

{{ template "setup-certs" . }}

{{ if .CloudConfig }}
cat > /etc/kubernetes/cloud-config <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

mkdir -p /etc/kubernetes/kubeadm

{{ if .MasterConfiguration }}
cat > /etc/kubernetes/kubeadm/base.yaml <<EOF
{{ .MasterConfigurationYAML }}
EOF
{{ end }}

pre-k merge master-config \
	--config=/etc/kubernetes/kubeadm/base.yaml \
	--apiserver-advertise-address=$(pre-k get public-ips --all=false) \
	--apiserver-cert-extra-sans=$(pre-k get public-ips --routable) \
	--apiserver-cert-extra-sans=$(pre-k get private-ips) \
	--apiserver-cert-extra-sans={{ .ExtraDomains }} \
	> /etc/kubernetes/kubeadm/config.yaml
kubeadm init --config=/etc/kubernetes/kubeadm/config.yaml --skip-token-print

{{ if eq .NetworkProvider "flannel" }}
{{ template "flannel" . }}
{{ else if eq .NetworkProvider "calico" }}
{{ template "calico" . }}
{{ end }}

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/kubeadm-probe/installer.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

{{ if .ExternalProvider }}
{{ template "ccm" . }}
{{end}}

{{ template "prepare-cluster" . }}
`))

	_ = template.Must(StartupScriptTemplate.New(api.RoleNode).Parse(`#!/bin/bash
set -euxo pipefail
# log to /var/log/pharmer.log
exec > >(tee -a /var/log/pharmer.log)
exec 2>&1

export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "init-os" . }}
{{ template "init-script" }}

# https://major.io/2016/05/05/preventing-ubuntu-16-04-starting-daemons-package-installed/
echo -e '#!/bin/bash\nexit 101' > /usr/sbin/policy-rc.d
chmod +x /usr/sbin/policy-rc.d

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates software-properties-common tzdata
curl -fsSL --retry 5 https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list
add-repo gluster/glusterfs-3.10
apt-get update -y
apt-get install -y {{ .PackageList }} || true
{{ if .IsPreReleaseVersion }}
curl -fsSL --retry 5 -o kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}
curl -fsSL --retry 5 -o pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.8/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

timedatectl set-timezone Etc/UTC
{{ template "prepare-host" . }}

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF
systemctl daemon-reload
rm -rf /usr/sbin/policy-rc.d
systemctl enable docker kubelet nfs-utils
systemctl start docker kubelet nfs-utils

kubeadm reset
{{ .KubeadmTokenLoader  }}
KUBEADM_TOKEN=${KUBEADM_TOKEN:-{{ .KubeadmToken }}}
kubeadm join --token=${KUBEADM_TOKEN} {{ .APIServerAddress }}
`))

	_ = template.Must(StartupScriptTemplate.New("init-os").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("init-script").Parse(`
function add-repo() {
	add-apt-repository -y ppa:$1
	while [ $? -ne 0 ]; do
		sleep 2
		add-apt-repository -y ppa:gluster/$1
	done
}
`))

	_ = template.Must(StartupScriptTemplate.New("prepare-host").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("prepare-cluster").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("setup-certs").Parse(`
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca.key <<EOF
{{ .CAKey }}
EOF
pre-k get ca-cert --common-name=ca < /etc/kubernetes/pki/ca.key > /etc/kubernetes/pki/ca.crt

cat > /etc/kubernetes/pki/front-proxy-ca.key <<EOF
{{ .FrontProxyKey }}
EOF
pre-k get ca-cert --common-name=front-proxy-ca < /etc/kubernetes/pki/front-proxy-ca.key > /etc/kubernetes/pki/front-proxy-ca.crt
chmod 600 /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key
`))

	_ = template.Must(StartupScriptTemplate.New("ccm").Parse(`
until [ $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/cloud-controller-manager/rbac.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/cloud-controller-manager/{{ .Provider }}/installer.yaml \
  --kubeconfig /etc/kubernetes/admin.conf

until [ $(kubectl get pods -n kube-system -l app=cloud-controller-manager -o jsonpath='{.items[0].status.phase}' --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

kubectl taint nodes $(uname -n) node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule --kubeconfig /etc/kubernetes/admin.conf

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS={{ .KubeletExtraArgsStr }}"
EOF
systemctl daemon-reload
systemctl restart kubelet
systemctl restart docker
`))

	_ = template.Must(StartupScriptTemplate.New("calico").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/appscode/pharmer/master/addons/calico/2.6/calico.yaml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("flannel").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel.yml \
  --kubeconfig /etc/kubernetes/admin.conf
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel-rbac.yml \
  --kubeconfig /etc/kubernetes/admin.conf
`))
)
<|endoftext|>


<commit_before>package build

import (
	"fmt"
	"neon/util"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
)

const (
	// PluginSite is default site for plugins
	PluginSite = "github.com"
)

// RegexpParentName is regexp for parent name
var RegexpParentName = regexp.MustCompile(`[^/]+/[^/]+/[^/]+.yml`)
// RegexpTemplateName is regexp for template name
var RegexpTemplateName = regexp.MustCompile(`[^/]+/[^/]+/[^/]+.tpl`)

// FindParents finds parent build files in given repository.
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - list of parent build files relative to repo.
// - error if something went wrong.
func FindParents(repo string) ([]string, error) {
	if repo == "" {
		repo = DefaultRepo
	}
	repo = util.ExpandUserHome(repo)
	files, err := util.FindFiles(repo, []string{"*/*/*.yml"}, nil, false)
	if err != nil {
		return nil, err
	}
	sort.Strings(files)
	return files, nil
}

// FindParent finds a parent in given repository.
// - parent: the parent to find (such as "golang").
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - parent path relative to repo (such as "c4s4/build/golang.tpl").
// - error if something went wrong.
func FindParent(parent, repo string) ([]string, error) {
	files, err := FindParents(repo)
	if err != nil {
		return nil, err
	}
	var parents []string
	for _, file := range files {
		start := strings.LastIndex(file, "/") + 1
		end := strings.LastIndex(file, ".")
		name := file[start:end]
		if name == parent {
			parents = append(parents, file)
		}
	}
	return parents, nil
}

// ParentPath returns file path for plugin with given name.
// - name: the name of the plugin (as "c4s4/build/foo.yml" or "foo")
// Return:
// - the plugin path as a string (as /home/casa/.neon/c4s4/build/foo.yml)
// - error if something went wrong
func (build *Build) ParentPath(name string) (string, error) {
	if path.IsAbs(name) {
		return name, nil
	}
	if strings.HasPrefix(name, "./") {
		return filepath.Join(build.Dir, name), nil
	}
	if RegexpParentName.MatchString(name) {
		return util.ExpandUserHome(filepath.Join(build.Repository, name)), nil
	}
	parents, err := FindParent(name, build.Repository)
	if err != nil || len(parents) == 0 {
		return "", fmt.Errorf("parent '%s' was not found", name)
	}
	if len(parents) > 1 {
		return "", fmt.Errorf("there are %d parents matching name '%s'", len(parents), name)
	}
	return util.ExpandUserHome(filepath.Join(build.Repository, parents[0])), nil
}

// InstallPlugin installs given plugin in repository:
// - plugin: the plugin name such as c4s4/build. First part us Github user name
//   and second is repository name for the plugin.
// - repository: plugin repository, defaults to ~/.neon.
// Return: an error if something went wrong downloading plugin.
func InstallPlugin(plugin, repository string) error {
	re := regexp.MustCompile(`^` + RegexpPlugin + `$`)
	if !re.MatchString(plugin) {
		return fmt.Errorf("plugin name '%s' is invalid", plugin)
	}
	repopath := filepath.Join(repository, plugin)
	if util.DirExists(repopath) {
		Message("Plugin '%s' already installed in '%s'", plugin, repopath)
		return nil
	}
	absolute := util.ExpandUserHome(repopath)
	repo := "git://" + PluginSite + "/" + plugin + ".git"
	command := exec.Command("git", "clone", repo, absolute)
	Message("Running command '%s'...", strings.Join(command.Args, " "))
	output, err := command.CombinedOutput()
	if err != nil {
		re = regexp.MustCompile("\n\n")
		message := re.ReplaceAllString(string(output), "\n")
		message = strings.TrimSpace(message)
		Message(message)
		return fmt.Errorf("installing plugin '%s'", plugin)
	}
	Message("Plugin '%s' installed in '%s'", plugin, repopath)
	return nil
}

// PrintParents prints parent build files in repository:
// - repo: the NeON repository (defaults to '~/.neon')
func PrintParents(repo string) {
	if repo == "" {
		repo = DefaultRepo
	}
	repo = util.ExpandUserHome(repo)
	files, err := util.FindFiles(repo, []string{"*/*/*.yml"}, nil, false)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		name := path.Base(file)
		if name != "CHANGELOG.yml" && name != "build.yml" {
			fmt.Println(file)
		}
	}
}

// FindTemplates finds templates in given repository.
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - list of template files relative to repo.
// - error if something went wrong.
func FindTemplates(repo string) ([]string, error) {
	if repo == "" {
		repo = DefaultRepo
	}
	repo = util.ExpandUserHome(repo)
	files, err := util.FindFiles(repo, []string{"*/*/*.tpl"}, nil, false)
	if err != nil {
		return nil, err
	}
	sort.Strings(files)
	return files, nil
}

// FindTemplate finds a template in given repository.
// - template: the template to find (such as "golang").
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - templates path relative to repo (such as "c4s4/build/golang.tpl").
// - error if something went wrong.
func FindTemplate(template, repo string) ([]string, error) {
	files, err := FindTemplates(repo)
	if err != nil {
		return nil, err
	}
	var templates []string
	for _, file := range files {
		start := strings.LastIndex(file, "/") + 1
		end := strings.LastIndex(file, ".")
		name := file[start:end]
		if name == template {
			templates = append(templates, file)
		}
	}
	return templates, nil
}

// TemplatePath return the template path:
// - name: the name of the template (such as 'c4s4/build/golang.tpl')
// - repo: the repository for plugins (defaults to '~/.neon')
// Return: template path (as '~/.neon/c4s4/build/golang.tpl')
func TemplatePath(name, repo string) (string, error) {
	if path.IsAbs(name) || strings.HasPrefix(name, "./") {
		return name, nil
	}
	if repo == "" {
		repo = DefaultRepo
	}
	if RegexpTemplateName.MatchString(name) {
		return util.ExpandUserHome(filepath.Join(repo, name)), nil
	}
	templates, err := FindTemplate(name, repo)
	if err != nil || len(templates) == 0 {
		return "", fmt.Errorf("template '%s' was not found", name)
	}
	if len(templates) > 1 {
		return "", fmt.Errorf("there are %d templates matching name '%s'", len(templates), name)
	}
	return util.ExpandUserHome(filepath.Join(repo, templates[0])), nil
}

// PrintTemplates prints templates in repository:
// - repo: the NeON repository (defaults to '~/.neon')
func PrintTemplates(repo string) {
	files, err := FindTemplates(repo)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		fmt.Println(file)
	}
}
<commit_msg>Passed fmt<commit_after>package build

import (
	"fmt"
	"neon/util"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
)

const (
	// PluginSite is default site for plugins
	PluginSite = "github.com"
)

// RegexpParentName is regexp for parent name
var RegexpParentName = regexp.MustCompile(`[^/]+/[^/]+/[^/]+.yml`)

// RegexpTemplateName is regexp for template name
var RegexpTemplateName = regexp.MustCompile(`[^/]+/[^/]+/[^/]+.tpl`)

// FindParents finds parent build files in given repository.
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - list of parent build files relative to repo.
// - error if something went wrong.
func FindParents(repo string) ([]string, error) {
	if repo == "" {
		repo = DefaultRepo
	}
	repo = util.ExpandUserHome(repo)
	files, err := util.FindFiles(repo, []string{"*/*/*.yml"}, nil, false)
	if err != nil {
		return nil, err
	}
	sort.Strings(files)
	return files, nil
}

// FindParent finds a parent in given repository.
// - parent: the parent to find (such as "golang").
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - parent path relative to repo (such as "c4s4/build/golang.tpl").
// - error if something went wrong.
func FindParent(parent, repo string) ([]string, error) {
	files, err := FindParents(repo)
	if err != nil {
		return nil, err
	}
	var parents []string
	for _, file := range files {
		start := strings.LastIndex(file, "/") + 1
		end := strings.LastIndex(file, ".")
		name := file[start:end]
		if name == parent {
			parents = append(parents, file)
		}
	}
	return parents, nil
}

// ParentPath returns file path for plugin with given name.
// - name: the name of the plugin (as "c4s4/build/foo.yml" or "foo")
// Return:
// - the plugin path as a string (as /home/casa/.neon/c4s4/build/foo.yml)
// - error if something went wrong
func (build *Build) ParentPath(name string) (string, error) {
	if path.IsAbs(name) {
		return name, nil
	}
	if strings.HasPrefix(name, "./") {
		return filepath.Join(build.Dir, name), nil
	}
	if RegexpParentName.MatchString(name) {
		return util.ExpandUserHome(filepath.Join(build.Repository, name)), nil
	}
	parents, err := FindParent(name, build.Repository)
	if err != nil || len(parents) == 0 {
		return "", fmt.Errorf("parent '%s' was not found", name)
	}
	if len(parents) > 1 {
		return "", fmt.Errorf("there are %d parents matching name '%s'", len(parents), name)
	}
	return util.ExpandUserHome(filepath.Join(build.Repository, parents[0])), nil
}

// InstallPlugin installs given plugin in repository:
// - plugin: the plugin name such as c4s4/build. First part us Github user name
//   and second is repository name for the plugin.
// - repository: plugin repository, defaults to ~/.neon.
// Return: an error if something went wrong downloading plugin.
func InstallPlugin(plugin, repository string) error {
	re := regexp.MustCompile(`^` + RegexpPlugin + `$`)
	if !re.MatchString(plugin) {
		return fmt.Errorf("plugin name '%s' is invalid", plugin)
	}
	repopath := filepath.Join(repository, plugin)
	if util.DirExists(repopath) {
		Message("Plugin '%s' already installed in '%s'", plugin, repopath)
		return nil
	}
	absolute := util.ExpandUserHome(repopath)
	repo := "git://" + PluginSite + "/" + plugin + ".git"
	command := exec.Command("git", "clone", repo, absolute)
	Message("Running command '%s'...", strings.Join(command.Args, " "))
	output, err := command.CombinedOutput()
	if err != nil {
		re = regexp.MustCompile("\n\n")
		message := re.ReplaceAllString(string(output), "\n")
		message = strings.TrimSpace(message)
		Message(message)
		return fmt.Errorf("installing plugin '%s'", plugin)
	}
	Message("Plugin '%s' installed in '%s'", plugin, repopath)
	return nil
}

// PrintParents prints parent build files in repository:
// - repo: the NeON repository (defaults to '~/.neon')
func PrintParents(repo string) {
	if repo == "" {
		repo = DefaultRepo
	}
	repo = util.ExpandUserHome(repo)
	files, err := util.FindFiles(repo, []string{"*/*/*.yml"}, nil, false)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		name := path.Base(file)
		if name != "CHANGELOG.yml" && name != "build.yml" {
			fmt.Println(file)
		}
	}
}

// FindTemplates finds templates in given repository.
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - list of template files relative to repo.
// - error if something went wrong.
func FindTemplates(repo string) ([]string, error) {
	if repo == "" {
		repo = DefaultRepo
	}
	repo = util.ExpandUserHome(repo)
	files, err := util.FindFiles(repo, []string{"*/*/*.tpl"}, nil, false)
	if err != nil {
		return nil, err
	}
	sort.Strings(files)
	return files, nil
}

// FindTemplate finds a template in given repository.
// - template: the template to find (such as "golang").
// - repo: the NeON repository (defaults to '~/.neon')
// Return:
// - templates path relative to repo (such as "c4s4/build/golang.tpl").
// - error if something went wrong.
func FindTemplate(template, repo string) ([]string, error) {
	files, err := FindTemplates(repo)
	if err != nil {
		return nil, err
	}
	var templates []string
	for _, file := range files {
		start := strings.LastIndex(file, "/") + 1
		end := strings.LastIndex(file, ".")
		name := file[start:end]
		if name == template {
			templates = append(templates, file)
		}
	}
	return templates, nil
}

// TemplatePath return the template path:
// - name: the name of the template (such as 'c4s4/build/golang.tpl')
// - repo: the repository for plugins (defaults to '~/.neon')
// Return: template path (as '~/.neon/c4s4/build/golang.tpl')
func TemplatePath(name, repo string) (string, error) {
	if path.IsAbs(name) || strings.HasPrefix(name, "./") {
		return name, nil
	}
	if repo == "" {
		repo = DefaultRepo
	}
	if RegexpTemplateName.MatchString(name) {
		return util.ExpandUserHome(filepath.Join(repo, name)), nil
	}
	templates, err := FindTemplate(name, repo)
	if err != nil || len(templates) == 0 {
		return "", fmt.Errorf("template '%s' was not found", name)
	}
	if len(templates) > 1 {
		return "", fmt.Errorf("there are %d templates matching name '%s'", len(templates), name)
	}
	return util.ExpandUserHome(filepath.Join(repo, templates[0])), nil
}

// PrintTemplates prints templates in repository:
// - repo: the NeON repository (defaults to '~/.neon')
func PrintTemplates(repo string) {
	files, err := FindTemplates(repo)
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		fmt.Println(file)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/animenotifier/arn"
	"github.com/bwmarrin/discordgo"
)

// Session provides access to the Discord session.
var discord *discordgo.Session

func main() {
	var err error

	discord, _ = discordgo.New()
	discord.Token = "Bot " + arn.APIKeys.Discord.Token

	// Verify a Token was provided
	if discord.Token == "" {
		log.Println("You must provide a Discord authentication token.")
		return
	}

	// Verify the Token is valid and grab user information
	discord.State.User, err = discord.User("@me")

	if err != nil {
		log.Printf("Error fetching user information: %s\n", err)
	}

	// Open a websocket connection to Discord
	err = discord.Open()

	if err != nil {
		log.Printf("Error opening connection to Discord, %s\n", err)
	}

	defer discord.Close()

	// Receive messages
	discord.AddHandler(onMessage)

	// Wait for a CTRL-C
	log.Printf("Tsundere is ready. Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc
}

// This function will be called every time a new message is created on any channel.
func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if m.Author.ID == s.State.User.ID {
		return
	}

	if m.Content == "!commands" {
		s.ChannelMessageSend(m.ChannelID, `
**!user** [username]
**!anime** [id]
**!animelist** [username]
**!tag** [forum tag]`)
	}

	// Has the bot been mentioned?
	for _, user := range m.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(m.ChannelID, m.Author.Mention()+" :heart:")
			return
		}
	}

	if strings.HasPrefix(m.Content, "!user ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/+"+strings.Split(m.Content, " ")[1])
		return
	}

	if strings.HasPrefix(m.Content, "!anime ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/anime/"+strings.Split(m.Content, " ")[1])
		return
	}

	if strings.HasPrefix(m.Content, "!animelist ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/+"+strings.Split(m.Content, " ")[1]+"/animelist")
		return
	}

	if strings.HasPrefix(m.Content, "!tag ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/forum/"+strings.ToLower(strings.Split(m.Content, " ")[1]))
		return
	}

	if strings.HasPrefix(m.Content, "!play ") {
		s.UpdateStatus(0, strings.Split(m.Content, " ")[1])
		return
	}

	if strings.HasPrefix(m.Content, "!s ") {
		term := m.Content[len("!s "):]
		users, animes, posts, threads, tracks := arn.Search(term, 3, 3, 3, 3, 3)
		message := ""

		for _, user := range users {
			message += "https://notify.moe" + user.Link() + "\n"
		}

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		for _, post := range posts {
			message += "https://notify.moe" + post.Link() + "\n"
		}

		for _, thread := range threads {
			message += "https://notify.moe" + thread.Link() + "\n"
		}

		for _, track := range tracks {
			message += "https://notify.moe" + track.Link() + "\n"
		}

		if len(users) == 0 && len(animes) == 0 && len(posts) == 0 && len(threads) == 0 && len(tracks) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(m.ChannelID, message)
		return
	}
}
<commit_msg>Fixed discord bot<commit_after>package main

import (
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/animenotifier/arn"
	"github.com/bwmarrin/discordgo"
)

// Session provides access to the Discord session.
var discord *discordgo.Session

func main() {
	var err error

	discord, _ = discordgo.New()
	discord.Token = "Bot " + arn.APIKeys.Discord.Token

	// Verify a Token was provided
	if discord.Token == "" {
		log.Println("You must provide a Discord authentication token.")
		return
	}

	// Verify the Token is valid and grab user information
	discord.State.User, err = discord.User("@me")

	if err != nil {
		log.Printf("Error fetching user information: %s\n", err)
	}

	// Open a websocket connection to Discord
	err = discord.Open()

	if err != nil {
		log.Printf("Error opening connection to Discord, %s\n", err)
	}

	defer discord.Close()

	// Receive messages
	discord.AddHandler(onMessage)

	// Wait for a CTRL-C
	log.Printf("Tsundere is ready. Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc
}

// This function will be called every time a new message is created on any channel.
func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if m.Author.ID == s.State.User.ID {
		return
	}

	if m.Content == "!commands" {
		s.ChannelMessageSend(m.ChannelID, `
**!user** [username]
**!anime** [id]
**!animelist** [username]
**!tag** [forum tag]`)
	}

	// Has the bot been mentioned?
	for _, user := range m.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(m.ChannelID, m.Author.Mention()+" :heart:")
			return
		}
	}

	if strings.HasPrefix(m.Content, "!user ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/+"+strings.Split(m.Content, " ")[1])
		return
	}

	if strings.HasPrefix(m.Content, "!anime ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/anime/"+strings.Split(m.Content, " ")[1])
		return
	}

	if strings.HasPrefix(m.Content, "!animelist ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/+"+strings.Split(m.Content, " ")[1]+"/animelist")
		return
	}

	if strings.HasPrefix(m.Content, "!tag ") {
		s.ChannelMessageSend(m.ChannelID, "https://notify.moe/forum/"+strings.ToLower(strings.Split(m.Content, " ")[1]))
		return
	}

	if strings.HasPrefix(m.Content, "!play ") {
		s.UpdateStatus(0, strings.Split(m.Content, " ")[1])
		return
	}

	if strings.HasPrefix(m.Content, "!s ") {
		term := m.Content[len("!s "):]
		users, animes, posts, threads, tracks, characters := arn.Search(term, 3, 3, 3, 3, 3, 3)
		message := ""

		for _, user := range users {
			message += "https://notify.moe" + user.Link() + "\n"
		}

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		for _, post := range posts {
			message += "https://notify.moe" + post.Link() + "\n"
		}

		for _, thread := range threads {
			message += "https://notify.moe" + thread.Link() + "\n"
		}

		for _, track := range tracks {
			message += "https://notify.moe" + track.Link() + "\n"
		}

		for _, character := range characters {
			message += "https://notify.moe" + character.Link() + "\n"
		}

		if len(users) == 0 && len(animes) == 0 && len(posts) == 0 && len(threads) == 0 && len(tracks) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(m.ChannelID, message)
		return
	}
}
<|endoftext|>


<commit_before>package zmtp

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"strings"
)

type Connection struct {
	rw                io.ReadWriter
	securityMechanism SecurityMechanism
	socket            Socket
	isPrepared        bool
	asServer, otherEndAsServer bool
}

type SocketType string

const (
	ClientSocketType SocketType = "CLIENT"
	ServerSocketType SocketType = "SERVER"
)

func NewConnection(rw io.ReadWriter) *Connection {
	return &Connection{rw: rw}
}

func (c *Connection) Prepare(mechanism SecurityMechanism, socketType SocketType, asServer bool, applicationMetadata map[string]string) (map[string]string, error) {
	if c.isPrepared {
		return nil, errors.New("Connection was already prepared")
	}

	c.isPrepared = true
	c.securityMechanism = mechanism

	var err error
	if c.socket, err = NewSocket(socketType); err != nil {
		return nil, fmt.Errorf("zmqgo/zmtp: Got error while creating socket: %v", err)
	}

	// Send/recv greeting
	if err := c.sendGreeting(asServer); err != nil {
		return nil, fmt.Errorf("zmqgo/zmtp: Got error while sending greeting: %v", err)
	}
	if err := c.recvGreeting(asServer); err != nil {
		return nil, fmt.Errorf("zmqgo/zmtp: Got error while receiving greeting: %v", err)
	}

	// Do security handshake
	if err := mechanism.Handshake(); err != nil {
		return nil, fmt.Errorf("zmqgo/zmtp: Got error while running the security handshake: %v", err)
	}

	// Send/recv metadata
	if err := c.sendMetadata(socketType, applicationMetadata); err != nil {
		return nil, fmt.Errorf("zmqgo/zmtp: Got error while sending metadata: %v", err)
	}

	otherEndApplicationMetaData, err := c.recvMetadata()
	if err != nil {
		return nil, fmt.Errorf("zmqgo/zmtp: Got error while receiving metadata: %v", err)
	}

	return otherEndApplicationMetaData, nil
}

func (c *Connection) sendGreeting(asServer bool) error {
	greeting := greeting{
		SignaturePrefix: signaturePrefix,
		SignatureSuffix: signatureSuffix,
		Version:    version,
	}
	toNullPaddedString(string(c.securityMechanism.Type()), greeting.Mechanism[:])

	if err := binary.Write(c.rw, byteOrder, &greeting); err != nil {
		return err
	}

	return nil
}

func (c *Connection) recvGreeting(asServer bool) error {
	var greeting greeting

	if err := binary.Read(c.rw, byteOrder, &greeting); err != nil {
		return fmt.Errorf("Error while reading: %v", err)
	}

	if greeting.SignaturePrefix != signaturePrefix {
		return fmt.Errorf("Signature prefix received does not correspond with expected signature. Received: %#v. Expected: %#v.", greeting.SignaturePrefix, signaturePrefix)
	}

	if greeting.SignatureSuffix != signatureSuffix {
		return fmt.Errorf("Signature prefix received does not correspond with expected signature. Received: %#v. Expected: %#v.", greeting.SignatureSuffix, signatureSuffix)
	}

	if greeting.Version != version {
		return fmt.Errorf("Version %v.%v received does match expected version %v.%v", int(greeting.Version[0]), int(greeting.Version[1]), int(majorVersion), int(minorVersion))
	}

	var otherMechanism = fromNullPaddedString(greeting.Mechanism[:])
	var thisMechanism = string(c.securityMechanism.Type())
	if thisMechanism != otherMechanism {
		return fmt.Errorf("Encryption mechanism on other side %q does not match this side's %q", otherMechanism, thisMechanism)
	}

	otherEndAsServer, err := fromByteBool(greeting.ServerFlag)
	if err != nil {
		return err
	}
	c.otherEndAsServer = otherEndAsServer

	return nil
}

func (c *Connection) sendMetadata(socketType SocketType, applicationMetadata map[string]string) error {
	buffer := new(bytes.Buffer)
	var usedKeys map[string]struct{}

	for k, v := range applicationMetadata {
		if len(k) == 0 {
			return errors.New("Cannot send empty application metadata key")
		}

		lowerCaseKey := strings.ToLower(k)
		if _, alreadyPresent := usedKeys[lowerCaseKey]; alreadyPresent {
			return fmt.Errorf("Key %q is specified multiple times with different casing", lowerCaseKey)
		}

		usedKeys[lowerCaseKey] = struct{}{}
		c.writeMetadata(buffer, "x-"+lowerCaseKey, v)
	}

	c.writeMetadata(buffer, "socket-type", string(socketType))

	return c.SendCommand("READY", buffer.Bytes())
}

func (c *Connection) writeMetadata(buffer *bytes.Buffer, name string, value string) {
	buffer.WriteByte(byte(len(name)))
	buffer.WriteString(name)
	binary.Write(buffer, byteOrder, uint32(len(value)))
	buffer.WriteString(value)
}

func (c *Connection) recvMetadata() (map[string]string, error) {
	isCommand, body, err := c.read()
	if err != nil {
		return nil, err
	}

	if !isCommand {
		return nil, errors.New("Got a message frame for metadata, expected a command frame")
	}

	command, err := c.parseCommand(body)
	if err != nil {
		return nil, err
	}

	if command.Name != "READY" {
		return nil, fmt.Errorf("Got a %v command for metadata instead of the expected READY command frame", command.Name)
	}

	metadata := make(map[string]string)
	applicationMetadata := make(map[string]string)
	i := 0
	for i < len(command.Body) {
		// Key length
		keyLength := int(command.Body[i])
		if i+keyLength >= len(command.Body) {
			return nil, fmt.Errorf("metadata key of length %v overflows body of length %v at position %v", keyLength, len(command.Body), i)
		}
		i++

		// Key
		key := strings.ToLower(string(command.Body[i : i+keyLength]))
		i += keyLength

		// Value length
		var rawValueLength uint32
		if err := binary.Read(bytes.NewBuffer(command.Body[i:i+4]), byteOrder, &rawValueLength); err != nil {
			return nil, err
		}

		if uint64(rawValueLength) > uint64(maxInt) {
			return nil, fmt.Errorf("Length of value %v overflows integer max length %v on this platform", rawValueLength, maxInt)
		}

		valueLength := int(rawValueLength)
		if i+valueLength >= len(command.Body) {
			return nil, fmt.Errorf("metadata value of length %v overflows body of length %v at position %v", valueLength, len(command.Body), i)
		}
		i += 4

		// Value
		value := string(command.Body[i : i+valueLength])
		i += valueLength

		if strings.HasPrefix(key, "x-") {
			applicationMetadata[key[2:]] = value
		} else {
			metadata[key] = value
		}
	}

	socketType := metadata["socket-type"]
	if !c.socket.IsSocketTypeCompatible(SocketType(socketType)) {
		return nil, fmt.Errorf("Socket type %v is not compatible with %v", c.socket.Type(), socketType)
	}

	return applicationMetadata, nil
}

func (c *Connection) SendCommand(commandName string, body []byte) error {
	if len(commandName) > 255 {
		return errors.New("Command names may not be longer than 255 characters")
	}

	// Make the buffer of the correct lenght and reset it
	buffer := new(bytes.Buffer)
	buffer.WriteByte(byte(len(commandName)))
	buffer.Write([]byte(commandName))
	buffer.Write(body)

	return c.send(true, buffer.Bytes())
}

func (c *Connection) SendFrame(body []byte) error {
	return c.send(false, body)
}

func (c *Connection) send(isCommand bool, body []byte) error {
	// Compute total body length
	length := len(body)

	var bitFlags byte

	// More flag: Unused, we don't support multiframe messages

	// Long flag
	isLong := length > 255
	if isLong {
		bitFlags ^= isLongBitFlag
	}

	// Command flag
	if isCommand {
		bitFlags ^= isCommandBitFlag
	}

	// Write out the message itself
	if _, err := c.rw.Write([]byte{bitFlags}); err != nil {
		return err
	}

	if isLong {
		if err := binary.Write(c.rw, byteOrder, int64(len(body))); err != nil {
			return err
		}
	} else {
		if err := binary.Write(c.rw, byteOrder, uint8(len(body))); err != nil {
			return err
		}
	}

	if _, err := c.rw.Write(c.securityMechanism.Encrypt(body)); err != nil {
		return err
	}

	return nil
}

// Recv starts listening to the ReadWriter and returns two channels: The first one is for messages, the second one is for commands
func (c *Connection) Recv() (<-chan []byte, <-chan *Command, <-chan error) {
	messageOut := make(chan []byte)
	commandOut := make(chan *Command)
	errorOut := make(chan error)

	go func() {
		defer close(messageOut)
		defer close(commandOut)
		defer close(errorOut)

		for {
			// Actually read out the body and send it over the channel now
			isCommand, body, err := c.read()
			if err != nil {
				errorOut <- err
				return
			}

			if !isCommand {
				// Data frame
				messageOut <- body
			} else {
				command, err := c.parseCommand(body)
				if err != nil {
					errorOut <- err
					return
				}

				// Check what type of command we got
				// Certain commands we deal with directly, the rest we send over to the application
				switch command.Name {
				case "PING":
					// When we get a ping, we want to send back a pong, we don't really care about the contents right now
					if err := c.SendCommand("PONG", nil); err != nil {
						errorOut <- err
						return
					}
				default:
					commandOut <- command
				}

			}
		}
	}()

	return messageOut, commandOut, errorOut
}

// read returns the isCommand flag, the body of the message, and optionally an error
func (c *Connection) read() (bool, []byte, error) {
	var header [2]byte
	var longLength [4]byte

	// Read out the header
	readLength := uint64(0)
	for readLength != 2 {
		l, err := c.rw.Read(header[readLength:])
		if err != nil {
			return false, nil, err
		}

		readLength += uint64(l)
	}

	bitFlags := header[0]

	// Read all the flags
	hasMore := bitFlags&hasMoreBitFlag == hasMoreBitFlag
	isLong := bitFlags&isLongBitFlag == isLongBitFlag
	isCommand := bitFlags&isCommandBitFlag == isCommandBitFlag

	// Error out in case get a more flag set to true
	if hasMore {
		return false, nil, errors.New("Received a packet with the MORE flag set to true, we don't support more")
	}

	// Determine the actual length of the body
	bodyLength := uint64(0)
	if isLong {
		// We read 2 bytes of the header already
		// In case of a long message, the length is bytes 2-8 of the header
		// We already have the first byte, so assign it, and then read the rest
		longLength[0] = header[1]

		readLength := 1
		for readLength != 8 {
			l, err := c.rw.Read(longLength[readLength:])
			if err != nil {
				return false, nil, err
			}

			readLength += l
		}

		if err := binary.Read(bytes.NewBuffer(longLength[:]), byteOrder, &bodyLength); err != nil {
			return false, nil, err
		}
	} else {
		// Short message length is just 1 byte, read it
		bodyLength = uint64(header[1])
	}

	if bodyLength > uint64(maxInt64) {
		return false, nil, fmt.Errorf("Body length %v overflows max int64 value %v", bodyLength, maxInt64)
	}

	buffer := new(bytes.Buffer)
	readLength = 0
	for readLength < bodyLength {
		l, err := buffer.ReadFrom(io.LimitReader(c.rw, int64(bodyLength)-int64(readLength)))
		if err != nil {
			return false, nil, err
		}

		readLength += uint64(l)
	}

	return isCommand, buffer.Bytes(), nil
}

func (c *Connection) parseCommand(body []byte) (*Command, error) {
	// Sanity check
	if len(body) == 0 {
		return nil, errors.New("Got empty command frame body")
	}

	// Read out the command length
	commandNameLength := int(body[0])
	if commandNameLength > len(body)-1 {
		return nil, fmt.Errorf("Got command name length %v, which is too long for a body of length %v", commandNameLength, len(body))
	}

	command := &Command{
		Name: string(body[1 : commandNameLength+1]),
		Body: body[1+commandNameLength:],
	}

	return command, nil
}
<commit_msg>Problem: some errors don't state the new name<commit_after>package zmtp

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"strings"
)

type Connection struct {
	rw                         io.ReadWriter
	securityMechanism          SecurityMechanism
	socket                     Socket
	isPrepared                 bool
	asServer, otherEndAsServer bool
}

type SocketType string

const (
	ClientSocketType SocketType = "CLIENT"
	ServerSocketType SocketType = "SERVER"
)

func NewConnection(rw io.ReadWriter) *Connection {
	return &Connection{rw: rw}
}

func (c *Connection) Prepare(mechanism SecurityMechanism, socketType SocketType, asServer bool, applicationMetadata map[string]string) (map[string]string, error) {
	if c.isPrepared {
		return nil, errors.New("Connection was already prepared")
	}

	c.isPrepared = true
	c.securityMechanism = mechanism

	var err error
	if c.socket, err = NewSocket(socketType); err != nil {
		return nil, fmt.Errorf("gomq/zmtp: Got error while creating socket: %v", err)
	}

	// Send/recv greeting
	if err := c.sendGreeting(asServer); err != nil {
		return nil, fmt.Errorf("gomq/zmtp: Got error while sending greeting: %v", err)
	}
	if err := c.recvGreeting(asServer); err != nil {
		return nil, fmt.Errorf("gomq/zmtp: Got error while receiving greeting: %v", err)
	}

	// Do security handshake
	if err := mechanism.Handshake(); err != nil {
		return nil, fmt.Errorf("gomq/zmtp: Got error while running the security handshake: %v", err)
	}

	// Send/recv metadata
	if err := c.sendMetadata(socketType, applicationMetadata); err != nil {
		return nil, fmt.Errorf("gomq/zmtp: Got error while sending metadata: %v", err)
	}

	otherEndApplicationMetaData, err := c.recvMetadata()
	if err != nil {
		return nil, fmt.Errorf("gomq/zmtp: Got error while receiving metadata: %v", err)
	}

	return otherEndApplicationMetaData, nil
}

func (c *Connection) sendGreeting(asServer bool) error {
	greeting := greeting{
		SignaturePrefix: signaturePrefix,
		SignatureSuffix: signatureSuffix,
		Version:         version,
	}
	toNullPaddedString(string(c.securityMechanism.Type()), greeting.Mechanism[:])

	if err := binary.Write(c.rw, byteOrder, &greeting); err != nil {
		return err
	}

	return nil
}

func (c *Connection) recvGreeting(asServer bool) error {
	var greeting greeting

	if err := binary.Read(c.rw, byteOrder, &greeting); err != nil {
		return fmt.Errorf("Error while reading: %v", err)
	}

	if greeting.SignaturePrefix != signaturePrefix {
		return fmt.Errorf("Signature prefix received does not correspond with expected signature. Received: %#v. Expected: %#v.", greeting.SignaturePrefix, signaturePrefix)
	}

	if greeting.SignatureSuffix != signatureSuffix {
		return fmt.Errorf("Signature prefix received does not correspond with expected signature. Received: %#v. Expected: %#v.", greeting.SignatureSuffix, signatureSuffix)
	}

	if greeting.Version != version {
		return fmt.Errorf("Version %v.%v received does match expected version %v.%v", int(greeting.Version[0]), int(greeting.Version[1]), int(majorVersion), int(minorVersion))
	}

	var otherMechanism = fromNullPaddedString(greeting.Mechanism[:])
	var thisMechanism = string(c.securityMechanism.Type())
	if thisMechanism != otherMechanism {
		return fmt.Errorf("Encryption mechanism on other side %q does not match this side's %q", otherMechanism, thisMechanism)
	}

	otherEndAsServer, err := fromByteBool(greeting.ServerFlag)
	if err != nil {
		return err
	}
	c.otherEndAsServer = otherEndAsServer

	return nil
}

func (c *Connection) sendMetadata(socketType SocketType, applicationMetadata map[string]string) error {
	buffer := new(bytes.Buffer)
	var usedKeys map[string]struct{}

	for k, v := range applicationMetadata {
		if len(k) == 0 {
			return errors.New("Cannot send empty application metadata key")
		}

		lowerCaseKey := strings.ToLower(k)
		if _, alreadyPresent := usedKeys[lowerCaseKey]; alreadyPresent {
			return fmt.Errorf("Key %q is specified multiple times with different casing", lowerCaseKey)
		}

		usedKeys[lowerCaseKey] = struct{}{}
		c.writeMetadata(buffer, "x-"+lowerCaseKey, v)
	}

	c.writeMetadata(buffer, "socket-type", string(socketType))

	return c.SendCommand("READY", buffer.Bytes())
}

func (c *Connection) writeMetadata(buffer *bytes.Buffer, name string, value string) {
	buffer.WriteByte(byte(len(name)))
	buffer.WriteString(name)
	binary.Write(buffer, byteOrder, uint32(len(value)))
	buffer.WriteString(value)
}

func (c *Connection) recvMetadata() (map[string]string, error) {
	isCommand, body, err := c.read()
	if err != nil {
		return nil, err
	}

	if !isCommand {
		return nil, errors.New("Got a message frame for metadata, expected a command frame")
	}

	command, err := c.parseCommand(body)
	if err != nil {
		return nil, err
	}

	if command.Name != "READY" {
		return nil, fmt.Errorf("Got a %v command for metadata instead of the expected READY command frame", command.Name)
	}

	metadata := make(map[string]string)
	applicationMetadata := make(map[string]string)
	i := 0
	for i < len(command.Body) {
		// Key length
		keyLength := int(command.Body[i])
		if i+keyLength >= len(command.Body) {
			return nil, fmt.Errorf("metadata key of length %v overflows body of length %v at position %v", keyLength, len(command.Body), i)
		}
		i++

		// Key
		key := strings.ToLower(string(command.Body[i : i+keyLength]))
		i += keyLength

		// Value length
		var rawValueLength uint32
		if err := binary.Read(bytes.NewBuffer(command.Body[i:i+4]), byteOrder, &rawValueLength); err != nil {
			return nil, err
		}

		if uint64(rawValueLength) > uint64(maxInt) {
			return nil, fmt.Errorf("Length of value %v overflows integer max length %v on this platform", rawValueLength, maxInt)
		}

		valueLength := int(rawValueLength)
		if i+valueLength >= len(command.Body) {
			return nil, fmt.Errorf("metadata value of length %v overflows body of length %v at position %v", valueLength, len(command.Body), i)
		}
		i += 4

		// Value
		value := string(command.Body[i : i+valueLength])
		i += valueLength

		if strings.HasPrefix(key, "x-") {
			applicationMetadata[key[2:]] = value
		} else {
			metadata[key] = value
		}
	}

	socketType := metadata["socket-type"]
	if !c.socket.IsSocketTypeCompatible(SocketType(socketType)) {
		return nil, fmt.Errorf("Socket type %v is not compatible with %v", c.socket.Type(), socketType)
	}

	return applicationMetadata, nil
}

func (c *Connection) SendCommand(commandName string, body []byte) error {
	if len(commandName) > 255 {
		return errors.New("Command names may not be longer than 255 characters")
	}

	// Make the buffer of the correct lenght and reset it
	buffer := new(bytes.Buffer)
	buffer.WriteByte(byte(len(commandName)))
	buffer.Write([]byte(commandName))
	buffer.Write(body)

	return c.send(true, buffer.Bytes())
}

func (c *Connection) SendFrame(body []byte) error {
	return c.send(false, body)
}

func (c *Connection) send(isCommand bool, body []byte) error {
	// Compute total body length
	length := len(body)

	var bitFlags byte

	// More flag: Unused, we don't support multiframe messages

	// Long flag
	isLong := length > 255
	if isLong {
		bitFlags ^= isLongBitFlag
	}

	// Command flag
	if isCommand {
		bitFlags ^= isCommandBitFlag
	}

	// Write out the message itself
	if _, err := c.rw.Write([]byte{bitFlags}); err != nil {
		return err
	}

	if isLong {
		if err := binary.Write(c.rw, byteOrder, int64(len(body))); err != nil {
			return err
		}
	} else {
		if err := binary.Write(c.rw, byteOrder, uint8(len(body))); err != nil {
			return err
		}
	}

	if _, err := c.rw.Write(c.securityMechanism.Encrypt(body)); err != nil {
		return err
	}

	return nil
}

// Recv starts listening to the ReadWriter and returns two channels: The first one is for messages, the second one is for commands
func (c *Connection) Recv() (<-chan []byte, <-chan *Command, <-chan error) {
	messageOut := make(chan []byte)
	commandOut := make(chan *Command)
	errorOut := make(chan error)

	go func() {
		defer close(messageOut)
		defer close(commandOut)
		defer close(errorOut)

		for {
			// Actually read out the body and send it over the channel now
			isCommand, body, err := c.read()
			if err != nil {
				errorOut <- err
				return
			}

			if !isCommand {
				// Data frame
				messageOut <- body
			} else {
				command, err := c.parseCommand(body)
				if err != nil {
					errorOut <- err
					return
				}

				// Check what type of command we got
				// Certain commands we deal with directly, the rest we send over to the application
				switch command.Name {
				case "PING":
					// When we get a ping, we want to send back a pong, we don't really care about the contents right now
					if err := c.SendCommand("PONG", nil); err != nil {
						errorOut <- err
						return
					}
				default:
					commandOut <- command
				}

			}
		}
	}()

	return messageOut, commandOut, errorOut
}

// read returns the isCommand flag, the body of the message, and optionally an error
func (c *Connection) read() (bool, []byte, error) {
	var header [2]byte
	var longLength [4]byte

	// Read out the header
	readLength := uint64(0)
	for readLength != 2 {
		l, err := c.rw.Read(header[readLength:])
		if err != nil {
			return false, nil, err
		}

		readLength += uint64(l)
	}

	bitFlags := header[0]

	// Read all the flags
	hasMore := bitFlags&hasMoreBitFlag == hasMoreBitFlag
	isLong := bitFlags&isLongBitFlag == isLongBitFlag
	isCommand := bitFlags&isCommandBitFlag == isCommandBitFlag

	// Error out in case get a more flag set to true
	if hasMore {
		return false, nil, errors.New("Received a packet with the MORE flag set to true, we don't support more")
	}

	// Determine the actual length of the body
	bodyLength := uint64(0)
	if isLong {
		// We read 2 bytes of the header already
		// In case of a long message, the length is bytes 2-8 of the header
		// We already have the first byte, so assign it, and then read the rest
		longLength[0] = header[1]

		readLength := 1
		for readLength != 8 {
			l, err := c.rw.Read(longLength[readLength:])
			if err != nil {
				return false, nil, err
			}

			readLength += l
		}

		if err := binary.Read(bytes.NewBuffer(longLength[:]), byteOrder, &bodyLength); err != nil {
			return false, nil, err
		}
	} else {
		// Short message length is just 1 byte, read it
		bodyLength = uint64(header[1])
	}

	if bodyLength > uint64(maxInt64) {
		return false, nil, fmt.Errorf("Body length %v overflows max int64 value %v", bodyLength, maxInt64)
	}

	buffer := new(bytes.Buffer)
	readLength = 0
	for readLength < bodyLength {
		l, err := buffer.ReadFrom(io.LimitReader(c.rw, int64(bodyLength)-int64(readLength)))
		if err != nil {
			return false, nil, err
		}

		readLength += uint64(l)
	}

	return isCommand, buffer.Bytes(), nil
}

func (c *Connection) parseCommand(body []byte) (*Command, error) {
	// Sanity check
	if len(body) == 0 {
		return nil, errors.New("Got empty command frame body")
	}

	// Read out the command length
	commandNameLength := int(body[0])
	if commandNameLength > len(body)-1 {
		return nil, fmt.Errorf("Got command name length %v, which is too long for a body of length %v", commandNameLength, len(body))
	}

	command := &Command{
		Name: string(body[1 : commandNameLength+1]),
		Body: body[1+commandNameLength:],
	}

	return command, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/jack-zh/ztodo/task"
	"github.com/jack-zh/ztodo/utils"
)

var noAct = errors.New("")

var userconfig_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "userconfig.json")
var cloud_work_tasks_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "worktasks.json")
var cloud_backup_tasks_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "backuptasks.json")
var simple_tasks_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "simpletasks")

const version = "0.4.7"
const build_time = "2015-02-25"
const usage = `Incorrect Usage.

NAME:
   ztodo - a command line todo list!

USAGE:
   ztodo [global options] command [command options] [arguments...]

VERSION:
   version:` + version + " (" + build_time + ") build" + `

AUTHOR:
  Jack.z - <zzh.coder@qq.com>

COMMANDS:

	ztodo list|ls [verbose]    -- Show all tasks
	ztodo list|ls N [verbose]  -- Show task N
	ztodo rm|remove N          -- Remove task N
	ztodo done N               -- Done task N
	ztodo undo N               -- Undo task N
	ztodo doing N              -- Doing task N
	ztodo clean                -- Rm done task
	ztodo clear                -- Rm all task
	ztodo add ...              -- Add task to list

GLOBAL OPTIONS:
	ztodo version              -- Show ztodo version
	ztodo help                 -- Show usage
`

func printSimpleTask(t string, i string) {
	if strings.HasPrefix(t, "0") {
		t = strings.Replace(t, "0", "[New]", 1)
	}
	if strings.HasPrefix(t, "1") {
		t = strings.Replace(t, "1", "[Doing ]", 1)
	}
	if strings.HasPrefix(t, "2") {
		t = strings.Replace(t, "2", "[Done  ]", 1)
	}
	fmt.Printf("%2s: %s\n", i, t)
}

func dirCheck() error {
	var filename = filepath.Join(os.Getenv("HOME"), ".ztodo")
	finfo, err := os.Stat(filename)
	if err != nil {
		os.Mkdir(filename, os.ModePerm)
		return nil
	}
	if finfo.IsDir() {
		return nil
	} else {
		return errors.New("$HOME/.ztodo is a file not dir.")
	}
}

func printUsgaes() {
	fmt.Println("Happy New Year.\n")
	fmt.Fprint(os.Stdout, usage)
}

func main() {
	errdir := dirCheck()
	if errdir != nil {
		os.Exit(1)
	}
	flag.Usage = func() {
		fmt.Fprint(os.Stderr, usage)
		flag.PrintDefaults()
	}
	flag.Parse()

	simplelist := task.SimpleNewList(simple_tasks_filename)
	cloudlist := task.CloudNewList(cloud_work_tasks_filename, cloud_backup_tasks_filename, userconfig_filename)
	a, n := flag.Arg(0), len(flag.Args())

	a = strings.ToLower(a)
	if a == "ls" {
		a = "list"
	} else if a == "remove" {
		a = "rm"
	} else if a == "simplels" {
		a = "simplelist"
	} else if a == "simpleremove" {
		a = "simplerm"
	}

	err := noAct
	switch {
	case a == "version" && n == 1:
		fmt.Println("ztodo version " + version + " (" + build_time + ") build")
		err = nil

	case a == "help" && n == 1:
		fmt.Println(usage)
		err = nil

	case a == "simplelist" && n == 1:
		var tasks []string
		tasks, err = simplelist.SimpleGet()
		for i := 0; i < len(tasks); i++ {
			printSimpleTask(tasks[i], strconv.Itoa(i+1))
		}
	case a == "simplelist" && n == 2:
		i, err2 := strconv.Atoi(flag.Arg(1))
		if err2 != nil {
			printUsgaes()
			break
		}
		var task string
		task, err = simplelist.SimpleGetTask(i - 1)
		if err == nil {
			printSimpleTask(task, strconv.Itoa(i))
		}
	case a == "simplerm" && n == 2:
		i, err2 := strconv.Atoi(flag.Arg(1))
		if err2 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleRemoveTask(i - 1)
		if err != nil {
			break
		}
	case a == "simpleadd" && n > 1:
		t := strings.Join(flag.Args()[1:], " ")
		err = simplelist.SimpleAddTask(t)
		err = cloudlist.CloudAddTask(t)

	case a == "simpledoing" && n == 2:
		i, err3 := strconv.Atoi(flag.Args()[1])
		if err3 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleDoingTask(i - 1)

	case a == "simpledone" && n == 2:
		i, err4 := strconv.Atoi(flag.Args()[1])
		if err4 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleDoneTask(i - 1)
	case a == "simpleundo" && n == 2:
		i, err5 := strconv.Atoi(flag.Args()[1])
		if err5 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleUndoTask(i - 1)
	case a == "simpleclean" && n == 1:
		err = simplelist.SimpleCleanTask()
	case a == "simpleclear" && n == 1:
		err = simplelist.SimpleClearTask()

	case a == "list" && n == 1:
		err = cloudlist.CloudGetAllWorkTaskByFile()
		if err == nil {
			cloudlist.CloudTasksPrint(-1)
		}

	case a == "list" && n == 2:
		if flag.Arg(1) == "verbose" {
			err = cloudlist.CloudGetAllWorkTaskByFile()
			if err == nil {
				cloudlist.CloudTasksPrintVerbose(-1)
			}
		} else {
			i, err2 := strconv.Atoi(flag.Arg(1))
			if err2 != nil {
				printUsgaes()
				break
			}
			err = cloudlist.CloudGetAllWorkTaskByFile()
			if err == nil {
				cloudlist.CloudTasksPrint(i)
			}
		}
	case a == "list" && n == 3:
		if flag.Arg(2) == "verbose" {
			i, err2 := strconv.Atoi(flag.Arg(1))
			if err2 != nil {
				printUsgaes()
				break
			}
			err = cloudlist.CloudGetAllWorkTaskByFile()
			if err == nil {
				cloudlist.CloudTasksPrintVerbose(i)
			}
		} else {
			printUsgaes()
		}

	case a == "rm" && n == 2:
		i, err2 := strconv.Atoi(flag.Arg(1))
		if err2 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudRemoveTask(i)
		if err != nil {
			break
		}
	case a == "add" && n > 1:
		t := strings.Join(flag.Args()[1:], " ")
		err = cloudlist.CloudAddTask(t)

	case a == "doing" && n == 2:
		i, err3 := strconv.Atoi(flag.Args()[1])
		if err3 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudDoingTask(i)

	case a == "done" && n == 2:
		i, err4 := strconv.Atoi(flag.Args()[1])
		if err4 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudDoneTask(i)
	case a == "undo" && n == 2:
		i, err5 := strconv.Atoi(flag.Args()[1])
		if err5 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudUndoTask(i)
	case a == "clean" && n == 1:
		err = cloudlist.CloudCleanTask()
	case a == "clear" && n == 1:
		err = cloudlist.CloudClearTask()

	case a == "pull" && n == 1:
		_, _ = cloudlist.CloudPullAll()
	case a == "pull" && n == 2:
		i, err6 := strconv.Atoi(flag.Args()[1])
		if err6 != nil {
			printUsgaes()
			break
		}
		_, _ = cloudlist.CloudPullOne(i)
	case a == "push" && n == 1:
		_ = cloudlist.CloudPushAll()
	case a == "push" && n == 2:
		i, err7 := strconv.Atoi(flag.Args()[1])
		if err7 != nil {
			printUsgaes()
			break
		}
		_ = cloudlist.CloudPushOne(i)
	case a == "signup" && n == 1:
		username, password, retypepassword := utils.CredentialsRetype()
		if password == retypepassword {
			err = cloudlist.Signup(username, password)
		} else {
			err = errors.New("Mismatch")
		}
	case a == "login" && n == 1:
		username, password := utils.Credentials()
		err = cloudlist.Login(username, password)

	case a == "logout" && n == 1:
		err = cloudlist.Logout()

	case a == "user" && n == 1:
		err = cloudlist.ShowUserConfig()

	case a == "staying-up" && n == 1:
		fmt.Print("Happy New Year.")

	default:
		printUsgaes()
		err = nil
		os.Exit(0)
	}
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<commit_msg>update hello<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/jack-zh/ztodo/task"
	"github.com/jack-zh/ztodo/utils"
)

var noAct = errors.New("")

var userconfig_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "userconfig.json")
var cloud_work_tasks_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "worktasks.json")
var cloud_backup_tasks_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "backuptasks.json")
var simple_tasks_filename = filepath.Join(os.Getenv("HOME"), ".ztodo", "simpletasks")

const version = "0.4.8"
const build_time = "2015-03-23"
const usage = `Incorrect Usage.

NAME:
   ztodo - a command line todo list!

USAGE:
   ztodo [global options] command [command options] [arguments...]

VERSION:
   version:` + version + " (" + build_time + ") build" + `

AUTHOR:
  Jack.z - <zzh.coder@qq.com>

COMMANDS:

	ztodo list|ls [verbose]    -- Show all tasks
	ztodo list|ls N [verbose]  -- Show task N
	ztodo rm|remove N          -- Remove task N
	ztodo done N               -- Done task N
	ztodo undo N               -- Undo task N
	ztodo doing N              -- Doing task N
	ztodo clean                -- Rm done task
	ztodo clear                -- Rm all task
	ztodo add ...              -- Add task to list

GLOBAL OPTIONS:
	ztodo version              -- Show ztodo version
	ztodo help                 -- Show usage
`

func printSimpleTask(t string, i string) {
	if strings.HasPrefix(t, "0") {
		t = strings.Replace(t, "0", "[New]", 1)
	}
	if strings.HasPrefix(t, "1") {
		t = strings.Replace(t, "1", "[Doing ]", 1)
	}
	if strings.HasPrefix(t, "2") {
		t = strings.Replace(t, "2", "[Done  ]", 1)
	}
	fmt.Printf("%2s: %s\n", i, t)
}

func dirCheck() error {
	var filename = filepath.Join(os.Getenv("HOME"), ".ztodo")
	finfo, err := os.Stat(filename)
	if err != nil {
		os.Mkdir(filename, os.ModePerm)
		return nil
	}
	if finfo.IsDir() {
		return nil
	} else {
		return errors.New("$HOME/.ztodo is a file not dir.")
	}
}

func printUsgaes() {
	fmt.Println("Have a nice day.\n")
	fmt.Fprint(os.Stdout, usage)
}

func main() {
	errdir := dirCheck()
	if errdir != nil {
		os.Exit(1)
	}
	flag.Usage = func() {
		fmt.Fprint(os.Stderr, usage)
		flag.PrintDefaults()
	}
	flag.Parse()

	simplelist := task.SimpleNewList(simple_tasks_filename)
	cloudlist := task.CloudNewList(cloud_work_tasks_filename, cloud_backup_tasks_filename, userconfig_filename)
	a, n := flag.Arg(0), len(flag.Args())

	a = strings.ToLower(a)
	if a == "ls" {
		a = "list"
	} else if a == "remove" {
		a = "rm"
	} else if a == "simplels" {
		a = "simplelist"
	} else if a == "simpleremove" {
		a = "simplerm"
	}

	err := noAct
	switch {
	case a == "version" && n == 1:
		fmt.Println("ztodo version " + version + " (" + build_time + ") build")
		err = nil

	case a == "help" && n == 1:
		fmt.Println(usage)
		err = nil

	case a == "simplelist" && n == 1:
		var tasks []string
		tasks, err = simplelist.SimpleGet()
		for i := 0; i < len(tasks); i++ {
			printSimpleTask(tasks[i], strconv.Itoa(i+1))
		}
	case a == "simplelist" && n == 2:
		i, err2 := strconv.Atoi(flag.Arg(1))
		if err2 != nil {
			printUsgaes()
			break
		}
		var task string
		task, err = simplelist.SimpleGetTask(i - 1)
		if err == nil {
			printSimpleTask(task, strconv.Itoa(i))
		}
	case a == "simplerm" && n == 2:
		i, err2 := strconv.Atoi(flag.Arg(1))
		if err2 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleRemoveTask(i - 1)
		if err != nil {
			break
		}
	case a == "simpleadd" && n > 1:
		t := strings.Join(flag.Args()[1:], " ")
		err = simplelist.SimpleAddTask(t)
		err = cloudlist.CloudAddTask(t)

	case a == "simpledoing" && n == 2:
		i, err3 := strconv.Atoi(flag.Args()[1])
		if err3 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleDoingTask(i - 1)

	case a == "simpledone" && n == 2:
		i, err4 := strconv.Atoi(flag.Args()[1])
		if err4 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleDoneTask(i - 1)
	case a == "simpleundo" && n == 2:
		i, err5 := strconv.Atoi(flag.Args()[1])
		if err5 != nil {
			printUsgaes()
			break
		}
		err = simplelist.SimpleUndoTask(i - 1)
	case a == "simpleclean" && n == 1:
		err = simplelist.SimpleCleanTask()
	case a == "simpleclear" && n == 1:
		err = simplelist.SimpleClearTask()

	case a == "list" && n == 1:
		err = cloudlist.CloudGetAllWorkTaskByFile()
		if err == nil {
			cloudlist.CloudTasksPrint(-1)
		}

	case a == "list" && n == 2:
		if flag.Arg(1) == "verbose" {
			err = cloudlist.CloudGetAllWorkTaskByFile()
			if err == nil {
				cloudlist.CloudTasksPrintVerbose(-1)
			}
		} else {
			i, err2 := strconv.Atoi(flag.Arg(1))
			if err2 != nil {
				printUsgaes()
				break
			}
			err = cloudlist.CloudGetAllWorkTaskByFile()
			if err == nil {
				cloudlist.CloudTasksPrint(i)
			}
		}
	case a == "list" && n == 3:
		if flag.Arg(2) == "verbose" {
			i, err2 := strconv.Atoi(flag.Arg(1))
			if err2 != nil {
				printUsgaes()
				break
			}
			err = cloudlist.CloudGetAllWorkTaskByFile()
			if err == nil {
				cloudlist.CloudTasksPrintVerbose(i)
			}
		} else {
			printUsgaes()
		}

	case a == "rm" && n == 2:
		i, err2 := strconv.Atoi(flag.Arg(1))
		if err2 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudRemoveTask(i)
		if err != nil {
			break
		}
	case a == "add" && n > 1:
		t := strings.Join(flag.Args()[1:], " ")
		err = cloudlist.CloudAddTask(t)

	case a == "doing" && n == 2:
		i, err3 := strconv.Atoi(flag.Args()[1])
		if err3 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudDoingTask(i)

	case a == "done" && n == 2:
		i, err4 := strconv.Atoi(flag.Args()[1])
		if err4 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudDoneTask(i)
	case a == "undo" && n == 2:
		i, err5 := strconv.Atoi(flag.Args()[1])
		if err5 != nil {
			printUsgaes()
			break
		}
		err = cloudlist.CloudUndoTask(i)
	case a == "clean" && n == 1:
		err = cloudlist.CloudCleanTask()
	case a == "clear" && n == 1:
		err = cloudlist.CloudClearTask()

	case a == "pull" && n == 1:
		_, _ = cloudlist.CloudPullAll()
	case a == "pull" && n == 2:
		i, err6 := strconv.Atoi(flag.Args()[1])
		if err6 != nil {
			printUsgaes()
			break
		}
		_, _ = cloudlist.CloudPullOne(i)
	case a == "push" && n == 1:
		_ = cloudlist.CloudPushAll()
	case a == "push" && n == 2:
		i, err7 := strconv.Atoi(flag.Args()[1])
		if err7 != nil {
			printUsgaes()
			break
		}
		_ = cloudlist.CloudPushOne(i)
	case a == "signup" && n == 1:
		username, password, retypepassword := utils.CredentialsRetype()
		if password == retypepassword {
			err = cloudlist.Signup(username, password)
		} else {
			err = errors.New("Mismatch")
		}
	case a == "login" && n == 1:
		username, password := utils.Credentials()
		err = cloudlist.Login(username, password)

	case a == "logout" && n == 1:
		err = cloudlist.Logout()

	case a == "user" && n == 1:
		err = cloudlist.ShowUserConfig()

	case a == "staying-up" && n == 1:
		fmt.Print("Happy New Year.")

	default:
		printUsgaes()
		err = nil
		os.Exit(0)
	}
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/mitchellh/ioprogress"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

var basespaceApiUrl = "https://api.basespace.illumina.com/v1pre3"

func main() {
	app := cli.NewApp()
	app.Name = "basespace-download"
	app.Version = "basespace-download"
	app.Usage = "basespace-download - Basespace file downloader"
	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "t", Value: "", Usage: "Application token from Basespace"},
		cli.StringFlag{Name: "s", Value: "", Usage: "Sample ID to download"},
		cli.StringFlag{Name: "p", Value: "", Usage: "Project ID to download (all samples)"},
		cli.BoolFlag{Name: "dr", Usage: "Dry-run (don't download files)"},
	}

	app.Action = func(c *cli.Context) {
		if c.String("t") == "" {
			fmt.Fprintf(os.Stderr, "Missing app-token! You must obtain an Application Token from Illumina!\n\n")
			os.Exit(1)
		} else if c.String("s") != "" {
			downloadSample(c.String("t"), c.String("s"), "", "", c.Bool("dr"))
		} else if c.String("p") != "" {
			downloadProject(c.String("t"), c.String("p"), c.Bool("dr"))
		} else {
			fmt.Fprintf(os.Stderr, "You must specify either a sample (-s) or project (-p) to download!\n\n")
			os.Exit(1)
		}
	}

	app.Run(os.Args)

}

func downloadSample(token, sampleId, sampleName, prefix string, dryrun bool) {
	if sampleName == "" {
		sampleName = getSampleName(token, sampleId)
	}
	fmt.Fprintf(os.Stderr, "%sSample: [%s] %s\n", prefix, sampleId, sampleName)

	offset := 0
	total := 0

	for total == 0 || offset < total {
		url := fmt.Sprintf("%s/samples/%s/files?Offset=%d&access_token=", basespaceApiUrl, sampleId, offset)

		resp, err := http.Get(url + token)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
			os.Exit(1)
		}

		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
			os.Exit(1)
		}

		var f map[string]interface{}

		if err = json.Unmarshal(body, &f); err != nil {
			fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
			os.Exit(1)
		}

		for k, v := range f {
			if k == "Response" {
				for k2, v2 := range v.(map[string]interface{}) {
					if k2 == "Items" {
						items := v2.([]interface{})
						for i := range items {
							v3 := items[i].(map[string]interface{})
							fileId := v3["Id"].(string)
							filename := v3["Name"].(string)
							fileSize := v3["Size"].(float64)
							downloadFile(token, fileId, filename, int64(fileSize), prefix+"  ", dryrun)
						}
					}
				}
				total = int(v.(map[string]interface{})["TotalCount"].(float64))
				displayed := int(v.(map[string]interface{})["DisplayedCount"].(float64))
				offset += displayed
			}
		}
	}

}

func downloadFile(token, fileId, filename string, fileSize int64, prefix string, dryrun bool) {
	fmt.Fprintf(os.Stderr, "%s%s\n", prefix, filename)

	if dryrun {
		return
	}

	url := basespaceApiUrl + "/files/" + fileId + "/content?access_token="
	resp, err := http.Get(url + token)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
		os.Exit(1)
	}

	out, err := os.Create(filename)
	defer out.Close()

	defer resp.Body.Close()

	bar := ioprogress.DrawTextFormatBar(20)
	fmtfunc := func(progress, total int64) string {
		return fmt.Sprintf(
			"%s%s %s",
			prefix,
			bar(progress, total),
			ioprogress.DrawTextFormatBytes(progress, total))
	}

	progressR := &ioprogress.Reader{
		Reader:   resp.Body,
		Size:     fileSize,
		DrawFunc: ioprogress.DrawTerminalf(os.Stderr, fmtfunc),
	}

	n, err := io.Copy(out, progressR)
	if err != nil {
		log.Fatal(err, n)
	}
}

func getProjectName(token, projectId string) string {
	url := basespaceApiUrl + "/projects/" + projectId + "/?access_token="

	resp, err := http.Get(url + token)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
		os.Exit(1)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
		os.Exit(1)
	}

	var f map[string]interface{}

	if err = json.Unmarshal(body, &f); err != nil {
		fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
		os.Exit(1)
	}

	return (f["Response"].(map[string]interface{}))["Name"].(string)
}

func getSampleName(token, sampleId string) string {
	url := basespaceApiUrl + "/samples/" + sampleId + "/?access_token="

	resp, err := http.Get(url + token)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
		os.Exit(1)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
		os.Exit(1)
	}

	var f map[string]interface{}

	if err = json.Unmarshal(body, &f); err != nil {
		fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
		os.Exit(1)
	}

	return (f["Response"].(map[string]interface{}))["Name"].(string)
}

func downloadProject(token, projectId string, dryrun bool) {
	fmt.Fprintf(os.Stderr, "Project: [%s] %s\n", projectId, getProjectName(token, projectId))

	offset := 0
	total := 0

	for total == 0 || offset < total {
		url := fmt.Sprintf("%s/projects/%s/samples?Offset=%d&access_token=", basespaceApiUrl, projectId, offset)

		resp, err := http.Get(url + token)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
			os.Exit(1)
		}

		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
			os.Exit(1)
		}

		var f map[string]interface{}

		if err = json.Unmarshal(body, &f); err != nil {
			fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
			os.Exit(1)
		}

		for k, v := range f {
			if k == "Response" {
				for k2, v2 := range v.(map[string]interface{}) {
					if k2 == "Items" {
						items := v2.([]interface{})
						for i := range items {
							v3 := items[i].(map[string]interface{})
							sampleId := v3["Id"].(string)
							sampleName := v3["Name"].(string)
							downloadSample(token, sampleId, sampleName, "  ", dryrun)
						}
					}
				}
				total = int(v.(map[string]interface{})["TotalCount"].(float64))
				displayed := int(v.(map[string]interface{})["DisplayedCount"].(float64))
				offset += displayed
			}
		}
	}
}
<commit_msg>app token can now be an ENVVAR, better help messages<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/mitchellh/ioprogress"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

var basespaceApiUrl = "https://api.basespace.illumina.com/v1pre3"

func main() {
	app := cli.NewApp()
	app.Name = "basespace-download"
	app.Version = "basespace-download"
	app.Usage = "basespace-download - Basespace file downloader"
	app.Flags = []cli.Flag{
		cli.StringFlag{Name: "t", Value: "", Usage: "Application token from Basespace", EnvVar: "BASESPACE_APP_TOKEN"},
		cli.StringFlag{Name: "s", Value: "", Usage: "Sample ID to download"},
		cli.StringFlag{Name: "p", Value: "", Usage: "Project ID to download (all samples)"},
		cli.BoolFlag{Name: "dr", Usage: "Dry-run (don't download files)"},
	}

	app.Action = func(c *cli.Context) {
		if c.String("s") != "" {
			if c.String("t") == "" {
				fmt.Fprintf(os.Stderr, "ERROR: Missing app-token! You must obtain an Application Token from Illumina!\n\n")
				os.Exit(1)
			}
			downloadSample(c.String("t"), c.String("s"), "", "", c.Bool("dr"))
		} else if c.String("p") != "" {
			if c.String("t") == "" {
				fmt.Fprintf(os.Stderr, "ERROR: Missing app-token! You must obtain an Application Token from Illumina!\n\n")
				os.Exit(1)
			}
			downloadProject(c.String("t"), c.String("p"), c.Bool("dr"))
		} else {
			fmt.Fprintf(os.Stderr, "ERROR: You must specify either a sample (-s) or project (-p) to download!\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		}
	}

	app.Run(os.Args)

}

func downloadSample(token, sampleId, sampleName, prefix string, dryrun bool) {
	if sampleName == "" {
		sampleName = getSampleName(token, sampleId)
	}
	fmt.Fprintf(os.Stderr, "%sSample: [%s] %s\n", prefix, sampleId, sampleName)

	offset := 0
	total := 0

	for total == 0 || offset < total {
		url := fmt.Sprintf("%s/samples/%s/files?Offset=%d&access_token=", basespaceApiUrl, sampleId, offset)

		resp, err := http.Get(url + token)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
			os.Exit(1)
		}

		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
			os.Exit(1)
		}

		var f map[string]interface{}

		if err = json.Unmarshal(body, &f); err != nil {
			fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
			os.Exit(1)
		}

		for k, v := range f {
			if k == "Response" {
				for k2, v2 := range v.(map[string]interface{}) {
					if k2 == "Items" {
						items := v2.([]interface{})
						for i := range items {
							v3 := items[i].(map[string]interface{})
							fileId := v3["Id"].(string)
							filename := v3["Name"].(string)
							fileSize := v3["Size"].(float64)
							downloadFile(token, fileId, filename, int64(fileSize), prefix+"  ", dryrun)
						}
					}
				}
				total = int(v.(map[string]interface{})["TotalCount"].(float64))
				displayed := int(v.(map[string]interface{})["DisplayedCount"].(float64))
				offset += displayed
			}
		}
	}

}

func downloadFile(token, fileId, filename string, fileSize int64, prefix string, dryrun bool) {
	fmt.Fprintf(os.Stderr, "%s%s\n", prefix, filename)

	if dryrun {
		return
	}

	url := basespaceApiUrl + "/files/" + fileId + "/content?access_token="
	resp, err := http.Get(url + token)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
		os.Exit(1)
	}

	out, err := os.Create(filename)
	defer out.Close()

	defer resp.Body.Close()

	bar := ioprogress.DrawTextFormatBar(20)
	fmtfunc := func(progress, total int64) string {
		return fmt.Sprintf(
			"%s%s %s",
			prefix,
			bar(progress, total),
			ioprogress.DrawTextFormatBytes(progress, total))
	}

	progressR := &ioprogress.Reader{
		Reader:   resp.Body,
		Size:     fileSize,
		DrawFunc: ioprogress.DrawTerminalf(os.Stderr, fmtfunc),
	}

	n, err := io.Copy(out, progressR)
	if err != nil {
		log.Fatal(err, n)
	}
}

func getProjectName(token, projectId string) string {
	url := basespaceApiUrl + "/projects/" + projectId + "/?access_token="

	resp, err := http.Get(url + token)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
		os.Exit(1)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
		os.Exit(1)
	}

	var f map[string]interface{}

	if err = json.Unmarshal(body, &f); err != nil {
		fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
		os.Exit(1)
	}

	return (f["Response"].(map[string]interface{}))["Name"].(string)
}

func getSampleName(token, sampleId string) string {
	url := basespaceApiUrl + "/samples/" + sampleId + "/?access_token="

	resp, err := http.Get(url + token)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
		os.Exit(1)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
		os.Exit(1)
	}

	var f map[string]interface{}

	if err = json.Unmarshal(body, &f); err != nil {
		fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
		os.Exit(1)
	}

	return (f["Response"].(map[string]interface{}))["Name"].(string)
}

func downloadProject(token, projectId string, dryrun bool) {
	fmt.Fprintf(os.Stderr, "Project: [%s] %s\n", projectId, getProjectName(token, projectId))

	offset := 0
	total := 0

	for total == 0 || offset < total {
		url := fmt.Sprintf("%s/projects/%s/samples?Offset=%d&access_token=", basespaceApiUrl, projectId, offset)

		resp, err := http.Get(url + token)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error downloading URL: %s\n\n", url)
			os.Exit(1)
		}

		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error processing reading result: %s\n\n", string(body))
			os.Exit(1)
		}

		var f map[string]interface{}

		if err = json.Unmarshal(body, &f); err != nil {
			fmt.Fprintf(os.Stderr, "Error processing JSON: %s\n\n", string(body))
			os.Exit(1)
		}

		for k, v := range f {
			if k == "Response" {
				for k2, v2 := range v.(map[string]interface{}) {
					if k2 == "Items" {
						items := v2.([]interface{})
						for i := range items {
							v3 := items[i].(map[string]interface{})
							sampleId := v3["Id"].(string)
							sampleName := v3["Name"].(string)
							downloadSample(token, sampleId, sampleName, "  ", dryrun)
						}
					}
				}
				total = int(v.(map[string]interface{})["TotalCount"].(float64))
				displayed := int(v.(map[string]interface{})["DisplayedCount"].(float64))
				offset += displayed
			}
		}
	}
}
<|endoftext|>