<commit_before>package main

import (
	"io/ioutil"
	"net/url"
	"os"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/docker/docker/pkg/tlsconfig"
	"github.com/docker/libkv"
	kvstore "github.com/docker/libkv/store"
	"github.com/docker/libkv/store/consul"
	"github.com/docker/libkv/store/etcd"
	"github.com/ehazlett/interlock/config"
	"github.com/ehazlett/interlock/server"
	"github.com/ehazlett/interlock/version"
)

const (
	defaultConfig = `listenAddr = ":8080"
dockerURL = "unix:///var/run/docker.sock"
`
	kvConfigKey = "/v1/interlock/config"
)

var cmdRun = cli.Command{
	Name:   "run",
	Action: runAction,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "path to config file",
			Value: "config.toml",
		},
		cli.StringFlag{
			Name:  "discovery, k",
			Usage: "discovery address",
			Value: "",
		},
		cli.StringFlag{
			Name:  "discovery-tls-ca-cert",
			Usage: "discovery tls ca certificate",
			Value: "",
		},
		cli.StringFlag{
			Name:  "discovery-tls-cert",
			Usage: "discovery tls certificate",
			Value: "",
		},
		cli.StringFlag{
			Name:  "discovery-tls-key",
			Usage: "discovery tls key",
			Value: "",
		},
	},
}

func init() {
	consul.Register()
	etcd.Register()
}

func getKVStore(addr string, options *kvstore.Config) (kvstore.Store, error) {
	u, err := url.Parse(addr)
	if err != nil {
		return nil, err

	}

	kvType := strings.ToLower(u.Scheme)
	kvHost := u.Host
	var backend kvstore.Backend

	switch kvType {
	case "consul":
		backend = kvstore.CONSUL
	case "etcd":
		backend = kvstore.ETCD
	}

	kv, err := libkv.NewStore(
		backend,
		[]string{kvHost},
		options,
	)

	if err != nil {
		return nil, err

	}

	return kv, nil
}

func runAction(c *cli.Context) {
	log.Infof("interlock %s", version.FullVersion())

	// init kv
	kvOpts := &kvstore.Config{
		ConnectionTimeout: time.Second * 10,
	}

	dURL := c.String("discovery")
	dTLSCACert := c.String("discovery-tls-ca-cert")
	dTLSCert := c.String("discovery-tls-cert")
	dTLSKey := c.String("discovery-tls-key")

	var data string
	if dURL != "" {
		log.Debugf("using kv: addr=%s", dURL)
		if dTLSCACert != "" && dTLSCert != "" && dTLSKey != "" {
			tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
				CAFile:   dTLSCACert,
				CertFile: dTLSCert,
				KeyFile:  dTLSKey,
			})
			if err != nil {
				log.Fatal(err)
			}

			log.Debug("configuring TLS for KV")
			kvOpts.TLS = tlsConfig
		}

		kv, err := getKVStore(dURL, kvOpts)
		if err != nil {
			log.Fatal(err)
		}

		// get config from kv
		exists, err := kv.Exists(kvConfigKey)
		if err != nil {
			log.Fatal(err)
		}

		if !exists {
			data = defaultConfig
		} else {
			kvPair, err := kv.Get(kvConfigKey)
			if err != nil {
				log.Fatalf("error getting configuration from kv: %s", err)
			}

			data = string(kvPair.Value)

			if data == "" {
				data = defaultConfig
			}
		}
	} else {
		configPath := c.String("config")

		d, err := ioutil.ReadFile(configPath)
		switch {
		case os.IsNotExist(err):
			log.Debug("no config detected; generating local config")
			data = defaultConfig
		case err == nil:
			data = string(d)
		default:
			log.Fatal(err)
		}
	}

	config, err := config.ParseConfig(data)
	if err != nil {
		log.Fatal(err)
	}

	srv, err := server.NewServer(config)
	if err != nil {
		log.Fatal(err)
	}

	if err := srv.Run(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>update default kv key<commit_after>package main

import (
	"io/ioutil"
	"net/url"
	"os"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/docker/docker/pkg/tlsconfig"
	"github.com/docker/libkv"
	kvstore "github.com/docker/libkv/store"
	"github.com/docker/libkv/store/consul"
	"github.com/docker/libkv/store/etcd"
	"github.com/ehazlett/interlock/config"
	"github.com/ehazlett/interlock/server"
	"github.com/ehazlett/interlock/version"
)

const (
	defaultConfig = `ListenAddr = ":8080"
DockerURL = "unix:///var/run/docker.sock"
`
	kvConfigKey = "interlock/v1/config"
)

var cmdRun = cli.Command{
	Name:   "run",
	Action: runAction,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "path to config file",
			Value: "config.toml",
		},
		cli.StringFlag{
			Name:  "discovery, k",
			Usage: "discovery address",
			Value: "",
		},
		cli.StringFlag{
			Name:  "discovery-tls-ca-cert",
			Usage: "discovery tls ca certificate",
			Value: "",
		},
		cli.StringFlag{
			Name:  "discovery-tls-cert",
			Usage: "discovery tls certificate",
			Value: "",
		},
		cli.StringFlag{
			Name:  "discovery-tls-key",
			Usage: "discovery tls key",
			Value: "",
		},
	},
}

func init() {
	consul.Register()
	etcd.Register()
}

func getKVStore(addr string, options *kvstore.Config) (kvstore.Store, error) {
	u, err := url.Parse(addr)
	if err != nil {
		return nil, err

	}

	kvType := strings.ToLower(u.Scheme)
	kvHost := u.Host
	var backend kvstore.Backend

	switch kvType {
	case "consul":
		backend = kvstore.CONSUL
	case "etcd":
		backend = kvstore.ETCD
	}

	kv, err := libkv.NewStore(
		backend,
		[]string{kvHost},
		options,
	)

	if err != nil {
		return nil, err

	}

	return kv, nil
}

func runAction(c *cli.Context) {
	log.Infof("interlock %s", version.FullVersion())

	// init kv
	kvOpts := &kvstore.Config{
		ConnectionTimeout: time.Second * 10,
	}

	dURL := c.String("discovery")
	dTLSCACert := c.String("discovery-tls-ca-cert")
	dTLSCert := c.String("discovery-tls-cert")
	dTLSKey := c.String("discovery-tls-key")

	var data string
	if dURL != "" {
		log.Debugf("using kv: addr=%s", dURL)
		if dTLSCACert != "" && dTLSCert != "" && dTLSKey != "" {
			tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
				CAFile:   dTLSCACert,
				CertFile: dTLSCert,
				KeyFile:  dTLSKey,
			})
			if err != nil {
				log.Fatal(err)
			}

			log.Debug("configuring TLS for KV")
			kvOpts.TLS = tlsConfig
		}

		kv, err := getKVStore(dURL, kvOpts)
		if err != nil {
			log.Fatal(err)
		}

		// get config from kv
		exists, err := kv.Exists(kvConfigKey)
		if err != nil {
			log.Fatal(err)
		}

		if !exists {
			data = defaultConfig
		} else {
			kvPair, err := kv.Get(kvConfigKey)
			if err != nil {
				log.Fatalf("error getting configuration from kv: %s", err)
			}

			data = string(kvPair.Value)

			if data == "" {
				data = defaultConfig
			}
		}
	} else {
		configPath := c.String("config")

		d, err := ioutil.ReadFile(configPath)
		switch {
		case os.IsNotExist(err):
			log.Debug("no config detected; generating local config")
			data = defaultConfig
		case err == nil:
			data = string(d)
		default:
			log.Fatal(err)
		}
	}

	config, err := config.ParseConfig(data)
	if err != nil {
		log.Fatal(err)
	}

	srv, err := server.NewServer(config)
	if err != nil {
		log.Fatal(err)
	}

	if err := srv.Run(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


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

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/ikawaha/kagome"
)

type KagomeHandler struct {
	tokenizer *kagome.Tokenizer
}

func (h *KagomeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	type record struct {
		Id       int      `json:"id"`
		Start    int      `json:"start"`
		End      int      `json:"end"`
		Surface  string   `json:"surface"`
		Class    string   `json:"class"`
		Features []string `json:"features"`
	}

	var body struct {
		Input string `json:"sentence"`
	}
	e := json.NewDecoder(r.Body).Decode(&body)
	if e != nil {
		fmt.Fprintf(w, "{\"status\":false,\"error\":\"%v\"}", e)
		return
	}
	if body.Input == "" {
		fmt.Fprint(w, "{\"status\":true,\"tokens\":[]}")
		return
	}
	tokens := h.tokenizer.Tokenize(body.Input)
	var rsp []record
	for _, tok := range tokens {
		if tok.Id == kagome.BosEosId {
			continue
		}
		fs := tok.Features()
		m := record{
			Id:       tok.Id,
			Class:    fmt.Sprintf("%v", tok.Class),
			Start:    tok.Start,
			End:      tok.End,
			Surface:  tok.Surface,
			Features: fs,
		}
		rsp = append(rsp, m)
	}
	j, e := json.Marshal(struct {
		Status bool     `json:"status"`
		Tokens []record `json:"tokens"`
	}{Status: true, Tokens: rsp})
	if e != nil {
		fmt.Fprintf(w, "{\"status\":false,\"error\":\"%v\"}", e)
		return
	}
	w.Write(j)
}

type KagomeDemoHandler struct {
	tokenizer *kagome.Tokenizer
}

func (h *KagomeDemoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	type record struct {
		Surface        string
		Pos            string
		Baseform       string
		Reading        string
		Pronounciation string
	}
	sen := r.FormValue("s")
	opt := r.FormValue("r")
	var records []record
	var tokens []kagome.Token
	var svg string
	var cmdErr string
	const cmdTimeout = 30 * time.Second
	switch opt {
	case "1": // normal
		tokens = h.tokenizer.Tokenize(sen)
	case "2": // search
		tokens = h.tokenizer.SearchModeTokenize(sen)
	case "3": // extended
		tokens = h.tokenizer.ExtendedModeTokenize(sen)
	case "4": // lattice
		if _, e := exec.LookPath("dot"); e != nil {
			log.Print("graphviz is not in your future\n")
			break
		}
		var buf bytes.Buffer
		cmd := exec.Command("dot", "-Tsvg")
		r, w := io.Pipe()
		cmd.Stdin = r
		cmd.Stdout = &buf
		cmd.Stderr = os.Stderr
		cmd.Start()
		h.tokenizer.Dot(sen, w)
		w.Close()

		done := make(chan error, 1) //XXX
		go func() {
			done <- cmd.Wait()
		}()
		select {
		case <-time.After(cmdTimeout):
			if err := cmd.Process.Kill(); err != nil {
				log.Fatal("failed to kill: ", err)
			}
			cmdErr = "Time out"
			<-done
		case err := <-done:
			if err != nil {
				cmdErr = "Error"
				log.Printf("process done with error = %v", err)
			}
		}
		svg = buf.String()
		if pos := strings.Index(svg, "<svg"); pos > 0 {
			svg = svg[pos:]
		}
	}
	for _, tok := range tokens {
		if tok.Id == kagome.BosEosId {
			continue
		}
		m := record{Surface: tok.Surface}
		fs := tok.Features()
		switch len(fs) {
		case 9:
			m.Pos = strings.Join(fs[0:5], ",")
			m.Baseform = fs[6]
			m.Reading = fs[7]
			m.Pronounciation = fs[8]
		case 7:
			m.Pos = strings.Join(fs[0:5], ",")
			m.Baseform = fs[6]
			m.Reading = "*"
			m.Pronounciation = "*"
		case 3:
			m.Pos = fs[0]
			m.Baseform = fs[1]
			m.Reading = fs[2]
			m.Pronounciation = "*"
		}
		records = append(records, m)
	}
	d := struct {
		Sentence string
		Tokens   []record
		CmdErr   string
		GraphSvg template.HTML
		RadioOpt string
	}{Sentence: sen, Tokens: records, CmdErr: cmdErr, GraphSvg: template.HTML(svg), RadioOpt: opt}
	t := template.Must(template.New("top").Parse(demo_html))
	t.Execute(w, d)
}

var usageMessage = "usage: kagome [-file input_file | --http addr] [-udic userdic_file] [-mode (normal|search|extended)]"

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	flag.PrintDefaults()
	os.Exit(0)
}

var (
	fHttp         = flag.String("http", "", "HTTP service address (e.g., ':6060')")
	fInputFile    = flag.String("file", "", "input file")
	fUserDicFile  = flag.String("udic", "", "user dic")
	fTokenizeMode = flag.String("mode", "normal", "tokenize mode")
)

func Main() {
	if *fHttp != "" && *fInputFile != "" {
		usage()
	}

	var udic *kagome.UserDic
	if *fUserDicFile != "" {
		var err error
		udic, err = kagome.NewUserDic(*fUserDicFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	if *fHttp != "" {
		t := kagome.NewThreadsafeTokenizer()
		if udic != nil {
			t.SetUserDic(udic)
		}
		hTok := &KagomeHandler{tokenizer: t}
		hDem := &KagomeDemoHandler{tokenizer: t}
		mux := http.NewServeMux()
		mux.Handle("/", hTok)
		mux.Handle("/_demo", hDem)
		log.Fatal(http.ListenAndServe(*fHttp, mux))
		os.Exit(0)
	}

	var inputFile = os.Stdin
	if *fInputFile != "" {
		var err error
		inputFile, err = os.Open(*fInputFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		defer inputFile.Close()
	}

	t := kagome.NewTokenizer()
	if udic != nil {
		t.SetUserDic(udic)
	}

	var tokenize = t.Tokenize
	switch {
	case *fTokenizeMode == "normal":
		break
	case *fTokenizeMode == "search":
		tokenize = t.SearchModeTokenize
	case *fTokenizeMode == "extended":
		tokenize = t.ExtendedModeTokenize
	case *fTokenizeMode != "":
		fmt.Fprintf(os.Stderr, "invalid argument: -mode %v\n", *fTokenizeMode)
		usage()
	}

	scanner := bufio.NewScanner(inputFile)
	for scanner.Scan() {
		line := scanner.Text()
		tokens := tokenize(line)
		for i, size := 1, len(tokens); i < size; i++ {
			tok := tokens[i]
			c := tok.Features()
			if tok.Class == kagome.DUMMY {
				fmt.Printf("%s\n", tok.Surface)
			} else {
				fmt.Printf("%s\t%v\n", tok.Surface, strings.Join(c, ","))
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Println(err)
	}
}

func main() {
	flag.Usage = usage
	flag.Parse()
	Main()
}

var demo_html = `
<!DOCTYPE html>

  <meta charset="UTF-8">
  <title>kagome demo - japanese morphological analyzer</title>

Kagome

    Kagome is an open source Japanese morphological analyzer written in Golang

Feature summary

Word segmentation. Segmenting text into words (or morphemes)

Part-of-speech tagging. Assign word-categories (nouns, verbs, particles, adjectives, etc.)

Lemmatization. Get dictionary forms for inflected verbs and adjectives

Readings. Extract readings for kanji.

  <form class="frm" action="/_demo" method="POST">

    <textarea class="txar" rows="3" name="s" placeholder="Enter Japanese text blow in UTF-8 and click tokenize.">{{.Sentence}}</textarea>

<input type="radio" name="r" value="1" checked>Normal

<input type="radio" name="r" value="2" {{if eq .RadioOpt "2"}}checked{{end}}>Search

<input type="radio" name="r" value="3" {{if eq .RadioOpt "3"}}checked{{end}}>Extended

<input type="radio" name="r" value="4" {{if eq .RadioOpt "4"}}checked{{end}}>Lattice

<input class="btn" type="submit" value="Tokenize"/>

  </form>
  {{if .CmdErr}}
    {{.CmdErr}}
  {{end}}
  {{if .GraphSvg}}
    {{.GraphSvg}}
  {{end}}
  {{if .Tokens}}

    <thread>

      Surface
      Part-of-Speech
      Base Form
      Reading
      Pronounciation

</thread>

    {{range .Tokens}}

      {{.Surface}}
      {{.Pos}}
      {{.Baseform}}
      {{.Reading}}
      {{.Pronounciation}}

    {{end}}

  {{end}}

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

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/ikawaha/kagome"
)

type KagomeHandler struct {
	tokenizer *kagome.Tokenizer
}

func (h *KagomeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	type record struct {
		Id       int      `json:"id"`
		Start    int      `json:"start"`
		End      int      `json:"end"`
		Surface  string   `json:"surface"`
		Class    string   `json:"class"`
		Features []string `json:"features"`
	}

	var body struct {
		Input string `json:"sentence"`
	}
	e := json.NewDecoder(r.Body).Decode(&body)
	if e != nil {
		fmt.Fprintf(w, "{\"status\":false,\"error\":\"%v\"}", e)
		return
	}
	if body.Input == "" {
		fmt.Fprint(w, "{\"status\":true,\"tokens\":[]}")
		return
	}
	tokens := h.tokenizer.Tokenize(body.Input)
	var rsp []record
	for _, tok := range tokens {
		if tok.Id == kagome.BosEosId {
			continue
		}
		fs := tok.Features()
		m := record{
			Id:       tok.Id,
			Class:    fmt.Sprintf("%v", tok.Class),
			Start:    tok.Start,
			End:      tok.End,
			Surface:  tok.Surface,
			Features: fs,
		}
		rsp = append(rsp, m)
	}
	j, e := json.Marshal(struct {
		Status bool     `json:"status"`
		Tokens []record `json:"tokens"`
	}{Status: true, Tokens: rsp})
	if e != nil {
		fmt.Fprintf(w, "{\"status\":false,\"error\":\"%v\"}", e)
		return
	}
	w.Write(j)
}

type KagomeDemoHandler struct {
	tokenizer *kagome.Tokenizer
}

func (h *KagomeDemoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	type record struct {
		Surface        string
		Pos            string
		Baseform       string
		Reading        string
		Pronounciation string
	}
	sen := r.FormValue("s")
	opt := r.FormValue("r")
	var records []record
	var tokens []kagome.Token
	var svg string
	var cmdErr string
	const cmdTimeout = 30 * time.Second
	switch opt {
	case "1": // normal
		tokens = h.tokenizer.Tokenize(sen)
	case "2": // search
		tokens = h.tokenizer.SearchModeTokenize(sen)
	case "3": // extended
		tokens = h.tokenizer.ExtendedModeTokenize(sen)
	case "4": // lattice
		if _, e := exec.LookPath("dot"); e != nil {
			log.Print("graphviz is not in your future\n")
			break
		}
		var buf bytes.Buffer
		cmd := exec.Command("dot", "-Tsvg")
		r, w := io.Pipe()
		cmd.Stdin = r
		cmd.Stdout = &buf
		cmd.Stderr = os.Stderr
		cmd.Start()
		h.tokenizer.Dot(sen, w)
		w.Close()

		done := make(chan error, 1) //XXX
		go func() {
			done <- cmd.Wait()
		}()
		select {
		case <-time.After(cmdTimeout):
			if err := cmd.Process.Kill(); err != nil {
				log.Fatal("failed to kill: ", err)
			}
			cmdErr = "Time out"
			<-done
		case err := <-done:
			if err != nil {
				cmdErr = "Error"
				log.Printf("process done with error = %v", err)
			}
		}
		svg = buf.String()
		if pos := strings.Index(svg, "<svg"); pos > 0 {
			svg = svg[pos:]
		}
	}
	for _, tok := range tokens {
		if tok.Id == kagome.BosEosId {
			continue
		}
		m := record{Surface: tok.Surface}
		fs := tok.Features()
		switch len(fs) {
		case 9:
			m.Pos = strings.Join(fs[0:5], ",")
			m.Baseform = fs[6]
			m.Reading = fs[7]
			m.Pronounciation = fs[8]
		case 7:
			m.Pos = strings.Join(fs[0:5], ",")
			m.Baseform = fs[6]
			m.Reading = "*"
			m.Pronounciation = "*"
		case 3:
			m.Pos = fs[0]
			m.Baseform = fs[1]
			m.Reading = fs[2]
			m.Pronounciation = "*"
		}
		records = append(records, m)
	}
	d := struct {
		Sentence string
		Tokens   []record
		CmdErr   string
		GraphSvg template.HTML
		RadioOpt string
	}{Sentence: sen, Tokens: records, CmdErr: cmdErr, GraphSvg: template.HTML(svg), RadioOpt: opt}
	t := template.Must(template.New("top").Parse(demo_html))
	t.Execute(w, d)
}

var usageMessage = "usage: kagome [-file input_file | --http addr] [-udic userdic_file] [-mode (normal|search|extended)]"

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	flag.PrintDefaults()
	os.Exit(0)
}

var (
	fHttp         = flag.String("http", "", "HTTP service address (e.g., ':6060')")
	fInputFile    = flag.String("file", "", "input file")
	fUserDicFile  = flag.String("udic", "", "user dic")
	fTokenizeMode = flag.String("mode", "normal", "tokenize mode")
)

func Main() {
	if *fHttp != "" && *fInputFile != "" {
		usage()
	}

	var udic *kagome.UserDic
	if *fUserDicFile != "" {
		var err error
		udic, err = kagome.NewUserDic(*fUserDicFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	if *fHttp != "" {
		t := kagome.NewTokenizer()
		if udic != nil {
			t.SetUserDic(udic)
		}
		hTok := &KagomeHandler{tokenizer: t}
		hDem := &KagomeDemoHandler{tokenizer: t}
		mux := http.NewServeMux()
		mux.Handle("/", hTok)
		mux.Handle("/_demo", hDem)
		log.Fatal(http.ListenAndServe(*fHttp, mux))
		os.Exit(0)
	}

	var inputFile = os.Stdin
	if *fInputFile != "" {
		var err error
		inputFile, err = os.Open(*fInputFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		defer inputFile.Close()
	}

	t := kagome.NewTokenizer()
	if udic != nil {
		t.SetUserDic(udic)
	}

	var tokenize = t.Tokenize
	switch {
	case *fTokenizeMode == "normal":
		break
	case *fTokenizeMode == "search":
		tokenize = t.SearchModeTokenize
	case *fTokenizeMode == "extended":
		tokenize = t.ExtendedModeTokenize
	case *fTokenizeMode != "":
		fmt.Fprintf(os.Stderr, "invalid argument: -mode %v\n", *fTokenizeMode)
		usage()
	}

	scanner := bufio.NewScanner(inputFile)
	for scanner.Scan() {
		line := scanner.Text()
		tokens := tokenize(line)
		for i, size := 1, len(tokens); i < size; i++ {
			tok := tokens[i]
			c := tok.Features()
			if tok.Class == kagome.DUMMY {
				fmt.Printf("%s\n", tok.Surface)
			} else {
				fmt.Printf("%s\t%v\n", tok.Surface, strings.Join(c, ","))
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Println(err)
	}
}

func main() {
	flag.Usage = usage
	flag.Parse()
	Main()
}

var demo_html = `
<!DOCTYPE html>

  <meta charset="UTF-8">
  <title>kagome demo - japanese morphological analyzer</title>

Kagome

    Kagome is an open source Japanese morphological analyzer written in Golang

Feature summary

Word segmentation. Segmenting text into words (or morphemes)

Part-of-speech tagging. Assign word-categories (nouns, verbs, particles, adjectives, etc.)

Lemmatization. Get dictionary forms for inflected verbs and adjectives

Readings. Extract readings for kanji.

  <form class="frm" action="/_demo" method="POST">

    <textarea class="txar" rows="3" name="s" placeholder="Enter Japanese text blow in UTF-8 and click tokenize.">{{.Sentence}}</textarea>

<input type="radio" name="r" value="1" checked>Normal

<input type="radio" name="r" value="2" {{if eq .RadioOpt "2"}}checked{{end}}>Search

<input type="radio" name="r" value="3" {{if eq .RadioOpt "3"}}checked{{end}}>Extended

<input type="radio" name="r" value="4" {{if eq .RadioOpt "4"}}checked{{end}}>Lattice

<input class="btn" type="submit" value="Tokenize"/>

  </form>
  {{if .CmdErr}}
    {{.CmdErr}}
  {{end}}
  {{if .GraphSvg}}
    {{.GraphSvg}}
  {{end}}
  {{if .Tokens}}

    <thread>

      Surface
      Part-of-Speech
      Base Form
      Reading
      Pronounciation

</thread>

    {{range .Tokens}}

      {{.Surface}}
      {{.Pos}}
      {{.Baseform}}
      {{.Reading}}
      {{.Pronounciation}}

    {{end}}

  {{end}}

`
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/bradfitz/gomemcache/memcache"
	"github.com/ubleipzig/marctools"
)

const (
	version = "1.0.0"
	backoff = 50 * time.Millisecond
)

var errSetFailed = errors.New("cache set failed")

type work struct {
	blob []byte
	id   string
}

type options struct {
	hostport string
	key      string
	retry    uint
	verbose  bool
}

func worker(queue chan []work, opts options, wg *sync.WaitGroup) {
	defer wg.Done()
	mc := memcache.New(opts.hostport)
	for batch := range queue {
		for _, work := range batch {
			ok := false
			var i uint

			for i = 1; i <= opts.retry; i++ {
				err := mc.Set(&memcache.Item{Key: work.id, Value: work.blob})
				if err != nil {
					pause := 2 << i * backoff
					if opts.verbose {
						log.Printf("retry %d for %s in %s ...", i, work.id, pause)
					}
					time.Sleep(pause)
				} else {
					ok = true
					break
				}
			}
			if !ok {
				log.Fatal(errSetFailed)
			}
		}
	}
}

func main() {

	hostport := flag.String("addr", "127.0.0.1:11211", "hostport of memcache")
	key := flag.String("key", "id", "key to use")
	retry := flag.Int("retry", 10, "retry set operation this many times")
	numWorker := flag.Int("w", runtime.NumCPU(), "number of workers")
	size := flag.Int("b", 10000, "batch size")
	verbose := flag.Bool("verbose", false, "be verbose")
	showVersion := flag.Bool("v", false, "prints current program version")

	flag.Parse()

	runtime.GOMAXPROCS(*numWorker)

	if *showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	if flag.NArg() < 1 {
		log.Fatal("input file required")
	}

	file, err := os.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}

	opts := options{
		hostport: *hostport,
		key:      *key,
		retry:    uint(*retry),
		verbose:  *verbose,
	}

	queue := make(chan []work)
	var wg sync.WaitGroup

	for i := 0; i < *numWorker; i++ {
		wg.Add(1)
		go worker(queue, opts, &wg)
	}

	var batch []work
	var offset int64
	var i int
	ids := marctools.IDList(file.Name())

	for {
		length, err := marctools.RecordLength(file)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}

		file.Seek(offset, 0)
		buf := make([]byte, length)
		_, err = file.Read(buf)

		batch = append(batch, work{id: ids[i], blob: buf})

		if i%*size == 0 {
			queue <- batch
			batch = batch[:0]
		}

		offset = offset + length
		i++
	}

	queue <- batch
	close(queue)
	wg.Wait()
}
<commit_msg>add support for multiple files<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sync"
	"time"

	"github.com/bradfitz/gomemcache/memcache"
	"github.com/ubleipzig/marctools"
)

const (
	version = "1.0.0"
	backoff = 50 * time.Millisecond
)

var errSetFailed = errors.New("cache set failed")

type work struct {
	blob []byte
	id   string
}

type options struct {
	hostport string
	key      string
	retry    uint
	verbose  bool
}

func worker(queue chan []work, opts options, wg *sync.WaitGroup) {
	defer wg.Done()
	mc := memcache.New(opts.hostport)
	for batch := range queue {
		for _, work := range batch {
			ok := false
			var i uint

			for i = 1; i <= opts.retry; i++ {
				err := mc.Set(&memcache.Item{Key: work.id, Value: work.blob})
				if err != nil {
					pause := 2 << i * backoff
					if opts.verbose {
						log.Printf("retry %d for %s in %s ...", i, work.id, pause)
					}
					time.Sleep(pause)
				} else {
					ok = true
					break
				}
			}
			if !ok {
				log.Fatal(errSetFailed)
			}
		}
	}
}

func main() {

	hostport := flag.String("addr", "127.0.0.1:11211", "hostport of memcache")
	key := flag.String("key", "id", "key to use")
	retry := flag.Int("retry", 10, "retry set operation this many times")
	numWorker := flag.Int("w", runtime.NumCPU(), "number of workers")
	size := flag.Int("b", 10000, "batch size")
	verbose := flag.Bool("verbose", false, "be verbose")
	showVersion := flag.Bool("v", false, "prints current program version")

	flag.Parse()

	runtime.GOMAXPROCS(*numWorker)

	if *showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	if flag.NArg() < 1 {
		log.Fatal("input file or files required")
	}

	opts := options{
		hostport: *hostport,
		key:      *key,
		retry:    uint(*retry),
		verbose:  *verbose,
	}

	queue := make(chan []work)
	var wg sync.WaitGroup

	for i := 0; i < *numWorker; i++ {
		wg.Add(1)
		go worker(queue, opts, &wg)
	}

	var batch []work

	for _, filename := range flag.Args() {

		var offset int64
		var i int

		file, err := os.Open(filename)
		if err != nil {
			log.Fatal(err)
		}

		ids := marctools.IDList(file.Name())

		for {
			length, err := marctools.RecordLength(file)
			if err == io.EOF {
				break
			}
			if err != nil {
				log.Fatal(err)
			}

			file.Seek(offset, 0)
			buf := make([]byte, length)
			_, err = file.Read(buf)
			if err != nil {
				log.Fatal(err)
			}

			batch = append(batch, work{id: ids[i], blob: buf})

			if i%*size == 0 {
				queue <- batch
				batch = batch[:0]
			}

			offset = offset + length
			i++
		}
	}

	queue <- batch
	close(queue)
	wg.Wait()

}
<|endoftext|>


<commit_before>package shared

import (
	"bytes"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/lestrrat/go-jwx/jwa"
	"github.com/lestrrat/go-jwx/jwt"
	"github.com/spf13/viper"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path"
	"strconv"
	"strings"
	"time"
)

const BaseURL = "https://apigee.googleapis.com/v1/organizations/"

// Arguements is the base struct to hold all command arguments
type Arguments struct {
	Verbose        bool
	Org            string
	Env            string
	Token          string
	ServiceAccount string
}

var RootArgs = Arguments{}

//log levels, default is error
var (
	Info    *log.Logger
	Warning *log.Logger
	Error   *log.Logger
)

var LogInfo = false
var skipCheck = false
var skipCache = false

// Structure to hold OAuth response
type OAuthAccessToken struct {
	AccessToken string `json:"access_token,omitempty"`
	ExpiresIn   int    `json:"expires_in,omitempty"`
	TokenType   string `json:"token_type,omitempty"`
}

const access_token_file = ".access_token"

//Init function initializes the logger objects
func Init() {

	var infoHandle = ioutil.Discard

	if LogInfo {
		infoHandle = os.Stdout
	}

	warningHandle := os.Stdout
	errorHandle := os.Stdout

	Info = log.New(infoHandle,
		"INFO: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Warning = log.New(warningHandle,
		"WARNING: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Error = log.New(errorHandle,
		"ERROR: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

func PostHttpOctet(url string, proxyName string) error {

	file, _ := os.Open(proxyName)
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("proxy", proxyName)
	if err != nil {
		Error.Fatalln("Error writing multi-part:\n", err)
		return err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		Error.Fatalln("Error copying multi-part:\n", err)
		return err
	}

	err = writer.Close()
	if err != nil {
		Error.Fatalln("Error closing multi-part:\n", err)
		return err
	}
	client := &http.Client{}

	Info.Println("Connecting to : ", url)
	req, err := http.NewRequest("POST", url, body)

	Info.Println("Setting token : ", RootArgs.Token)
	req.Header.Add("Authorization", "Bearer "+RootArgs.Token)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Error connecting:\n", err)
		return err
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			Error.Fatalln("Error in response:\n", err)
			return err
		} else if resp.StatusCode != 200 {
			Error.Fatalln("Error in response:\n", string(body))
			return errors.New("Error in response")
		} else {
			var prettyJSON bytes.Buffer
			err = json.Indent(&prettyJSON, body, "", "\t")
			if err != nil {
				Error.Fatalln("Error parsing response:\n", err)
				return err
			}
			fmt.Println(string(prettyJSON.Bytes()))
			return nil
		}
	}
}

func DownloadResource(url string, name string) error {

	out, err := os.Create(name + ".zip")
	if err != nil {
		Error.Fatalln("Error creating file:\n", err)
		return err
	}
	defer out.Close()

	client := &http.Client{}

	Info.Println("Connecting to : ", url)
	req, err := http.NewRequest("GET", url, nil)

	Info.Println("Setting token : ", RootArgs.Token)
	req.Header.Add("Authorization", "Bearer "+RootArgs.Token)

	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Error connecting:\n", err)
		return err
	} else if resp.StatusCode > 299 {
		Error.Fatalln("Response Code:\n", resp.StatusCode)
		Error.Fatalln("Error in response:\n", resp.Body)
		return errors.New("Error in response")
	} else {
		defer resp.Body.Close()
		_, err = io.Copy(out, resp.Body)
		if err != nil {
			Error.Fatalln("Error writing response to file:\n", err)
			return err
		}

		fmt.Println("Proxy bundle " + name + ".zip completed")
		return nil
	}
}

// The first parameter is url. If only one parameter is sent, assume GET
// The second parameter is the payload. The two parameters are sent, assume POST
// THe third parammeter is the method. If three parameters are sent, assume method in param
func HttpClient(params ...string) error {

	var req *http.Request
	var err error

	client := &http.Client{}
	Info.Println("Connecting to : ", params[0])

	if len(params) == 1 {
		req, err = http.NewRequest("GET", params[0], nil)
	} else if len(params) == 2 {
		req, err = http.NewRequest("POST", params[0], bytes.NewBuffer([]byte(params[1])))
	} else if len(params) == 3 {
		if params[2] == "DELETE" {
			req, err = http.NewRequest("DELETE", params[0], nil)
		} else if params[2] == "PUT" {
			req, err = http.NewRequest("PUT", params[0], bytes.NewBuffer([]byte(params[1])))
		} else {
			return errors.New("Unsupported method")
		}
	} else {
		return errors.New("Incorrect parameters to invoke the method")
	}

	Info.Println("Setting token : ", RootArgs.Token)
	req.Header.Add("Authorization", "Bearer "+RootArgs.Token)

	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Error connecting:\n", err)
		return err
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			Error.Fatalln("Error in response:\n", err)
			return err
		} else if resp.StatusCode > 299 {
			Error.Fatalln("Response Code:\n", resp.StatusCode)
			Error.Fatalln("Error in response:\n", string(body))
			return errors.New("Error in response")
		} else {
			var prettyJSON bytes.Buffer
			err = json.Indent(&prettyJSON, body, "", "\t")
			if err != nil {
				Error.Fatalln("Error parsing response:\n", err)
				return err
			}
			fmt.Println(string(prettyJSON.Bytes()))
			return nil
		}
	}
}

func getPrivateKey() (interface{}, error) {
	pemPrivateKey := fmt.Sprintf("%v", viper.Get("private_key"))
	block, _ := pem.Decode([]byte(pemPrivateKey))
	privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		Error.Fatalln("Error parsing Private Key:\n", err)
		return nil, err
	} else {
		return privKey, nil
	}
}

func generateJWT() (string, error) {

	const aud = "https://www.googleapis.com/oauth2/v4/token"
	const scope = "https://www.googleapis.com/auth/cloud-platform"

	privKey, err := getPrivateKey()

	if err != nil {
		return "", err
	}

	now := time.Now()
	token := jwt.New()

	token.Set(jwt.AudienceKey, aud)
	token.Set(jwt.IssuerKey, viper.Get("client_email"))
	token.Set("scope", scope)
	token.Set(jwt.IssuedAtKey, now.Unix())
	token.Set(jwt.ExpirationKey, now.Unix())

	payload, err := token.Sign(jwa.RS256, privKey)
	if err != nil {
		Error.Fatalln("Error parsing Private Key:\n", err)
		return "", err
	} else {
		Info.Println("jwt token : ", string(payload))
		return string(payload), nil
	}
}

func GenerateAccessToken() (string, error) {

	const token_endpoint = "https://www.googleapis.com/oauth2/v4/token"
	const grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"

	token, err := generateJWT()

	if err != nil {
		return "", nil
	}

	form := url.Values{}
	form.Add("grant_type", grant_type)
	form.Add("assertion", token)

	client := &http.Client{}
	req, err := http.NewRequest("POST", token_endpoint, strings.NewReader(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))

	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Failed to generate oauth token: \n", err)
		return "", err
	} else {
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			bodyBytes, _ := ioutil.ReadAll(resp.Body)
			Error.Fatalln("Error in response: \n", string(bodyBytes))
			return "", errors.New("Error in response")
		} else {
			decoder := json.NewDecoder(resp.Body)
			accessToken := OAuthAccessToken{}
			if err := decoder.Decode(&accessToken); err != nil {
				Error.Fatalln("Error in response: \n", err)
				return "", errors.New("Error in response")
			} else {
				Info.Println("access token : ", accessToken)
				RootArgs.Token = accessToken.AccessToken
				writeAccessToken()
				return accessToken.AccessToken, nil
			}
		}
	}
}

func readAccessToken() error {
	usr, err := user.Current()
	if err != nil {
		return err
	}
	content, err := ioutil.ReadFile(path.Join(usr.HomeDir, access_token_file))
	if err != nil {
		Info.Println("Cached access token was not found")
		return err
	} else {
		Info.Println("Using cached access token: ", string(content))
		RootArgs.Token = string(content)
		return nil
	}
}

func writeAccessToken() error {

	if skipCache {
		return nil
	}
	usr, err := user.Current()
	if err != nil {
		Warning.Println(err)
	} else {
		Info.Println("Cache access token: ", RootArgs.Token)
		err = ioutil.WriteFile(path.Join(usr.HomeDir, access_token_file), []byte(RootArgs.Token), 0644)
	}
	return err
}

func checkAccessToken() bool {

	if skipCheck {
		Warning.Println("skipping token validity")
		return true
	}

	const tokenInfo = "https://www.googleapis.com/oauth2/v1/tokeninfo"
	u, _ := url.Parse(tokenInfo)
	q := u.Query()
	q.Set("access_token", RootArgs.Token)
	u.RawQuery = q.Encode()

	client := &http.Client{}

	Info.Println("Connecting to : ", u.String())
	req, err := http.NewRequest("GET", u.String(), nil)
	resp, err := client.Do(req)
	if err != nil {
		Error.Fatalln("Error connecting to token endpoint:\n", err)
		return false
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			Error.Fatalln("Token info error:\n", err)
			return false
		} else if resp.StatusCode != 200 {
			Error.Fatalln("Token expired:\n", string(body))
			return false
		} else {
			Info.Println("Response: ", string(body))
			Info.Println("Reusing the cached token: ", RootArgs.Token)
			return true
		}
	}
}

func SetAccessToken() error {

	if RootArgs.Token == "" && RootArgs.ServiceAccount == "" {
		err := readAccessToken() //try to read from config
		if err != nil {
			return fmt.Errorf("Either token or service account must be provided")
		} else {
			if checkAccessToken() { //check if the token is still valid
				return nil
			} else {
				return fmt.Errorf("Token expired: request a new access token or pass the service account")
			}
		}
	} else {
		if RootArgs.ServiceAccount != "" {
			viper.SetConfigFile(RootArgs.ServiceAccount)
			err := viper.ReadInConfig() // Find and read the config file
			if err != nil {             // Handle errors reading the config file
				return fmt.Errorf("Fatal error config file: %s \n", err)
			} else {
				if viper.Get("private_key") == "" {
					return fmt.Errorf("Fatal error: Private key missing in the service account")
				}
				if viper.Get("client_email") == "" {
					return fmt.Errorf("Fatal error: client email missing in the service account")
				}
				_, err = GenerateAccessToken()
				if err != nil {
					return fmt.Errorf("Fatal error generating access token: %s \n", err)
				} else {
					return nil
				}
			}
		} else {
			//a token was passed, cache it
			if checkAccessToken() {
				writeAccessToken()
				return nil
			} else {
				return fmt.Errorf("Token expired: request a new access token or pass the service account")
			}
		}
	}
}
<commit_msg>skip check<commit_after>package shared

import (
	"bytes"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/lestrrat/go-jwx/jwa"
	"github.com/lestrrat/go-jwx/jwt"
	"github.com/spf13/viper"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path"
	"strconv"
	"strings"
	"time"
)

const BaseURL = "https://apigee.googleapis.com/v1/organizations/"

// Arguements is the base struct to hold all command arguments
type Arguments struct {
	Verbose        bool
	Org            string
	Env            string
	Token          string
	ServiceAccount string
}

var RootArgs = Arguments{}

//log levels, default is error
var (
	Info    *log.Logger
	Warning *log.Logger
	Error   *log.Logger
)

var LogInfo = false
var skipCheck = false
var skipCache = true

// Structure to hold OAuth response
type OAuthAccessToken struct {
	AccessToken string `json:"access_token,omitempty"`
	ExpiresIn   int    `json:"expires_in,omitempty"`
	TokenType   string `json:"token_type,omitempty"`
}

const access_token_file = ".access_token"

//Init function initializes the logger objects
func Init() {

	var infoHandle = ioutil.Discard

	if LogInfo {
		infoHandle = os.Stdout
	}

	warningHandle := os.Stdout
	errorHandle := os.Stdout

	Info = log.New(infoHandle,
		"INFO: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Warning = log.New(warningHandle,
		"WARNING: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Error = log.New(errorHandle,
		"ERROR: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

func PostHttpOctet(url string, proxyName string) error {

	file, _ := os.Open(proxyName)
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("proxy", proxyName)
	if err != nil {
		Error.Fatalln("Error writing multi-part:\n", err)
		return err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		Error.Fatalln("Error copying multi-part:\n", err)
		return err
	}

	err = writer.Close()
	if err != nil {
		Error.Fatalln("Error closing multi-part:\n", err)
		return err
	}
	client := &http.Client{}

	Info.Println("Connecting to : ", url)
	req, err := http.NewRequest("POST", url, body)

	Info.Println("Setting token : ", RootArgs.Token)
	req.Header.Add("Authorization", "Bearer "+RootArgs.Token)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Error connecting:\n", err)
		return err
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			Error.Fatalln("Error in response:\n", err)
			return err
		} else if resp.StatusCode != 200 {
			Error.Fatalln("Error in response:\n", string(body))
			return errors.New("Error in response")
		} else {
			var prettyJSON bytes.Buffer
			err = json.Indent(&prettyJSON, body, "", "\t")
			if err != nil {
				Error.Fatalln("Error parsing response:\n", err)
				return err
			}
			fmt.Println(string(prettyJSON.Bytes()))
			return nil
		}
	}
}

func DownloadResource(url string, name string) error {

	out, err := os.Create(name + ".zip")
	if err != nil {
		Error.Fatalln("Error creating file:\n", err)
		return err
	}
	defer out.Close()

	client := &http.Client{}

	Info.Println("Connecting to : ", url)
	req, err := http.NewRequest("GET", url, nil)

	Info.Println("Setting token : ", RootArgs.Token)
	req.Header.Add("Authorization", "Bearer "+RootArgs.Token)

	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Error connecting:\n", err)
		return err
	} else if resp.StatusCode > 299 {
		Error.Fatalln("Response Code:\n", resp.StatusCode)
		Error.Fatalln("Error in response:\n", resp.Body)
		return errors.New("Error in response")
	} else {
		defer resp.Body.Close()
		_, err = io.Copy(out, resp.Body)
		if err != nil {
			Error.Fatalln("Error writing response to file:\n", err)
			return err
		}

		fmt.Println("Proxy bundle " + name + ".zip completed")
		return nil
	}
}

// The first parameter is url. If only one parameter is sent, assume GET
// The second parameter is the payload. The two parameters are sent, assume POST
// THe third parammeter is the method. If three parameters are sent, assume method in param
func HttpClient(params ...string) error {

	var req *http.Request
	var err error

	client := &http.Client{}
	Info.Println("Connecting to : ", params[0])

	if len(params) == 1 {
		req, err = http.NewRequest("GET", params[0], nil)
	} else if len(params) == 2 {
		req, err = http.NewRequest("POST", params[0], bytes.NewBuffer([]byte(params[1])))
	} else if len(params) == 3 {
		if params[2] == "DELETE" {
			req, err = http.NewRequest("DELETE", params[0], nil)
		} else if params[2] == "PUT" {
			req, err = http.NewRequest("PUT", params[0], bytes.NewBuffer([]byte(params[1])))
		} else {
			return errors.New("Unsupported method")
		}
	} else {
		return errors.New("Incorrect parameters to invoke the method")
	}

	Info.Println("Setting token : ", RootArgs.Token)
	req.Header.Add("Authorization", "Bearer "+RootArgs.Token)

	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Error connecting:\n", err)
		return err
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			Error.Fatalln("Error in response:\n", err)
			return err
		} else if resp.StatusCode > 299 {
			Error.Fatalln("Response Code:\n", resp.StatusCode)
			Error.Fatalln("Error in response:\n", string(body))
			return errors.New("Error in response")
		} else {
			var prettyJSON bytes.Buffer
			err = json.Indent(&prettyJSON, body, "", "\t")
			if err != nil {
				Error.Fatalln("Error parsing response:\n", err)
				return err
			}
			fmt.Println(string(prettyJSON.Bytes()))
			return nil
		}
	}
}

func getPrivateKey() (interface{}, error) {
	pemPrivateKey := fmt.Sprintf("%v", viper.Get("private_key"))
	block, _ := pem.Decode([]byte(pemPrivateKey))
	privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		Error.Fatalln("Error parsing Private Key:\n", err)
		return nil, err
	} else {
		return privKey, nil
	}
}

func generateJWT() (string, error) {

	const aud = "https://www.googleapis.com/oauth2/v4/token"
	const scope = "https://www.googleapis.com/auth/cloud-platform"

	privKey, err := getPrivateKey()

	if err != nil {
		return "", err
	}

	now := time.Now()
	token := jwt.New()

	token.Set(jwt.AudienceKey, aud)
	token.Set(jwt.IssuerKey, viper.Get("client_email"))
	token.Set("scope", scope)
	token.Set(jwt.IssuedAtKey, now.Unix())
	token.Set(jwt.ExpirationKey, now.Unix())

	payload, err := token.Sign(jwa.RS256, privKey)
	if err != nil {
		Error.Fatalln("Error parsing Private Key:\n", err)
		return "", err
	} else {
		Info.Println("jwt token : ", string(payload))
		return string(payload), nil
	}
}

func GenerateAccessToken() (string, error) {

	const token_endpoint = "https://www.googleapis.com/oauth2/v4/token"
	const grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"

	token, err := generateJWT()

	if err != nil {
		return "", nil
	}

	form := url.Values{}
	form.Add("grant_type", grant_type)
	form.Add("assertion", token)

	client := &http.Client{}
	req, err := http.NewRequest("POST", token_endpoint, strings.NewReader(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))

	resp, err := client.Do(req)

	if err != nil {
		Error.Fatalln("Failed to generate oauth token: \n", err)
		return "", err
	} else {
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			bodyBytes, _ := ioutil.ReadAll(resp.Body)
			Error.Fatalln("Error in response: \n", string(bodyBytes))
			return "", errors.New("Error in response")
		} else {
			decoder := json.NewDecoder(resp.Body)
			accessToken := OAuthAccessToken{}
			if err := decoder.Decode(&accessToken); err != nil {
				Error.Fatalln("Error in response: \n", err)
				return "", errors.New("Error in response")
			} else {
				Info.Println("access token : ", accessToken)
				RootArgs.Token = accessToken.AccessToken
				writeAccessToken()
				return accessToken.AccessToken, nil
			}
		}
	}
}

func readAccessToken() error {
	usr, err := user.Current()
	if err != nil {
		return err
	}
	content, err := ioutil.ReadFile(path.Join(usr.HomeDir, access_token_file))
	if err != nil {
		Info.Println("Cached access token was not found")
		return err
	} else {
		Info.Println("Using cached access token: ", string(content))
		RootArgs.Token = string(content)
		return nil
	}
}

func writeAccessToken() error {

	if skipCache {
		return nil
	}
	usr, err := user.Current()
	if err != nil {
		Warning.Println(err)
	} else {
		Info.Println("Cache access token: ", RootArgs.Token)
		err = ioutil.WriteFile(path.Join(usr.HomeDir, access_token_file), []byte(RootArgs.Token), 0644)
	}
	return err
}

func checkAccessToken() bool {

	if skipCheck {
		Warning.Println("skipping token validity")
		return true
	}

	const tokenInfo = "https://www.googleapis.com/oauth2/v1/tokeninfo"
	u, _ := url.Parse(tokenInfo)
	q := u.Query()
	q.Set("access_token", RootArgs.Token)
	u.RawQuery = q.Encode()

	client := &http.Client{}

	Info.Println("Connecting to : ", u.String())
	req, err := http.NewRequest("GET", u.String(), nil)
	resp, err := client.Do(req)
	if err != nil {
		Error.Fatalln("Error connecting to token endpoint:\n", err)
		return false
	} else {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			Error.Fatalln("Token info error:\n", err)
			return false
		} else if resp.StatusCode != 200 {
			Error.Fatalln("Token expired:\n", string(body))
			return false
		} else {
			Info.Println("Response: ", string(body))
			Info.Println("Reusing the cached token: ", RootArgs.Token)
			return true
		}
	}
}

func SetAccessToken() error {

	if RootArgs.Token == "" && RootArgs.ServiceAccount == "" {
		err := readAccessToken() //try to read from config
		if err != nil {
			return fmt.Errorf("Either token or service account must be provided")
		} else {
			if checkAccessToken() { //check if the token is still valid
				return nil
			} else {
				return fmt.Errorf("Token expired: request a new access token or pass the service account")
			}
		}
	} else {
		if RootArgs.ServiceAccount != "" {
			viper.SetConfigFile(RootArgs.ServiceAccount)
			err := viper.ReadInConfig() // Find and read the config file
			if err != nil {             // Handle errors reading the config file
				return fmt.Errorf("Fatal error config file: %s \n", err)
			} else {
				if viper.Get("private_key") == "" {
					return fmt.Errorf("Fatal error: Private key missing in the service account")
				}
				if viper.Get("client_email") == "" {
					return fmt.Errorf("Fatal error: client email missing in the service account")
				}
				_, err = GenerateAccessToken()
				if err != nil {
					return fmt.Errorf("Fatal error generating access token: %s \n", err)
				} else {
					return nil
				}
			}
		} else {
			//a token was passed, cache it
			if checkAccessToken() {
				writeAccessToken()
				return nil
			} else {
				return fmt.Errorf("Token expired: request a new access token or pass the service account")
			}
		}
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"time"

	"github.com/spf13/cobra"
	"github.com/stormforger/cli/api/testrun"
)

var (
	// testRunWatchCmd represents the test run watch command
	testRunWatchCmd = &cobra.Command{
		Use:   "watch <test-run-id>",
		Short: "Wait and watch for a active test run",
		Long: `Wait and watch for a active test run

watch will continue to look for the active test run until it reaches
a final state (like "done" or "aborted").

It will exit with 0 on success; 1 on test run errors (like "aborted")
and 2 if the given timeout was exceeded.`,
		Run: testRunWatch,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if len(args) != 1 {
				log.Fatal("Expect exactly one argument: test run reference!")
			}
		},
	}

	testRunWatchOpts struct {
		MaxWatchTime time.Duration
	}
)

var successStates = []string{
	"launching",
	"deploying",
	"starting",
	"running",
	"fetching_logs",
	"log_fetched",
	"analysing",
	"done",
}

func init() {
	TestRunCmd.AddCommand(testRunWatchCmd)

	testRunWatchCmd.Flags().DurationVar(&testRunWatchOpts.MaxWatchTime, "timeout", 0, "Maximum duration in seconds to watch")
}

func testRunWatch(cmd *cobra.Command, args []string) {
	client := NewClient()

	testRunUID := getTestRunUID(*client, args[0])

	watchTestRun(testRunUID, testRunWatchOpts.MaxWatchTime.Round(time.Second).Seconds(), rootOpts.OutputFormat)

	result := fetchTestRun(*client, testRunUID)

	if rootOpts.OutputFormat == "json" {
		fmt.Println(string(result))
	}
}

func testRunOkay(testRun *testrun.TestRun) bool {
	return stringInSlice(testRun.State, successStates)
}

func testRunSuccess(testRun *testrun.TestRun) bool {
	successStates := []string{
		"done",
	}

	return stringInSlice(testRun.State, successStates)
}
<commit_msg>feat: treat finished as a successful test-run outcome and do not fail the cli<commit_after>package cmd

import (
	"fmt"
	"log"
	"time"

	"github.com/spf13/cobra"
	"github.com/stormforger/cli/api/testrun"
)

var (
	// testRunWatchCmd represents the test run watch command
	testRunWatchCmd = &cobra.Command{
		Use:   "watch <test-run-id>",
		Short: "Wait and watch for a active test run",
		Long: `Wait and watch for a active test run

watch will continue to look for the active test run until it reaches
a final state (like "done" or "aborted").

It will exit with 0 on success; 1 on test run errors (like "aborted")
and 2 if the given timeout was exceeded.`,
		Run: testRunWatch,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if len(args) != 1 {
				log.Fatal("Expect exactly one argument: test run reference!")
			}
		},
	}

	testRunWatchOpts struct {
		MaxWatchTime time.Duration
	}
)

var successStates = []string{
	"analysing",
	"deploying",
	"done",
	"fetching_logs",
	"finished",
	"launching",
	"log_fetched",
	"running",
	"starting",
}

func init() {
	TestRunCmd.AddCommand(testRunWatchCmd)

	testRunWatchCmd.Flags().DurationVar(&testRunWatchOpts.MaxWatchTime, "timeout", 0, "Maximum duration in seconds to watch")
}

func testRunWatch(cmd *cobra.Command, args []string) {
	client := NewClient()

	testRunUID := getTestRunUID(*client, args[0])

	watchTestRun(testRunUID, testRunWatchOpts.MaxWatchTime.Round(time.Second).Seconds(), rootOpts.OutputFormat)

	result := fetchTestRun(*client, testRunUID)

	if rootOpts.OutputFormat == "json" {
		fmt.Println(string(result))
	}
}

func testRunOkay(testRun *testrun.TestRun) bool {
	return stringInSlice(testRun.State, successStates)
}

func testRunSuccess(testRun *testrun.TestRun) bool {
	successStates := []string{
		"done",
	}

	return stringInSlice(testRun.State, successStates)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !windows
// +build !openbsd

package main

import (
	_ "expvar"
	"flag"
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"upspin.io/cmd/cacheserver/cacheutil"
	"upspin.io/config"
	"upspin.io/flags"
	"upspin.io/log"
	"upspin.io/rpc/local"

	_ "upspin.io/pack/ee"
	_ "upspin.io/pack/eeintegrity"
	_ "upspin.io/pack/plain"

	"upspin.io/transports"
)

const cmdName = "upspinfs"

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s <mountpoint>\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flags.Parse(flags.Server, "cachedir")

	if flag.NArg() != 1 {
		usage()
		os.Exit(2)
	}

	// Normal setup, get configuration from file and push user cache onto config.
	cfg, err := config.FromFile(flags.Config)
	if err != nil {
		log.Debug.Fatal(err)
	}

	// Set any flags contained in the config.
	if err := config.SetFlagValues(cfg, cmdName); err != nil {
		log.Fatalf("%s: %s", cmdName, err)
	}

	transports.Init(cfg)

	// Start the cache if needed.
	cacheutil.Start(cfg)

	// Mount the file system and start serving.
	mountpoint, err := filepath.Abs(flag.Arg(0))
	if err != nil {
		log.Fatalf("can't determine absolute path to mount point %s: %s", flag.Arg(0), err)
	}
	done := do(cfg, mountpoint, flags.CacheDir)

	// Serve expvar data.
	ln, err := local.Listen("tcp", local.LocalName(cfg, cmdName))
	if err != nil {
		log.Fatal(err)
	}
	srv := &http.Server{}
	go func() {
		log.Fatal(srv.Serve(ln))
	}()

	// Wait for an unmount.
	<-done
	srv.Close()
}
<commit_msg>cmd/upspinfs: allow -prudent<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !windows
// +build !openbsd

package main

import (
	_ "expvar"
	"flag"
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"upspin.io/cmd/cacheserver/cacheutil"
	"upspin.io/config"
	"upspin.io/flags"
	"upspin.io/log"
	"upspin.io/rpc/local"

	_ "upspin.io/pack/ee"
	_ "upspin.io/pack/eeintegrity"
	_ "upspin.io/pack/plain"

	"upspin.io/transports"
)

const cmdName = "upspinfs"

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s <mountpoint>\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flags.Parse(flags.Server, "cachedir", "prudent")

	if flag.NArg() != 1 {
		usage()
		os.Exit(2)
	}

	// Normal setup, get configuration from file and push user cache onto config.
	cfg, err := config.FromFile(flags.Config)
	if err != nil {
		log.Debug.Fatal(err)
	}

	// Set any flags contained in the config.
	if err := config.SetFlagValues(cfg, cmdName); err != nil {
		log.Fatalf("%s: %s", cmdName, err)
	}

	transports.Init(cfg)

	// Start the cache if needed.
	cacheutil.Start(cfg)

	// Mount the file system and start serving.
	mountpoint, err := filepath.Abs(flag.Arg(0))
	if err != nil {
		log.Fatalf("can't determine absolute path to mount point %s: %s", flag.Arg(0), err)
	}
	done := do(cfg, mountpoint, flags.CacheDir)

	// Serve expvar data.
	ln, err := local.Listen("tcp", local.LocalName(cfg, cmdName))
	if err != nil {
		log.Fatal(err)
	}
	srv := &http.Server{}
	go func() {
		log.Fatal(srv.Serve(ln))
	}()

	// Wait for an unmount.
	<-done
	srv.Close()
}
<|endoftext|>


<commit_before>// Copyright 2019 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Upsync keeps a local disk copy in sync with a master version in Upspin.
// See the command's usage method for documentation.
package main

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

	"upspin.io/client"
	"upspin.io/cmd/cacheserver/cacheutil"
	"upspin.io/config"
	"upspin.io/flags"
	"upspin.io/transports"
	"upspin.io/upspin"
	"upspin.io/version"
)

var lastUpsync int64 // Unix time when an upsync was last completed

const help = `Upsync keeps a local disk copy in sync with a master version in
Upspin. It is a weak substitute for upspinfs.

To start, create a local directory whose path ends in a string that looks like
an existing upspin directory, such as ~/u/alice@example.com. Cd there and execute
upsync.  Make local edits to the downloaded files or create new files, and then
upsync to upload your changes to the Upspin master. To discard your local changes,
just remove the edited local files and upsync. (Executing both local rm and
upspin rm are required to remove content permanently.)

Upsync prints which files it is uploading or downloading and declines to download
files larger than 50MB. It promises never to write outside the starting directory
and subdirectories and, as an initial way to enforce that, declines all symlinks.

There are no clever merge heuristics;  copying back and forth proceeds by a trivial
"newest wins" rule.  This requires some discipline in remembering to upsync after
each editing session and is better suited to single person rather than joint
editing. Don't let your computer clocks drift.

With better FUSE support on Windows and OpenBSD it will be possible to switch
to the much preferable upspinfs. But even then upsync may have benefits:
* enables work offline, i.e. a workaround for (missing) distributed upspinfs
* offers mitigation of user misfortune, such as losing upspin keys
* provides a worked out example for new Upspin client developers
* leaves a backup in case cloud store or Upspin projects die without warning

This tool was written assuming you are an experienced Upspin user trying to
assist a friend with file sharing or backup on Windows 10.  Here is a checklist:
1. create or check existing upspin account and permissions
   It is helpful if you can provide them space on an existing server.
2. confirm \Users\alice\upspin\config is correct
3. disk must be NTFS (because FAT has peculiar timestamps)
4. open a powershell window
5. install go and git, if not already there
6. go get -u upspin.io/cmd/...
7. fetch upsync.go; go install
   Go files must be transferred as UTF8, else expect a NUL compile warning.
8. mkdir \Users\alice\u\alice@example.com
9. upsync

`

const cmdName = "upsync"

var upsyncFlag = flag.String("upsync", upspinDir("upsync"), "file whose mtime is last upsync")

func usage() {
	fmt.Fprintln(os.Stderr, help)
	fmt.Fprintf(os.Stderr, "Usage: %s [flags]\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("upsync: ")
	flag.Usage = usage
	flags.Parse(flags.Client, "version")
	if flags.Version {
		fmt.Print(version.Version())
		return
	}
	if flag.NArg() > 0 {
		usage()
		os.Exit(2)
	}

	err := do()
	if err != nil {
		log.Fatal(err)
	}
}

func do() error {
	// Setup Upspin client.
	cfg, err := config.FromFile(flags.Config)
	if err != nil {
		return err
	}
	transports.Init(cfg)
	cacheutil.Start(cfg)
	upc := client.New(cfg)

	// Guess at previous upsync time.
	getwd, err := os.Getwd()
	if err != nil {
		return err
	}
	lastUpsyncFi, err := os.Stat(*upsyncFlag)
	if os.IsNotExist(err) { // first time
		err = ioutil.WriteFile(*upsyncFlag, []byte(getwd), 0644)
		if err != nil {
			return err
		}
	} else if err != nil { // stat failed; very unusual
		return err
	} else { // normal case
		lastUpsync = lastUpsyncFi.ModTime().Unix()
	}
	log.Printf("lastUpsync %v", lastUpsyncFi.ModTime())

	// Find first component of current directory that looks like email address,
	// then make wd == upspin working directory.
	wd := getwd
	i := strings.IndexByte(wd, '@')
	if i < 0 {
		return fmt.Errorf("couldn't find upspin user name in working directory %s", getwd)
	}
	i = strings.LastIndexAny(wd[:i], "\\/")
	if i < 0 {
		return fmt.Errorf("unable to parse working directory %s", getwd)
	}
	slash := wd[i : i+1]
	wd = wd[i+1:]
	if slash != "/" {
		wd = strings.ReplaceAll(wd, slash, "/")
	}

	// Start copying.
	err = upsync(upc, wd, "")
	if err != nil {
		return err
	}

	// Save time of this upsync for next upsync "skipping old" heuristic.
	err = ioutil.WriteFile(*upsyncFlag, []byte(getwd), 0644)
	// We're more or less successful even if we can't record the time.  But warn.
	return err
}

// upsync walks the local and remote trees rooted at subdir to update each file to newer versions.
// The upspin.Client upc and the Upspin starting directory wd don't change from what was set in main.
// The subdir argument changes for the depth-first recursive tree walk and is either empty or a
// directory pathname with trailing slash.
func upsync(upc upspin.Client, wd, subdir string) error {

	// udir and ldir are sorted lists of remote and local files in subdir.
	udir, err := upc.Glob(wd + "/" + subdir + "*")
	if err != nil {
		return err
	}
	ldir, err := ioutil.ReadDir(subdir + ".")
	if err != nil {
		return err
	}

	// Advance through the two lists, comparing at each iteration udir[uj] and ldir[lj].
	uj := 0
	lj := 0
	for {
		cmp := 0 // -1,0,1 as udir[uj] sorts before,same,after ldir[lj]
		if lj < len(ldir) && ldir[lj].Mode()&os.ModeSymlink != 0 {
			return fmt.Errorf("local symlinks are not allowed: %s", ldir[lj].Name())
		}
		if uj >= len(udir) {
			if lj >= len(ldir) {
				break // both lists exhausted
			}
			cmp = 1
		} else if lj >= len(ldir) {
			cmp = -1
		} else {
			cmp = strings.Compare(string(udir[uj].SignedName)[len(wd)+1:], subdir+ldir[lj].Name())
		}

		// Copy newer to older/missing.
		switch cmp {
		case -1:
			pathname := string(udir[uj].SignedName)[len(wd)+1:]
			switch {
			case udir[uj].Attr&upspin.AttrLink != 0:
				fmt.Println("ignoring upspin symlink", pathname)
			case udir[uj].Attr&upspin.AttrDirectory != 0:
				err = os.Mkdir(pathname, 0700)
				if err != nil {
					return err
				}
				err = upsync(upc, wd, pathname+"/")
				if err != nil {
					return err
				}
			case udir[uj].Attr&upspin.AttrIncomplete != 0:
				fmt.Println("permission problem; creating placeholder ", pathname)
				empty := make([]byte, 0)
				err = ioutil.WriteFile(pathname, empty, 0)
				if err != nil {
					return err
				}
			case len(udir[uj].Blocks) > 50:
				fmt.Println("skipping big", pathname)
			default:
				utime := int64(udir[uj].Time)
				err = pull(upc, wd, pathname, utime)
				if err != nil {
					return err
				}
			}
			uj++
		case 0:
			pathname := subdir + ldir[lj].Name()
			uIsDir := udir[uj].Attr&upspin.AttrDirectory != 0
			lIsDir := ldir[lj].IsDir()
			if uIsDir != lIsDir {
				return fmt.Errorf("same name, different Directory attribute! %s", pathname)
			}
			if uIsDir {
				err = upsync(upc, wd, pathname+"/")
				if err != nil {
					return err
				}
			} else {
				utime := int64(udir[uj].Time)
				ltime := ldir[lj].ModTime().Unix()
				if utime > ltime {
					err = pull(upc, wd, pathname, utime)
					if err != nil {
						return err
					}
				} else if utime < ltime {
					err = push(upc, wd, pathname, ltime)
					if err != nil {
						return err
					}
				} else {
					// Assume already in sync.
					// TODO(ehg) Compare sizes as sanity check?
				}
			}
			uj++
			lj++
		case 1:
			pathname := subdir + ldir[lj].Name()
			if ldir[lj].IsDir() {
				fmt.Println("upspin mkdir", wd+"/"+pathname)
				_, err = upc.MakeDirectory(upspin.PathName(wd + "/" + pathname))
				if err != nil {
					return err
				}
				err = upsync(upc, wd, pathname+"/")
				if err != nil {
					return err
				}
			} else {
				ltime := ldir[lj].ModTime().Unix()
				err = push(upc, wd, pathname, ltime)
				if err != nil {
					return err
				}
			}
			lj++
		}
	}
	return nil
}

// pull copies pathname from Upspin to local disk, copying the modification time.
func pull(upc upspin.Client, wd, pathname string, utime int64) error {
	fmt.Println("pull", pathname)
	// TODO(ehg) If we ever decide to parallelize, or even if we decide to
	// run on small memory machines, switch to io.Copy().
	bytes, err := upc.Get(upspin.PathName(wd + "/" + pathname))
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(pathname, bytes, 0600)
	if err != nil {
		return err
	}
	mtime := time.Unix(utime, 0)
	err = os.Chtimes(pathname, mtime, mtime)
	if err != nil {
		return err
	}
	return nil
}

// pull copies pathname from local disk to Upspin, copying the modification time.
func push(upc upspin.Client, wd, pathname string, ltime int64) error {
	if ltime < lastUpsync {
		fmt.Printf("skipping old %v %v\n", pathname, ltime)
		return nil
	}
	fmt.Println("push", pathname)
	bytes, err := ioutil.ReadFile(pathname)
	if err != nil {
		return err
	}
	path := upspin.PathName(wd + "/" + pathname)
	_, err = upc.Put(path, bytes)
	if err != nil {
		return err
	}
	err = upc.SetTime(path, upspin.Time(ltime))
	if err != nil {
		return err
	}
	return nil
}

// upspinDir is copied from upspin.io/flags/flags.go.
func upspinDir(subdir string) string {
	home, err := config.Homedir()
	if err != nil {
		log.Printf("upsync: could not locate home directory: %v", err)
		home = "."
	}
	return filepath.Join(home, "upspin", subdir)
}
<commit_msg>upsync: no time to print on first execution<commit_after>// Copyright 2019 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Upsync keeps a local disk copy in sync with a master version in Upspin.
// See the command's usage method for documentation.
package main

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

	"upspin.io/client"
	"upspin.io/cmd/cacheserver/cacheutil"
	"upspin.io/config"
	"upspin.io/flags"
	"upspin.io/transports"
	"upspin.io/upspin"
	"upspin.io/version"
)

var lastUpsync int64 // Unix time when an upsync was last completed

const help = `Upsync keeps a local disk copy in sync with a master version in
Upspin. It is a weak substitute for upspinfs.

To start, create a local directory whose path ends in a string that looks like
an existing upspin directory, such as ~/u/alice@example.com. Cd there and execute
upsync.  Make local edits to the downloaded files or create new files, and then
upsync to upload your changes to the Upspin master. To discard your local changes,
just remove the edited local files and upsync. (Executing both local rm and
upspin rm are required to remove content permanently.)

Upsync prints which files it is uploading or downloading and declines to download
files larger than 50MB. It promises never to write outside the starting directory
and subdirectories and, as an initial way to enforce that, declines all symlinks.

There are no clever merge heuristics;  copying back and forth proceeds by a trivial
"newest wins" rule.  This requires some discipline in remembering to upsync after
each editing session and is better suited to single person rather than joint
editing. Don't let your computer clocks drift.

With better FUSE support on Windows and OpenBSD it will be possible to switch
to the much preferable upspinfs. But even then upsync may have benefits:
* enables work offline, i.e. a workaround for (missing) distributed upspinfs
* offers mitigation of user misfortune, such as losing upspin keys
* provides a worked out example for new Upspin client developers
* leaves a backup in case cloud store or Upspin projects die without warning

This tool was written assuming you are an experienced Upspin user trying to
assist a friend with file sharing or backup on Windows 10.  Here is a checklist:
1. create or check existing upspin account and permissions
   It is helpful if you can provide them space on an existing server.
2. confirm \Users\alice\upspin\config is correct
3. disk must be NTFS (because FAT has peculiar timestamps)
4. open a powershell window
5. install go and git, if not already there
6. go get -u upspin.io/cmd/...
7. fetch upsync.go; go install
   Go files must be transferred as UTF8, else expect a NUL compile warning.
8. mkdir \Users\alice\u\alice@example.com
9. upsync

`

const cmdName = "upsync"

var upsyncFlag = flag.String("upsync", upspinDir("upsync"), "file whose mtime is last upsync")

func usage() {
	fmt.Fprintln(os.Stderr, help)
	fmt.Fprintf(os.Stderr, "Usage: %s [flags]\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("upsync: ")
	flag.Usage = usage
	flags.Parse(flags.Client, "version")
	if flags.Version {
		fmt.Print(version.Version())
		return
	}
	if flag.NArg() > 0 {
		usage()
		os.Exit(2)
	}

	err := do()
	if err != nil {
		log.Fatal(err)
	}
}

func do() error {
	// Setup Upspin client.
	cfg, err := config.FromFile(flags.Config)
	if err != nil {
		return err
	}
	transports.Init(cfg)
	cacheutil.Start(cfg)
	upc := client.New(cfg)

	// Guess at previous upsync time.
	getwd, err := os.Getwd()
	if err != nil {
		return err
	}
	lastUpsyncFi, err := os.Stat(*upsyncFlag)
	if os.IsNotExist(err) { // first time
		err = ioutil.WriteFile(*upsyncFlag, []byte(getwd), 0644)
		if err != nil {
			return err
		}
	} else if err != nil { // stat failed; very unusual
		return err
	} else { // normal case
		lastUpsync = lastUpsyncFi.ModTime().Unix()
	}
	if lastUpsyncFi != nil {
		log.Printf("lastUpsync %v", lastUpsyncFi.ModTime())
	}

	// Find first component of current directory that looks like email address,
	// then make wd == upspin working directory.
	wd := getwd
	i := strings.IndexByte(wd, '@')
	if i < 0 {
		return fmt.Errorf("couldn't find upspin user name in working directory %s", getwd)
	}
	i = strings.LastIndexAny(wd[:i], "\\/")
	if i < 0 {
		return fmt.Errorf("unable to parse working directory %s", getwd)
	}
	slash := wd[i : i+1]
	wd = wd[i+1:]
	if slash != "/" {
		wd = strings.ReplaceAll(wd, slash, "/")
	}

	// Start copying.
	err = upsync(upc, wd, "")
	if err != nil {
		return err
	}

	// Save time of this upsync for next upsync "skipping old" heuristic.
	err = ioutil.WriteFile(*upsyncFlag, []byte(getwd), 0644)
	// We're more or less successful even if we can't record the time.  But warn.
	return err
}

// upsync walks the local and remote trees rooted at subdir to update each file to newer versions.
// The upspin.Client upc and the Upspin starting directory wd don't change from what was set in main.
// The subdir argument changes for the depth-first recursive tree walk and is either empty or a
// directory pathname with trailing slash.
func upsync(upc upspin.Client, wd, subdir string) error {

	// udir and ldir are sorted lists of remote and local files in subdir.
	udir, err := upc.Glob(wd + "/" + subdir + "*")
	if err != nil {
		return err
	}
	ldir, err := ioutil.ReadDir(subdir + ".")
	if err != nil {
		return err
	}

	// Advance through the two lists, comparing at each iteration udir[uj] and ldir[lj].
	uj := 0
	lj := 0
	for {
		cmp := 0 // -1,0,1 as udir[uj] sorts before,same,after ldir[lj]
		if lj < len(ldir) && ldir[lj].Mode()&os.ModeSymlink != 0 {
			return fmt.Errorf("local symlinks are not allowed: %s", ldir[lj].Name())
		}
		if uj >= len(udir) {
			if lj >= len(ldir) {
				break // both lists exhausted
			}
			cmp = 1
		} else if lj >= len(ldir) {
			cmp = -1
		} else {
			cmp = strings.Compare(string(udir[uj].SignedName)[len(wd)+1:], subdir+ldir[lj].Name())
		}

		// Copy newer to older/missing.
		switch cmp {
		case -1:
			pathname := string(udir[uj].SignedName)[len(wd)+1:]
			switch {
			case udir[uj].Attr&upspin.AttrLink != 0:
				fmt.Println("ignoring upspin symlink", pathname)
			case udir[uj].Attr&upspin.AttrDirectory != 0:
				err = os.Mkdir(pathname, 0700)
				if err != nil {
					return err
				}
				err = upsync(upc, wd, pathname+"/")
				if err != nil {
					return err
				}
			case udir[uj].Attr&upspin.AttrIncomplete != 0:
				fmt.Println("permission problem; creating placeholder ", pathname)
				empty := make([]byte, 0)
				err = ioutil.WriteFile(pathname, empty, 0)
				if err != nil {
					return err
				}
			case len(udir[uj].Blocks) > 50:
				fmt.Println("skipping big", pathname)
			default:
				utime := int64(udir[uj].Time)
				err = pull(upc, wd, pathname, utime)
				if err != nil {
					return err
				}
			}
			uj++
		case 0:
			pathname := subdir + ldir[lj].Name()
			uIsDir := udir[uj].Attr&upspin.AttrDirectory != 0
			lIsDir := ldir[lj].IsDir()
			if uIsDir != lIsDir {
				return fmt.Errorf("same name, different Directory attribute! %s", pathname)
			}
			if uIsDir {
				err = upsync(upc, wd, pathname+"/")
				if err != nil {
					return err
				}
			} else {
				utime := int64(udir[uj].Time)
				ltime := ldir[lj].ModTime().Unix()
				if utime > ltime {
					err = pull(upc, wd, pathname, utime)
					if err != nil {
						return err
					}
				} else if utime < ltime {
					err = push(upc, wd, pathname, ltime)
					if err != nil {
						return err
					}
				} else {
					// Assume already in sync.
					// TODO(ehg) Compare sizes as sanity check?
				}
			}
			uj++
			lj++
		case 1:
			pathname := subdir + ldir[lj].Name()
			if ldir[lj].IsDir() {
				fmt.Println("upspin mkdir", wd+"/"+pathname)
				_, err = upc.MakeDirectory(upspin.PathName(wd + "/" + pathname))
				if err != nil {
					return err
				}
				err = upsync(upc, wd, pathname+"/")
				if err != nil {
					return err
				}
			} else {
				ltime := ldir[lj].ModTime().Unix()
				err = push(upc, wd, pathname, ltime)
				if err != nil {
					return err
				}
			}
			lj++
		}
	}
	return nil
}

// pull copies pathname from Upspin to local disk, copying the modification time.
func pull(upc upspin.Client, wd, pathname string, utime int64) error {
	fmt.Println("pull", pathname)
	// TODO(ehg) If we ever decide to parallelize, or even if we decide to
	// run on small memory machines, switch to io.Copy().
	bytes, err := upc.Get(upspin.PathName(wd + "/" + pathname))
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(pathname, bytes, 0600)
	if err != nil {
		return err
	}
	mtime := time.Unix(utime, 0)
	err = os.Chtimes(pathname, mtime, mtime)
	if err != nil {
		return err
	}
	return nil
}

// pull copies pathname from local disk to Upspin, copying the modification time.
func push(upc upspin.Client, wd, pathname string, ltime int64) error {
	if ltime < lastUpsync {
		fmt.Printf("skipping old %v %v\n", pathname, ltime)
		return nil
	}
	fmt.Println("push", pathname)
	bytes, err := ioutil.ReadFile(pathname)
	if err != nil {
		return err
	}
	path := upspin.PathName(wd + "/" + pathname)
	_, err = upc.Put(path, bytes)
	if err != nil {
		return err
	}
	err = upc.SetTime(path, upspin.Time(ltime))
	if err != nil {
		return err
	}
	return nil
}

// upspinDir is copied from upspin.io/flags/flags.go.
func upspinDir(subdir string) string {
	home, err := config.Homedir()
	if err != nil {
		log.Printf("upsync: could not locate home directory: %v", err)
		home = "."
	}
	return filepath.Join(home, "upspin", subdir)
}
<|endoftext|>


<commit_before>package main

import (
  "os"
  "log"
  "net/http"
)

func main() {
  url := os.Getenv("WAKE_UP_URL")
  if url == "" {
    log.Fatal("$WAKE_UP_URL must be set.")
  }
  resp, err := http.Get(url)
  if err != nil {
    log.Fatal(err)
  }
  defer resp.Body.Close()
  log.Printf("Status: %s Protocol: %s", resp.Status, resp.Proto)
}
<commit_msg>Made wakeup log message more fun.<commit_after>package main

import (
  "os"
  "log"
  "net/http"
)

func main() {
  url := os.Getenv("WAKE_UP_URL")
  if url == "" {
    log.Fatal("$WAKE_UP_URL must be set.")
  }
  resp, err := http.Get(url)
  if err != nil {
    log.Fatal(err)
  }
  defer resp.Body.Close()
  log.Printf("Mmmf, what? Ok, waking up. Status: %s Protocol: %s", resp.Status, resp.Proto)
}
<|endoftext|>


<commit_before>package processorcommand

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"github.com/trustmaster/go-aspell"
)

type OCRResult struct {
	Type string
	Text string
}

func newOCRResult(ocrType string, result string) *OCRResult {
	return &OCRResult{
		ocrType,
		result,
	}
}

func (this *OCRResult) removeNonWords() {
	blob := this.Text

	speller, err := aspell.NewSpeller(map[string]string{
		"lang": "en_US",
	})
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
		return
	}
	defer speller.Delete()

	singleCharWords := regexp.MustCompile("(a|i)")
	numberRegex := regexp.MustCompile("\\d{3,}")
	wordRegexp := regexp.MustCompile("\\b(\\w+)\\b")
	words := wordRegexp.FindAllString(blob, -1)

	str := ""

	for _, word := range words {
		if numberRegex.MatchString(word) {
			str += " " + word
		} else if len(word) == 1 {
			if singleCharWords.MatchString(word) {
				str += " " + word
			}
		} else if speller.Check(word) {
			str += " " + word
		}
	}

	this.Text = strings.TrimSpace(str)
}

func (this *OCRResult) wordCount(blob string) int {
	word_regexp := regexp.MustCompile("\\b(\\w+)\\b")
	words := word_regexp.FindAllString(blob, -1)

	// don't let single char words count towards the overal word count. Gets thrown off by poor OCR results
	count := 0
	for _, word := range words {
		if len(word) > 1 {
			count++
		}
	}

	return count
}

type MultiOCRCommand []OCRCommand

func (this MultiOCRCommand) Run(image string) (*OCRResult, error) {
	results := make(chan *OCRResult, len(this))
	errs := make(chan error, len(this))

	for _, command := range this {
		go func(c OCRCommand) {
			k, err := c.Run(image)
			if err != nil {
				errs <- err
				return
			}
			results <- k
		}(command)
	}

	max := -1
	var best *OCRResult

	for i := 0; i < len(this); i++ {
		select {
		case result := <-results:
			result.removeNonWords()
			count := result.wordCount(result.Text)

			if count > max {
				best = result
				max = count
			}

		case err := <-errs:
			return nil, err
		}
	}

	// Return the average, same as before.
	return best, nil
}

type OCRCommand interface {
	Run(image string) (*OCRResult, error)
}

type MemeOCR struct {
	name string
}

func NewMemeOCR() *MemeOCR {
	return &MemeOCR{
		"MemeOCR",
	}
}

func (this *MemeOCR) Run(image string) (*OCRResult, error) {
	imageTif := fmt.Sprintf("%s_meme.jpg", image)
	outText := fmt.Sprintf("%s_meme", image)
	inImage := fmt.Sprintf("%s[0]", image)
	preprocessingArgs := []string{"convert", inImage, "-resize", "400%", "-fill", "black", "-fuzz", "10%", "+opaque", "#FFFFFF", imageTif}
	tesseractArgs := []string{"-l", "meme", imageTif, outText}

	err := runProcessorCommand(GM_COMMAND, preprocessingArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Meme preprocessing command failed with error = %v", err))
	}
	defer os.Remove(imageTif)

	err = runProcessorCommand("tesseract", tesseractArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Meme tesseract command failed with error = %v", err))
	}
	defer os.Remove(outText + ".txt")

	text, err := ioutil.ReadFile(outText + ".txt")
	if err != nil {
		return nil, err
	}

	result := strings.ToLower(strings.TrimSpace(string(text[:])))

	return newOCRResult(this.name, result), nil
}

type StandardOCR struct {
	name string
}

func NewStandardOCR() *StandardOCR {
	return &StandardOCR{
		"StandardOCR",
	}
}

func (this *StandardOCR) Run(image string) (*OCRResult, error) {
	imageTif := fmt.Sprintf("%s_standard.jpg", image)
	outText := fmt.Sprintf("%s_standard", image)
	inImage := fmt.Sprintf("%s[0]", image)
	preprocessingArgs := []string{"convert", inImage, "-resize", "400%", "-type", "Grayscale", imageTif}
	tesseractArgs := []string{"-l", "eng", imageTif, outText}

	err := runProcessorCommand(GM_COMMAND, preprocessingArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Standard preprocessing command failed with error = %v", err))
	}
	defer os.Remove(imageTif)

	err = runProcessorCommand("tesseract", tesseractArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Standard tesseract command failed with error = %v", err))
	}
	defer os.Remove(outText + ".txt")

	text, err := ioutil.ReadFile(outText + ".txt")
	if err != nil {
		return nil, err
	}

	result := strings.ToLower(strings.TrimSpace(string(text[:])))

	return newOCRResult(this.name, result), nil
}
<commit_msg>Fix OCR gm convert<commit_after>package processorcommand

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"github.com/trustmaster/go-aspell"
)

type OCRResult struct {
	Type string
	Text string
}

func newOCRResult(ocrType string, result string) *OCRResult {
	return &OCRResult{
		ocrType,
		result,
	}
}

func (this *OCRResult) removeNonWords() {
	blob := this.Text

	speller, err := aspell.NewSpeller(map[string]string{
		"lang": "en_US",
	})
	if err != nil {
		fmt.Printf("Error: %s", err.Error())
		return
	}
	defer speller.Delete()

	singleCharWords := regexp.MustCompile("(a|i)")
	numberRegex := regexp.MustCompile("\\d{3,}")
	wordRegexp := regexp.MustCompile("\\b(\\w+)\\b")
	words := wordRegexp.FindAllString(blob, -1)

	str := ""

	for _, word := range words {
		if numberRegex.MatchString(word) {
			str += " " + word
		} else if len(word) == 1 {
			if singleCharWords.MatchString(word) {
				str += " " + word
			}
		} else if speller.Check(word) {
			str += " " + word
		}
	}

	this.Text = strings.TrimSpace(str)
}

func (this *OCRResult) wordCount(blob string) int {
	word_regexp := regexp.MustCompile("\\b(\\w+)\\b")
	words := word_regexp.FindAllString(blob, -1)

	// don't let single char words count towards the overal word count. Gets thrown off by poor OCR results
	count := 0
	for _, word := range words {
		if len(word) > 1 {
			count++
		}
	}

	return count
}

type MultiOCRCommand []OCRCommand

func (this MultiOCRCommand) Run(image string) (*OCRResult, error) {
	results := make(chan *OCRResult, len(this))
	errs := make(chan error, len(this))

	for _, command := range this {
		go func(c OCRCommand) {
			k, err := c.Run(image)
			if err != nil {
				errs <- err
				return
			}
			results <- k
		}(command)
	}

	max := -1
	var best *OCRResult

	for i := 0; i < len(this); i++ {
		select {
		case result := <-results:
			result.removeNonWords()
			count := result.wordCount(result.Text)

			if count > max {
				best = result
				max = count
			}

		case err := <-errs:
			return nil, err
		}
	}

	// Return the average, same as before.
	return best, nil
}

type OCRCommand interface {
	Run(image string) (*OCRResult, error)
}

type MemeOCR struct {
	name string
}

func NewMemeOCR() *MemeOCR {
	return &MemeOCR{
		"MemeOCR",
	}
}

func (this *MemeOCR) Run(image string) (*OCRResult, error) {
	imageTif := fmt.Sprintf("%s_meme.jpg", image)
	outText := fmt.Sprintf("%s_meme", image)
	inImage := fmt.Sprintf("%s[0]", image)
	preprocessingArgs := []string{"convert", inImage, "-resize", "400%", "-fill", "black", "-fuzz", "10%", "+matte", "-matte", "-transparent", "white", imageTif}
	tesseractArgs := []string{"-l", "meme", imageTif, outText}

	err := runProcessorCommand(GM_COMMAND, preprocessingArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Meme preprocessing command failed with error = %v", err))
	}
	defer os.Remove(imageTif)

	err = runProcessorCommand("tesseract", tesseractArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Meme tesseract command failed with error = %v", err))
	}
	defer os.Remove(outText + ".txt")

	text, err := ioutil.ReadFile(outText + ".txt")
	if err != nil {
		return nil, err
	}

	result := strings.ToLower(strings.TrimSpace(string(text[:])))

	return newOCRResult(this.name, result), nil
}

type StandardOCR struct {
	name string
}

func NewStandardOCR() *StandardOCR {
	return &StandardOCR{
		"StandardOCR",
	}
}

func (this *StandardOCR) Run(image string) (*OCRResult, error) {
	imageTif := fmt.Sprintf("%s_standard.jpg", image)
	outText := fmt.Sprintf("%s_standard", image)
	inImage := fmt.Sprintf("%s[0]", image)
	preprocessingArgs := []string{"convert", inImage, "-resize", "400%", "-type", "Grayscale", imageTif}
	tesseractArgs := []string{"-l", "eng", imageTif, outText}

	err := runProcessorCommand(GM_COMMAND, preprocessingArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Standard preprocessing command failed with error = %v", err))
	}
	defer os.Remove(imageTif)

	err = runProcessorCommand("tesseract", tesseractArgs)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Standard tesseract command failed with error = %v", err))
	}
	defer os.Remove(outText + ".txt")

	text, err := ioutil.ReadFile(outText + ".txt")
	if err != nil {
		return nil, err
	}

	result := strings.ToLower(strings.TrimSpace(string(text[:])))

	return newOCRResult(this.name, result), nil
}
<|endoftext|>


<commit_before>package experimental

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("delete command", func() {
	var (
		orgName   string
		spaceName string
		appName   string
	)

	BeforeEach(func() {
		orgName = helpers.NewOrgName()
		spaceName = helpers.NewSpaceName()
		appName = helpers.PrefixedRandomName("app")
	})

	When("--help flag is set", func() {
		It("Displays command usage to output", func() {
			session := helpers.CF("delete", "--help")
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("delete - Delete an app"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("cf delete APP_NAME \\[-r\\] \\[-f\\]"))
			Eventually(session).Should(Say("OPTIONS:"))
			Eventually(session).Should(Say("\\s+-f\\s+Force deletion without confirmation"))
			Eventually(session).Should(Say("\\s+-r\\s+Also delete any mapped routes \\[Not currently functional\\]"))
			Eventually(session).Should(Exit(0))
		})
	})

	When("the app name is not provided", func() {
		It("tells the user that the app name is required, prints help text, and exits 1", func() {
			session := helpers.CF("delete")

			Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided"))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Exit(1))
		})
	})

	When("the environment is not setup correctly", func() {
		When("no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
				Eventually(session).Should(Exit(1))
			})
		})

		When("not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' to log in."))
				Eventually(session).Should(Exit(1))
			})
		})

		When("there is no org set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
			})

			It("fails with no targeted org error message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
				Eventually(session).Should(Exit(1))
			})
		})

		When("there is no space set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
				helpers.TargetOrg(ReadOnlyOrg)
			})

			It("fails with no targeted space error message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No space targeted, use 'cf target -s SPACE' to target a space."))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	When("the environment is setup correctly", func() {
		BeforeEach(func() {
			helpers.SetupCF(orgName, spaceName)
		})

		AfterEach(func() {
			helpers.QuickDeleteOrg(orgName)
		})

		When("the app does not exist", func() {
			When("the -f flag is provided", func() {
				It("it displays the app does not exist", func() {
					username, _ := helpers.GetCredentials()
					session := helpers.CF("delete", appName, "-f")
					Eventually(session).Should(Say("Deleting app %s in org %s / space %s as %s...", appName, orgName, spaceName, username))
					Eventually(session).Should(Say("App %s does not exist", appName))
					Eventually(session).Should(Say("OK"))
					Eventually(session).Should(Exit(0))
				})
			})

			When("the -f flag not is provided", func() {
				var buffer *Buffer

				BeforeEach(func() {
					buffer = NewBuffer()
				})

				When("the user enters 'y'", func() {
					BeforeEach(func() {
						buffer.Write([]byte("y\n"))
					})

					It("it displays the app does not exist", func() {
						username, _ := helpers.GetCredentials()
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("Deleting app %s in org %s / space %s as %s...", appName, orgName, spaceName, username))
						Eventually(session).Should(Say("App %s does not exist", appName))
						Eventually(session).Should(Say("OK"))
						Eventually(session).Should(Exit(0))
					})
				})

				When("the user enters 'n'", func() {
					BeforeEach(func() {
						buffer.Write([]byte("n\n"))
					})

					It("does not delete the app", func() {
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("Delete cancelled"))
						Eventually(session).Should(Exit(0))
					})
				})

				When("the user enters the default input (hits return)", func() {
					BeforeEach(func() {
						buffer.Write([]byte("\n"))
					})

					It("does not delete the app", func() {
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("Delete cancelled"))
						Eventually(session).Should(Exit(0))
					})
				})

				When("the user enters an invalid answer", func() {
					BeforeEach(func() {
						// The second '\n' is intentional. Otherwise the buffer will be
						// closed while the interaction is still waiting for input; it gets
						// an EOF and causes an error.
						buffer.Write([]byte("wat\n\n"))
					})

					It("asks again", func() {
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("invalid input \\(not y, n, yes, or no\\)"))
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Exit(0))
					})
				})
			})
		})

		When("the app exists", func() {
			BeforeEach(func() {
				helpers.WithHelloWorldApp(func(appDir string) {
					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
				})
			})

			It("deletes the app", func() {
				session := helpers.CF("delete", appName, "-f")
				username, _ := helpers.GetCredentials()
				Eventually(session).Should(Say("Deleting app %s in org %s / space %s as %s...", appName, orgName, spaceName, username))
				Eventually(session).Should(Say("OK"))
				Eventually(session).Should(Exit(0))

				Eventually(helpers.CF("app", appName)).Should(Exit(1))
			})
		})
	})
})
<commit_msg>Fixed the package name for delete_command_test<commit_after>package isolated

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("delete command", func() {
	var (
		orgName   string
		spaceName string
		appName   string
	)

	BeforeEach(func() {
		orgName = helpers.NewOrgName()
		spaceName = helpers.NewSpaceName()
		appName = helpers.PrefixedRandomName("app")
	})

	When("--help flag is set", func() {
		It("Displays command usage to output", func() {
			session := helpers.CF("delete", "--help")
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("delete - Delete an app"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say("cf delete APP_NAME \\[-r\\] \\[-f\\]"))
			Eventually(session).Should(Say("OPTIONS:"))
			Eventually(session).Should(Say("\\s+-f\\s+Force deletion without confirmation"))
			Eventually(session).Should(Say("\\s+-r\\s+Also delete any mapped routes \\[Not currently functional\\]"))
			Eventually(session).Should(Exit(0))
		})
	})

	When("the app name is not provided", func() {
		It("tells the user that the app name is required, prints help text, and exits 1", func() {
			session := helpers.CF("delete")

			Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided"))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Exit(1))
		})
	})

	When("the environment is not setup correctly", func() {
		When("no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
				Eventually(session).Should(Exit(1))
			})
		})

		When("not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' to log in."))
				Eventually(session).Should(Exit(1))
			})
		})

		When("there is no org set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
			})

			It("fails with no targeted org error message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
				Eventually(session).Should(Exit(1))
			})
		})

		When("there is no space set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
				helpers.TargetOrg(ReadOnlyOrg)
			})

			It("fails with no targeted space error message", func() {
				session := helpers.CF("delete", appName)
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No space targeted, use 'cf target -s SPACE' to target a space."))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	When("the environment is setup correctly", func() {
		BeforeEach(func() {
			helpers.SetupCF(orgName, spaceName)
		})

		AfterEach(func() {
			helpers.QuickDeleteOrg(orgName)
		})

		When("the app does not exist", func() {
			When("the -f flag is provided", func() {
				It("it displays the app does not exist", func() {
					username, _ := helpers.GetCredentials()
					session := helpers.CF("delete", appName, "-f")
					Eventually(session).Should(Say("Deleting app %s in org %s / space %s as %s...", appName, orgName, spaceName, username))
					Eventually(session).Should(Say("App %s does not exist", appName))
					Eventually(session).Should(Say("OK"))
					Eventually(session).Should(Exit(0))
				})
			})

			When("the -f flag not is provided", func() {
				var buffer *Buffer

				BeforeEach(func() {
					buffer = NewBuffer()
				})

				When("the user enters 'y'", func() {
					BeforeEach(func() {
						buffer.Write([]byte("y\n"))
					})

					It("it displays the app does not exist", func() {
						username, _ := helpers.GetCredentials()
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("Deleting app %s in org %s / space %s as %s...", appName, orgName, spaceName, username))
						Eventually(session).Should(Say("App %s does not exist", appName))
						Eventually(session).Should(Say("OK"))
						Eventually(session).Should(Exit(0))
					})
				})

				When("the user enters 'n'", func() {
					BeforeEach(func() {
						buffer.Write([]byte("n\n"))
					})

					It("does not delete the app", func() {
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("Delete cancelled"))
						Eventually(session).Should(Exit(0))
					})
				})

				When("the user enters the default input (hits return)", func() {
					BeforeEach(func() {
						buffer.Write([]byte("\n"))
					})

					It("does not delete the app", func() {
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("Delete cancelled"))
						Eventually(session).Should(Exit(0))
					})
				})

				When("the user enters an invalid answer", func() {
					BeforeEach(func() {
						// The second '\n' is intentional. Otherwise the buffer will be
						// closed while the interaction is still waiting for input; it gets
						// an EOF and causes an error.
						buffer.Write([]byte("wat\n\n"))
					})

					It("asks again", func() {
						session := helpers.CFWithStdin(buffer, "delete", appName)
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Say("invalid input \\(not y, n, yes, or no\\)"))
						Eventually(session).Should(Say("Really delete the app %s\\? \\[yN\\]", appName))
						Eventually(session).Should(Exit(0))
					})
				})
			})
		})

		When("the app exists", func() {
			BeforeEach(func() {
				helpers.WithHelloWorldApp(func(appDir string) {
					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
				})
			})

			It("deletes the app", func() {
				session := helpers.CF("delete", appName, "-f")
				username, _ := helpers.GetCredentials()
				Eventually(session).Should(Say("Deleting app %s in org %s / space %s as %s...", appName, orgName, spaceName, username))
				Eventually(session).Should(Say("OK"))
				Eventually(session).Should(Exit(0))

				Eventually(helpers.CF("app", appName)).Should(Exit(1))
			})
		})
	})
})
<|endoftext|>