<commit_before>package main

import (
  "fmt"
  "time"
  "os"
  "bufio"
  "log"
  "regexp"
  "errors"
  "math/rand"
)

type Posicao struct {
  linha  int
  coluna int
}

type PacGo struct {
  posicao Posicao
  figura  string // emoji
  pilula  bool
  vidas   int
  pontos  int
}

type Fantasma struct {
  posicao Posicao
  figura  string // emoji
}

type Labirinto struct {
  largura int
  altura  int
  mapa    []string
  figMuro string
  figSP   string
}

type Movimento int

const (
        Cima = iota
        Baixo
        Esquerda
        Direita
        Nenhum
        Sai
)

var labirinto  *Labirinto
var pacgo      *PacGo
var fantasmas  []*Fantasma
var mapaSinais map[int]string

func criarFantasma(posicao Posicao, figura string) {
  fantasma := &Fantasma{ posicao:posicao, figura: figura}
  fantasmas = append(fantasmas, fantasma)
}

func criarPacGo(posicao Posicao, figura string, pilula bool, vidas int) {
  pacgo = &PacGo{ posicao:posicao, figura: "\xF0\x9F\x98\x83", pilula: false, vidas:3 }
}

func construirLabirinto(nomeArquivo string) error {

  var ErrMapNotFound = errors.New("Não conseguiu ler o arquivo do mapa")

  var arquivo string
  if nomeArquivo == "" {
    arquivo = "./data/mapa.txt"
  } else {
    arquivo = nomeArquivo
  }

  if file, err := os.Open(arquivo); err == nil {

    // fecha depois de ler o arquivo
    defer file.Close()

    // inicializa o mapa vazio
    mapa := []string{}

    r, _ := regexp.Compile("[^ #.P]")

    // cria um leitor para ler linha a linha o arquivo
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
      linha := scanner.Text()

      for indice , caracter := range linha {
        switch caracter {
          case 'F': criarFantasma( Posicao{len(mapa), indice}, "\xF0\x9F\x91\xBB" )
          case 'G': criarPacGo( Posicao{len(mapa), indice}, "\xF0\x9F\x98\x83", false, 3 )
        }
      }

      linha = r.ReplaceAllString(linha, " ")
      mapa = append(mapa, linha)
    }

    // verifica se teve erro o leitor
    if err = scanner.Err(); err != nil {
      log.Fatal(err)
      return ErrMapNotFound
    }

    labirinto = &Labirinto{largura: len(mapa[0]), altura: len(mapa), mapa : mapa, figMuro: "\x1b[44m \x1b[0m", figSP: "\xF0\x9F\x8D\x84"}
    return nil

  } else {
    log.Fatal(err)
    return ErrMapNotFound
  }
}

func atualizarLabirinto() {
  limpaTela()

  // Imprime os pontos
  moveCursor(Posicao{0,0})
  fmt.Printf("%sPontos: %d%s\n", "\x1b[31;1m", pacgo.pontos, "\x1b[0m")

  posicaoInicial := Posicao{2,0}
  moveCursor(posicaoInicial)
  for _, linha := range labirinto.mapa {
    for _, char := range linha {
      switch char {
        case '#': fmt.Print(labirinto.figMuro)
        case '.': fmt.Print(".")
        case 'P': fmt.Print(labirinto.figSP)
        default:  fmt.Print(" ")
      }
    }
    fmt.Println("")
  }

  // Imprime PacGo
  moveCursor(posicaoInicial.adiciona(&pacgo.posicao))
  fmt.Printf("%s", pacgo.figura)

  // Imprime fantasmas
  for _, fantasma := range fantasmas {
    moveCursor(posicaoInicial.adiciona(&fantasma.posicao))
    fmt.Printf("%s", fantasma.figura)
  }

  // Move o cursor para fora do labirinto
  moveCursor(posicaoInicial.adiciona(&Posicao{labirinto.altura + 2, 0}))
}

func detectarColisao() bool {
  for _, fantasma := range fantasmas {
    if fantasma.posicao == pacgo.posicao {
      return true
    }
  }
  return false
}

func moverPacGo(m Movimento) {
  var novaLinha = pacgo.posicao.linha
  var novaColuna = pacgo.posicao.coluna

  switch m {
    case Cima:
      novaLinha--
      if novaLinha < 0 {
        novaLinha = labirinto.altura - 1
      }
    case Baixo:
      novaLinha++
      if novaLinha >= labirinto.altura {
        novaLinha = 0
      }
    case Direita:
      novaColuna++
      if novaColuna >= labirinto.largura {
        novaColuna = 0
      }
    case Esquerda:
      novaColuna--
      if novaColuna < 0 {
        novaColuna = labirinto.largura - 1
      }
  }

  conteudoDoMapa := labirinto.mapa[novaLinha][novaColuna]
  if conteudoDoMapa != '#' {
    pacgo.posicao.linha = novaLinha
    pacgo.posicao.coluna = novaColuna

    if (conteudoDoMapa == '.') || (conteudoDoMapa == 'P') {
      if (conteudoDoMapa == '.') {
        pacgo.pontos += 10
      } else {
        pacgo.pontos += 100
      }

      linha := labirinto.mapa[novaLinha]
      linha = linha[:novaColuna] + " " + linha[novaColuna+1:]
      labirinto.mapa[novaLinha] = linha
    }
  }
}

func random(min, max int) int {
    return rand.Intn(max - min) + min
}

func move(fantasma *Fantasma, valorDaPosicaoAtualDoFantasma byte, linhaAtualDoFantasma int, colunaAtualDoFantasma int){

  var direcao = random(0, 4)
  var sinal = mapaSinais[direcao]
  //fmt.Println(sinal)
  switch sinal {
  case "Cima":
              if linhaAtualDoFantasma == 0{
                if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = labirinto.altura - 1
                 }
             }else{
               var posicaoAcimaDoFantasma = labirinto.mapa[fantasma.posicao.linha - 1][fantasma.posicao.coluna]
               if posicaoAcimaDoFantasma != '#'{
                 fantasma.posicao.linha = fantasma.posicao.linha - 1
               }
             }
  case "Baixo":
              if linhaAtualDoFantasma == labirinto.altura - 1{
                 if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = 0
                 }
              }else{
                var posicaoAbaixoDoFantasma = labirinto.mapa[fantasma.posicao.linha + 1][fantasma.posicao.coluna]
                if posicaoAbaixoDoFantasma != '#'{
                  fantasma.posicao.linha = fantasma.posicao.linha + 1
                }
              }
  case "Direita":
                if colunaAtualDoFantasma == labirinto.largura-1{
                  if valorDaPosicaoAtualDoFantasma == ' '{
                    fantasma.posicao.coluna = 0
                  }
                }else{
                  var posicaoDireitaDofantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna + 1]
                  if posicaoDireitaDofantasma != '#'{
                    fantasma.posicao.coluna = fantasma.posicao.coluna + 1
                  }
                }
  case "Esquerda":
                 if colunaAtualDoFantasma == 0{
                   if valorDaPosicaoAtualDoFantasma == ' '{
                     fantasma.posicao.coluna = labirinto.largura - 1
                   }
                 }else{
                   var posicaoEsquerdaDoFantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna - 1]
                   if posicaoEsquerdaDoFantasma != '#'{
                     fantasma.posicao.coluna = fantasma.posicao.coluna - 1
                   }
                 }
  }
}

func moverFantasmas() {

  for {
    for i := 0; i < len(fantasmas); i++{
        var valorDaPosicaoAtualDoFantasma = labirinto.mapa[fantasmas[i].posicao.linha][fantasmas[i].posicao.coluna]
        var linhaAtualDoFantasma = fantasmas[i].posicao.linha
        var colunaAtualDoFantasma = fantasmas[i].posicao.coluna
        //fmt.Println(valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
        move(fantasmas[i], valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
    }
    dorme(300)
  }
}

func dorme(milisegundos time.Duration) {
  time.Sleep(time.Millisecond * milisegundos)
}

func entradaDoUsuario(canal chan<- Movimento) {
  array := make([]byte, 10)

  for {
    lido, _ := os.Stdin.Read(array)

    if lido == 1 && array[0] == 0x1b {
      canal <- Sai;
    } else if lido == 3 {
      if array[0] == 0x1b && array[1] == '[' {
        switch array[2] {
        case 'A': canal <- Cima
        case 'B': canal <- Baixo
        case 'C': canal <- Direita
        case 'D': canal <- Esquerda
        }
      }
    }
  }
}

func ativarPilula() {
  pacgo.pilula = true
  go desativarPilula(10000)
}

func desativarPilula(milisegundos time.Duration) {
  dorme(milisegundos)
  pacgo.pilula = false
}

func terminarJogo() {
  // pacgo morreu :(
  moveCursor( Posicao{labirinto.altura + 2, 0} )
  fmt.Println("Fim de jogo! Os fantasmas venceram... \xF0\x9F\x98\xAD")
}

func main() {
  inicializa()
  defer finaliza()

  mapaSinais = make(map[int]string)
  mapaSinais[0] = "Cima"
  mapaSinais[1] = "Baixo"
  mapaSinais[2] = "Direita"
  mapaSinais[3] = "Esquerda"

  args    := os.Args[1:]
  var arquivo string
  if len(args) >= 1 {
    arquivo = args[0]
  } else {
    arquivo = ""
  }

  _ = construirLabirinto(arquivo)

  canal := make(chan Movimento, 10)

  pacgo.pilula = true

  // Processos assincronos
  go entradaDoUsuario(canal)
  go moverFantasmas()

  var tecla Movimento
  for  {
    atualizarLabirinto()

    // canal não-bloqueador
    select {
    case tecla = <-canal:
        moverPacGo(tecla)
    default:
    }
    if tecla == Sai { break }

    if detectarColisao() {
      if pacgo.pilula {
        _, f := buscaFantasma(pacgo.posicao)
        go criarFantasmaTemporizado(f, 5000)
        matarFantasma(f)
        pacgo.pontos = pacgo.pontos + 500
      } else {
        // pacgo perde vidas
      }
    }

    dorme(100)
  }
}

func buscaFantasma(posicao Posicao) (int, *Fantasma) {
  for i, fantasma := range fantasmas {
    if fantasma.posicao == posicao {
      return i, fantasma
    }
  }
  return -1, nil
}

func criarFantasmaTemporizado(fantasma *Fantasma, milisegundos time.Duration) {
  dorme(milisegundos)
  criarFantasma(fantasma.posicao, fantasma.figura)
}

func matarFantasma(fantasma *Fantasma) {
  pos, _ := buscaFantasma(fantasma.posicao)
  fantasmas = append(fantasmas[:pos], fantasmas[pos+1:]...)
  fmt.Print("\x07")
}
<commit_msg>vidas<commit_after>package main

import (
  "fmt"
  "time"
  "os"
  "bufio"
  "log"
  "regexp"
  "errors"
  "math/rand"
)

type Posicao struct {
  linha  int
  coluna int
}

type PacGo struct {
  posicao    Posicao
  figura     string // emoji
  pilula     bool
  vidas      int
  pontos     int
  invencivel bool
}

type Fantasma struct {
  posicao Posicao
  figura  string // emoji
}

type Labirinto struct {
  largura int
  altura  int
  mapa    []string
  figMuro string
  figSP   string
}

type Movimento int

const (
        Cima = iota
        Baixo
        Esquerda
        Direita
        Nenhum
        Sai
)

var labirinto  *Labirinto
var pacgo      *PacGo
var fantasmas  []*Fantasma
var mapaSinais map[int]string

func criarFantasma(posicao Posicao, figura string) {
  fantasma := &Fantasma{ posicao:posicao, figura: figura}
  fantasmas = append(fantasmas, fantasma)
}

func criarPacGo(posicao Posicao, figura string, pilula bool, vidas int) {
  pacgo = &PacGo{ posicao:posicao, figura: "\xF0\x9F\x98\x83", pilula: false, vidas:3 }
}

func construirLabirinto(nomeArquivo string) error {

  var ErrMapNotFound = errors.New("Não conseguiu ler o arquivo do mapa")

  var arquivo string
  if nomeArquivo == "" {
    arquivo = "./data/mapa.txt"
  } else {
    arquivo = nomeArquivo
  }

  if file, err := os.Open(arquivo); err == nil {

    // fecha depois de ler o arquivo
    defer file.Close()

    // inicializa o mapa vazio
    mapa := []string{}

    r, _ := regexp.Compile("[^ #.P]")

    // cria um leitor para ler linha a linha o arquivo
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
      linha := scanner.Text()

      for indice , caracter := range linha {
        switch caracter {
          case 'F': criarFantasma( Posicao{len(mapa), indice}, "\xF0\x9F\x91\xBB" )
          case 'G': criarPacGo( Posicao{len(mapa), indice}, "\xF0\x9F\x98\x83", false, 3 )
        }
      }

      linha = r.ReplaceAllString(linha, " ")
      mapa = append(mapa, linha)
    }

    // verifica se teve erro o leitor
    if err = scanner.Err(); err != nil {
      log.Fatal(err)
      return ErrMapNotFound
    }

    labirinto = &Labirinto{largura: len(mapa[0]), altura: len(mapa), mapa : mapa, figMuro: "\x1b[44m \x1b[0m", figSP: "\xF0\x9F\x8D\x84"}
    return nil

  } else {
    log.Fatal(err)
    return ErrMapNotFound
  }
}

func atualizarLabirinto() {
  limpaTela()

  // Imprime os pontos
  moveCursor(Posicao{0,0})
  fmt.Printf("%sPontos: %d Vidas: %d%s\n", "\x1b[31;1m", pacgo.pontos, pacgo.vidas, "\x1b[0m")

  posicaoInicial := Posicao{2,0}
  moveCursor(posicaoInicial)
  for _, linha := range labirinto.mapa {
    for _, char := range linha {
      switch char {
        case '#': fmt.Print(labirinto.figMuro)
        case '.': fmt.Print(".")
        case 'P': fmt.Print(labirinto.figSP)
        default:  fmt.Print(" ")
      }
    }
    fmt.Println("")
  }

  // Imprime PacGo
  moveCursor(posicaoInicial.adiciona(&pacgo.posicao))
  fmt.Printf("%s", pacgo.figura)

  // Imprime fantasmas
  for _, fantasma := range fantasmas {
    moveCursor(posicaoInicial.adiciona(&fantasma.posicao))
    fmt.Printf("%s", fantasma.figura)
  }

  // Move o cursor para fora do labirinto
  moveCursor(posicaoInicial.adiciona(&Posicao{labirinto.altura + 2, 0}))
}

func detectarColisao() bool {
  for _, fantasma := range fantasmas {
    if fantasma.posicao == pacgo.posicao {
      return true
    }
  }
  return false
}

func moverPacGo(m Movimento) {
  var novaLinha = pacgo.posicao.linha
  var novaColuna = pacgo.posicao.coluna

  switch m {
    case Cima:
      novaLinha--
      if novaLinha < 0 {
        novaLinha = labirinto.altura - 1
      }
    case Baixo:
      novaLinha++
      if novaLinha >= labirinto.altura {
        novaLinha = 0
      }
    case Direita:
      novaColuna++
      if novaColuna >= labirinto.largura {
        novaColuna = 0
      }
    case Esquerda:
      novaColuna--
      if novaColuna < 0 {
        novaColuna = labirinto.largura - 1
      }
  }

  conteudoDoMapa := labirinto.mapa[novaLinha][novaColuna]
  if conteudoDoMapa != '#' {
    pacgo.posicao.linha = novaLinha
    pacgo.posicao.coluna = novaColuna

    if (conteudoDoMapa == '.') || (conteudoDoMapa == 'P') {
      if (conteudoDoMapa == '.') {
        pacgo.pontos += 10
      } else {
        pacgo.pontos += 100
      }

      linha := labirinto.mapa[novaLinha]
      linha = linha[:novaColuna] + " " + linha[novaColuna+1:]
      labirinto.mapa[novaLinha] = linha
    }
  }
}

func random(min, max int) int {
    return rand.Intn(max - min) + min
}

func move(fantasma *Fantasma, valorDaPosicaoAtualDoFantasma byte, linhaAtualDoFantasma int, colunaAtualDoFantasma int){

  var direcao = random(0, 4)
  var sinal = mapaSinais[direcao]
  //fmt.Println(sinal)
  switch sinal {
  case "Cima":
              if linhaAtualDoFantasma == 0{
                if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = labirinto.altura - 1
                 }
             }else{
               var posicaoAcimaDoFantasma = labirinto.mapa[fantasma.posicao.linha - 1][fantasma.posicao.coluna]
               if posicaoAcimaDoFantasma != '#'{
                 fantasma.posicao.linha = fantasma.posicao.linha - 1
               }
             }
  case "Baixo":
              if linhaAtualDoFantasma == labirinto.altura - 1{
                 if valorDaPosicaoAtualDoFantasma == ' '{
                   fantasma.posicao.linha = 0
                 }
              }else{
                var posicaoAbaixoDoFantasma = labirinto.mapa[fantasma.posicao.linha + 1][fantasma.posicao.coluna]
                if posicaoAbaixoDoFantasma != '#'{
                  fantasma.posicao.linha = fantasma.posicao.linha + 1
                }
              }
  case "Direita":
                if colunaAtualDoFantasma == labirinto.largura-1{
                  if valorDaPosicaoAtualDoFantasma == ' '{
                    fantasma.posicao.coluna = 0
                  }
                }else{
                  var posicaoDireitaDofantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna + 1]
                  if posicaoDireitaDofantasma != '#'{
                    fantasma.posicao.coluna = fantasma.posicao.coluna + 1
                  }
                }
  case "Esquerda":
                 if colunaAtualDoFantasma == 0{
                   if valorDaPosicaoAtualDoFantasma == ' '{
                     fantasma.posicao.coluna = labirinto.largura - 1
                   }
                 }else{
                   var posicaoEsquerdaDoFantasma = labirinto.mapa[fantasma.posicao.linha][fantasma.posicao.coluna - 1]
                   if posicaoEsquerdaDoFantasma != '#'{
                     fantasma.posicao.coluna = fantasma.posicao.coluna - 1
                   }
                 }
  }
}

func moverFantasmas() {

  for {
    for i := 0; i < len(fantasmas); i++{
        var valorDaPosicaoAtualDoFantasma = labirinto.mapa[fantasmas[i].posicao.linha][fantasmas[i].posicao.coluna]
        var linhaAtualDoFantasma = fantasmas[i].posicao.linha
        var colunaAtualDoFantasma = fantasmas[i].posicao.coluna
        //fmt.Println(valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
        move(fantasmas[i], valorDaPosicaoAtualDoFantasma, linhaAtualDoFantasma, colunaAtualDoFantasma)
    }
    dorme(300)
  }
}

func dorme(milisegundos time.Duration) {
  time.Sleep(time.Millisecond * milisegundos)
}

func entradaDoUsuario(canal chan<- Movimento) {
  array := make([]byte, 10)

  for {
    lido, _ := os.Stdin.Read(array)

    if lido == 1 && array[0] == 0x1b {
      canal <- Sai;
    } else if lido == 3 {
      if array[0] == 0x1b && array[1] == '[' {
        switch array[2] {
        case 'A': canal <- Cima
        case 'B': canal <- Baixo
        case 'C': canal <- Direita
        case 'D': canal <- Esquerda
        }
      }
    }
  }
}

func ativarPilula() {
  pacgo.pilula = true
  go desativarPilula(10000)
}

func desativarPilula(milisegundos time.Duration) {
  dorme(milisegundos)
  pacgo.pilula = false
}

func terminarJogo() {
  // pacgo morreu :(
  atualizarLabirinto()
  moveCursor( Posicao{labirinto.altura + 2, 0} )
  fmt.Println("Fim de jogo! Os fantasmas venceram... \xF0\x9F\x98\xAD")
}

func main() {
  inicializa()
  defer finaliza()

  mapaSinais = make(map[int]string)
  mapaSinais[0] = "Cima"
  mapaSinais[1] = "Baixo"
  mapaSinais[2] = "Direita"
  mapaSinais[3] = "Esquerda"

  args    := os.Args[1:]
  var arquivo string
  if len(args) >= 1 {
    arquivo = args[0]
  } else {
    arquivo = ""
  }

  _ = construirLabirinto(arquivo)

  canal := make(chan Movimento, 10)

  // Processos assincronos
  go entradaDoUsuario(canal)
  go moverFantasmas()

  var tecla Movimento
  for  {
    atualizarLabirinto()

    // canal não-bloqueador
    select {
    case tecla = <-canal:
        moverPacGo(tecla)
    default:
    }
    if tecla == Sai { break }

    if detectarColisao() {
      if pacgo.pilula {
        _, f := buscaFantasma(pacgo.posicao)
        go criarFantasmaTemporizado(f, 5000)
        matarFantasma(f)
        pacgo.pontos = pacgo.pontos + 500
      } else {
        // pacgo perde vidas
        if !pacgo.invencivel {
          pacgo.vidas--
          if pacgo.vidas < 0 {
            terminarJogo()
            break
          }
          ativarInvencibilidade(3000)
        }
      }
    }

    dorme(100)
  }
}

func ativarInvencibilidade(milisegundos time.Duration) {
  pacgo.invencivel = true
  go func() {
    dorme(milisegundos)
    pacgo.invencivel = false
  }()
}

func buscaFantasma(posicao Posicao) (int, *Fantasma) {
  for i, fantasma := range fantasmas {
    if fantasma.posicao == posicao {
      return i, fantasma
    }
  }
  return -1, nil
}

func criarFantasmaTemporizado(fantasma *Fantasma, milisegundos time.Duration) {
  dorme(milisegundos)
  criarFantasma(fantasma.posicao, fantasma.figura)
}

func matarFantasma(fantasma *Fantasma) {
  pos, _ := buscaFantasma(fantasma.posicao)
  fantasmas = append(fantasmas[:pos], fantasmas[pos+1:]...)
  fmt.Print("\x07")
}
<|endoftext|>


<commit_before>package main

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

	"github.com/bjwbell/gensimd/codegen"
	"github.com/bjwbell/gensimd/simd"

	"go/build"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/ssa"
	"golang.org/x/tools/go/ssa/ssautil"
	"golang.org/x/tools/go/types"
)

func filePath(pathName string) string {
	split := strings.Split(pathName, "/")
	dir := ""
	if len(split) == 1 {
		dir = "."
	} else if len(split) == 2 {
		dir = split[0] + "/"
	} else {
		dir = strings.Join(split[0:len(split)-2], "/") // + "/"
	}
	return dir
}

func fileName(pathName string) string {
	split := strings.Split(pathName, "/")
	name := ""
	if len(split) == 0 {
		name = ""
	} else {
		name = split[len(split)-1]
	}
	return name
}

func main() {
	var ssaDump = flag.Bool("ssa", false, "dump ssa representation")
	flag.Parse()
	args := flag.Args()
	file := os.ExpandEnv("$GOFILE")
	funcName := ""
	if len(args) == 1 {
		funcName = args[0]

	} else if len(args) == 2 {
		file = args[0]
		funcName = args[1]
	}
	f, err := simd.ParseFile(file)
	if err == nil {
		fmt.Println("Parsed: ", file)
	} else {
		fmt.Println(fmt.Sprintf("Error parsing file \"%v\", error: %v", file, err))
		return
	}

	filePkgName := f.Pkg.Name()
	filePkgPath := f.Pkg.Path()
	conf := loader.Config{Build: &build.Default}

	// Choose types.Sizes from conf.Build.
	var wordSize int64 = 8
	switch conf.Build.GOARCH {
	case "386", "arm":
		panic("SIMD invalid for x86 and arm")
	}
	conf.TypeChecker.Sizes = &types.StdSizes{
		MaxAlign: 8,
		WordSize: wordSize,
	}

	// Use the initial file from the command line/$GOFILE.
	conf.CreateFromFilenames(filePath(file), file)

	// Load, parse and type-check
	iprog, err := conf.Load()
	if err != nil {
		fmt.Println("err:", err)
		return
	}

	// Create and build SSA-form program representation.
	builderMode := ssa.SanityCheckFunctions
	if *ssaDump {
		builderMode = ssa.PrintFunctions
	}
	prog := ssautil.CreateProgram(iprog, builderMode)
	if prog == nil {
		fmt.Println("prog == nil")
	}
	// Build and display only the initial packages (and synthetic wrappers)
	for _, info := range iprog.InitialPackages() {
		prog.Package(info.Pkg).Build()
	}

	// TODO generate assembly instructions

	for _, pkg := range prog.AllPackages() {
		if pkg.Pkg.Path() == filePkgPath+"/" && pkg.Pkg.Name() == filePkgName {
			fmt.Println("found filePkgName:", filePkgName)
			if fn := pkg.Func(funcName); fn == nil {
				fmt.Println(fmt.Printf("Error function \"%v\" not found in \"%v\" package", funcName, filePkgName))
				return
			} else {
				fmt.Println("found function:", funcName)
				codegenFn, err := codegen.CreateFunction("codegen/instructionsetxml/Opcodes/opcodes/x86_64.xml", fn)
				if err != nil {
					fmt.Printf("Error in codegen.CreateFunction,  msg:\"%v\"", err)
				}
				if asm, err := codegenFn.GoAssembly(); err != nil {
					fmt.Println(asm)
					fmt.Printf("Error creating fn asm, msg:\"%v\"\n", err)
				} else {
					fmt.Println("fn asm:")
					fmt.Println(asm)
				}
			}
		}
	}
}
<commit_msg>Add flag, "-o", to output assembly to a file<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/bjwbell/gensimd/codegen"
	"github.com/bjwbell/gensimd/simd"

	"go/build"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/ssa"
	"golang.org/x/tools/go/ssa/ssautil"
	"golang.org/x/tools/go/types"
)

func filePath(pathName string) string {
	split := strings.Split(pathName, "/")
	dir := ""
	if len(split) == 1 {
		dir = "."
	} else if len(split) == 2 {
		dir = split[0] + "/"
	} else {
		dir = strings.Join(split[0:len(split)-2], "/") // + "/"
	}
	return dir
}

func fileName(pathName string) string {
	split := strings.Split(pathName, "/")
	name := ""
	if len(split) == 0 {
		name = ""
	} else {
		name = split[len(split)-1]
	}
	return name
}

func main() {
	var ssaDump = flag.Bool("ssa", false, "dump ssa representation")
	var outputFile = flag.String("o", "", "write Go Assembly to file")
	flag.Parse()
	args := flag.Args()
	file := os.ExpandEnv("$GOFILE")
	funcName := ""
	if len(args) == 1 {
		funcName = args[0]

	} else if len(args) == 2 {
		file = args[0]
		funcName = args[1]
	}
	f, err := simd.ParseFile(file)
	if err == nil {
		fmt.Println("Parsed: ", file)
	} else {
		fmt.Println(fmt.Sprintf("Error parsing file \"%v\", error: %v", file, err))
		return
	}

	filePkgName := f.Pkg.Name()
	filePkgPath := f.Pkg.Path()
	conf := loader.Config{Build: &build.Default}

	// Choose types.Sizes from conf.Build.
	var wordSize int64 = 8
	switch conf.Build.GOARCH {
	case "386", "arm":
		panic("SIMD invalid for x86 and arm")
	}
	conf.TypeChecker.Sizes = &types.StdSizes{
		MaxAlign: 8,
		WordSize: wordSize,
	}

	// Use the initial file from the command line/$GOFILE.
	conf.CreateFromFilenames(filePath(file), file)

	// Load, parse and type-check
	iprog, err := conf.Load()
	if err != nil {
		fmt.Println("err:", err)
		return
	}

	// Create and build SSA-form program representation.
	builderMode := ssa.SanityCheckFunctions
	if *ssaDump {
		builderMode = ssa.PrintFunctions
	}
	prog := ssautil.CreateProgram(iprog, builderMode)
	if prog == nil {
		fmt.Println("prog == nil")
	}
	// Build and display only the initial packages (and synthetic wrappers)
	for _, info := range iprog.InitialPackages() {
		prog.Package(info.Pkg).Build()
	}

	// TODO generate assembly instructions

	for _, pkg := range prog.AllPackages() {
		if pkg.Pkg.Path() == filePkgPath+"/" && pkg.Pkg.Name() == filePkgName {
			fmt.Println("found filePkgName:", filePkgName)
			if fn := pkg.Func(funcName); fn == nil {
				fmt.Println(fmt.Printf("Error function \"%v\" not found in \"%v\" package", funcName, filePkgName))
				return
			} else {
				fmt.Println("found function:", funcName)
				codegenFn, err := codegen.CreateFunction("codegen/instructionsetxml/Opcodes/opcodes/x86_64.xml", fn)
				if err != nil {
					fmt.Printf("Error in codegen.CreateFunction,  msg:\"%v\"", err)
				}
				if asm, err := codegenFn.GoAssembly(); err != nil {
					fmt.Println(asm)
					fmt.Printf("Error creating fn asm, msg:\"%v\"\n", err)
				} else {
					if *outputFile == "" {
						fmt.Println(asm)
					} else {
						writeFile(*outputFile, asm)
					}
				}
			}
		}
	}
}

func writeFile(filename, contents string) {

	if err := ioutil.WriteFile(filename, []byte(contents), 0644); err != nil {
		fmt.Fprintf(os.Stderr, "Error writing to file, error msg \"%v\"\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/kelseyhightower/envconfig"
	"github.com/therealbill/airbrake-go"
	"github.com/therealbill/redskull/actions"
	"github.com/therealbill/redskull/handlers"
	"github.com/zenazn/goji"
)

var Build string
var key string

type ConstellationConfig struct {
	Nodes           []string
	DeadNodes       []string
	ConnectionCount int64
}

func RefreshData() {
	t := time.Tick(60 * time.Second)
	for _ = range t {
		handlers.ManagedConstellation.LoadSentinelConfigFile()
		handlers.ManagedConstellation.GetAllSentinels()
		for _, pod := range handlers.ManagedConstellation.RemotePodMap {
			_, _ = handlers.ManagedConstellation.LocalPodMap[pod.Name]
			//if islocal { continue }
			auth := handlers.ManagedConstellation.GetPodAuth(pod.Name)
			if pod.AuthToken != auth && auth > "" {
				pod.AuthToken = auth
			}
			/*
				for _, node := range pod.Nodes {
					node.Auth = pod.AuthToken
					handlers.NodeMaster.AddNode(node)
					node.UpdateData()
				}
			*/
		}
		for _, pod := range handlers.ManagedConstellation.LocalPodMap {
			pod.AuthToken = handlers.ManagedConstellation.GetPodAuth(pod.Name)
			//for _, node := range pod.Nodes { node.Auth = pod.AuthToken node.UpdateData() }
		}
		handlers.ManagedConstellation.IsBalanced()
		log.Printf("Main Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetStats())
		log.Printf("Hot Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetHotStats())
	}
}

type LaunchConfig struct {
	Name               string
	Port               int
	SentinelConfigFile string
	GroupName          string
}

func init() {
	var config LaunchConfig
	err := envconfig.Process("redskull", &config)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Launch Config: %+v", config)
	if config.Port == 0 {
		log.Print("ENV contained no port, using default")
		flag.Set("bind", ":8000")
	} else {
		ps := fmt.Sprintf(":%d", config.Port)
		_ = ps
		//flag.Set("bind", ps)
	}
	// handle absent sentinel config file w/a default
	if config.SentinelConfigFile == "" {
		log.Print("ENV contained no SentinelConfigFile, using default")
		config.SentinelConfigFile = "/etc/redis/sentinel.conf"
	}

	// handle absent sentinel config file w/a default
	if config.GroupName == "" {
		config.GroupName = "redskull:1"
		log.Print("ENV contained no GroupName, using default:" + config.GroupName)
	}

	mc, err := actions.GetConstellation(config.Name, config.SentinelConfigFile, config.GroupName)
	if err != nil {
		log.Fatal("Unable to connect to constellation")
	}
	log.Print("Starting refresh ticker")

	// Try to set to the IP the sentinel is bound do
	//flag.Set("bind", mc.SentinelConfig.Host+":8000")

	go RefreshData()
	for _, pod := range mc.PodMap {
		handlers.NodeMaster.AddNode(pod.Master)
		//for _, node := range pod.Nodes { handlers.NodeMaster.AddNode(&node) }
	}
	mc.IsBalanced()
	handlers.ManagedConstellation = mc
	_ = handlers.NewPageContext()
	log.Printf("Main Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetStats())
	log.Printf("Hot Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetHotStats())
	//log.Printf("MC:%+v", handlers.ManagedConstellation)
	key = os.Getenv("AIRBRAKE_API_KEY")
	airbrake.Endpoint = "https://api.airbrake.io/notifier_api/v2/notices"
	airbrake.ApiKey = key
	airbrake.Environment = os.Getenv("RSM_ENVIRONMENT")
	if len(airbrake.Environment) == 0 {
		airbrake.Environment = "Development"
	}
	if len(Build) == 0 {
		Build = ".1"
		return
	}
}

func main() {

	// HTML Interface URLS
	goji.Get("/constellation/", handlers.ConstellationInfoHTML) // Needs moved? instance tree?
	goji.Get("/dashboard/", handlers.Dashboard)                 // Needs moved? instance tree?
	goji.Get("/constellation/addpodform/", handlers.AddPodForm)
	goji.Post("/constellation/addpod/", handlers.AddPodHTML)
	goji.Post("/constellation/addsentinel/", handlers.AddSentinelHTML)
	goji.Get("/constellation/addsentinelform/", handlers.AddSentinelForm)
	goji.Get("/constellation/rebalance/", handlers.RebalanceHTML)
	//goji.Get("/pod/:podName/dropslave", handlers.DropSlaveHTML)
	goji.Get("/pod/:podName/addslave", handlers.AddSlaveHTML)
	goji.Post("/pod/:podName/addslave", handlers.AddSlaveHTMLProcessor)
	goji.Post("/pod/:name/failover", handlers.DoFailoverHTML)
	goji.Post("/pod/:name/reset", handlers.ResetPodProcessor)
	goji.Post("/pod/:name/balance", handlers.BalancePodProcessor)
	goji.Get("/pod/:podName", handlers.ShowPod)
	goji.Get("/pods/", handlers.ShowPods)
	goji.Get("/nodes/", handlers.ShowNodes)
	goji.Get("/node/:name", handlers.ShowNode)
	goji.Get("/", handlers.Root) // Needs moved? instance tree?

	// API URLS
	goji.Get("/api/knownpods", handlers.APIGetPods)
	goji.Put("/api/monitor/:podName", handlers.APIMonitorPod)
	goji.Post("/api/constellation/:podName/failover", handlers.APIFailover)

	goji.Get("/api/pod/:podName", handlers.APIGetPod)
	goji.Put("/api/pod/:podName", handlers.APIMonitorPod)
	goji.Put("/api/pod/:podName/addslave", handlers.APIAddSlave)
	goji.Delete("/api/pod/:podName", handlers.APIRemovePod)
	goji.Get("/api/pod/:podName/master", handlers.APIGetMaster)
	goji.Get("/api/pod/:podName/slaves", handlers.APIGetSlaves)

	goji.Post("/api/node/clone", handlers.Clone) // Needs moved to the node tree
	goji.Get("/api/node/:name", handlers.GetNodeJSON)

	goji.Get("/static/*", handlers.Static) // Needs moved? instance tree?
	//goji.Abandon(middleware.Logger)

	goji.Serve()

}
<commit_msg>adding more ENV variables for config control<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/kelseyhightower/envconfig"
	"github.com/therealbill/airbrake-go"
	"github.com/therealbill/redskull/actions"
	"github.com/therealbill/redskull/handlers"
	"github.com/zenazn/goji"
)

var Build string
var key string

type ConstellationConfig struct {
	Nodes           []string
	DeadNodes       []string
	ConnectionCount int64
}

func RefreshData() {
	t := time.Tick(60 * time.Second)
	for _ = range t {
		handlers.ManagedConstellation.LoadSentinelConfigFile()
		handlers.ManagedConstellation.GetAllSentinels()
		for _, pod := range handlers.ManagedConstellation.RemotePodMap {
			_, _ = handlers.ManagedConstellation.LocalPodMap[pod.Name]
			auth := handlers.ManagedConstellation.GetPodAuth(pod.Name)
			if pod.AuthToken != auth && auth > "" {
				pod.AuthToken = auth
			}
		}
		for _, pod := range handlers.ManagedConstellation.LocalPodMap {
			pod.AuthToken = handlers.ManagedConstellation.GetPodAuth(pod.Name)
		}
		handlers.ManagedConstellation.IsBalanced()
		log.Printf("Main Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetStats())
		log.Printf("Hot Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetHotStats())
	}
}

type LaunchConfig struct {
	Name                string
	Port                int
	IP                  string
	SentinelConfigFile  string
	GroupName           string
	BindAddress         string
	SentinelHostAddress string
	SentinelPort        int
}

var config LaunchConfig

func init() {
	err := envconfig.Process("redskull", &config)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Launch Config: %+v", config)
	if config.BindAddress > "" {
		flag.Set("bind", config.BindAddress)
	} else {
		if config.Port == 0 {
			log.Print("ENV contained no port, using default")
			config.Port = 8000
		}
	}
	ps := fmt.Sprintf("%s:%d", config.IP, config.Port)
	log.Printf("binding to '%s'", ps)
	flag.Set("bind", ps)
	// handle absent sentinel config file w/a default
	if config.SentinelConfigFile == "" {
		log.Print("ENV contained no SentinelConfigFile, using default")
		config.SentinelConfigFile = "/etc/redis/sentinel.conf"
	}

	// handle absent sentinel config file w/a default
	if config.GroupName == "" {
		config.GroupName = "redskull:1"
		log.Print("ENV contained no GroupName, using default:" + config.GroupName)
	}

	key = os.Getenv("AIRBRAKE_API_KEY")
	airbrake.Endpoint = "https://api.airbrake.io/notifier_api/v2/notices"
	airbrake.ApiKey = key
	airbrake.Environment = os.Getenv("RSM_ENVIRONMENT")
	if len(airbrake.Environment) == 0 {
		airbrake.Environment = "Development"
	}
	if len(Build) == 0 {
		Build = ".1"
		return
	}
}

func main() {
	mc, err := actions.GetConstellation(config.Name, config.SentinelConfigFile, config.GroupName)
	if err != nil {
		log.Fatal("Unable to connect to constellation")
	}
	log.Print("Starting refresh ticker")

	// Try to set to the IP the sentinel is bound do
	//flag.Set("bind", mc.SentinelConfig.Host+":8000")

	go RefreshData()
	for _, pod := range mc.PodMap {
		handlers.NodeMaster.AddNode(pod.Master)
		//for _, node := range pod.Nodes { handlers.NodeMaster.AddNode(&node) }
	}
	mc.IsBalanced()
	handlers.ManagedConstellation = mc
	_ = handlers.NewPageContext()
	log.Printf("Main Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetStats())
	log.Printf("Hot Cache Stats: %+v", handlers.ManagedConstellation.AuthCache.GetHotStats())
	//log.Printf("MC:%+v", handlers.ManagedConstellation)

	// HTML Interface URLS
	goji.Get("/constellation/", handlers.ConstellationInfoHTML) // Needs moved? instance tree?
	goji.Get("/dashboard/", handlers.Dashboard)                 // Needs moved? instance tree?
	goji.Get("/constellation/addpodform/", handlers.AddPodForm)
	goji.Post("/constellation/addpod/", handlers.AddPodHTML)
	goji.Post("/constellation/addsentinel/", handlers.AddSentinelHTML)
	goji.Get("/constellation/addsentinelform/", handlers.AddSentinelForm)
	goji.Get("/constellation/rebalance/", handlers.RebalanceHTML)
	//goji.Get("/pod/:podName/dropslave", handlers.DropSlaveHTML)
	goji.Get("/pod/:podName/addslave", handlers.AddSlaveHTML)
	goji.Post("/pod/:podName/addslave", handlers.AddSlaveHTMLProcessor)
	goji.Post("/pod/:name/failover", handlers.DoFailoverHTML)
	goji.Post("/pod/:name/reset", handlers.ResetPodProcessor)
	goji.Post("/pod/:name/balance", handlers.BalancePodProcessor)
	goji.Get("/pod/:podName", handlers.ShowPod)
	goji.Get("/pods/", handlers.ShowPods)
	goji.Get("/nodes/", handlers.ShowNodes)
	goji.Get("/node/:name", handlers.ShowNode)
	goji.Get("/", handlers.Root) // Needs moved? instance tree?

	// API URLS
	goji.Get("/api/knownpods", handlers.APIGetPods)
	goji.Put("/api/monitor/:podName", handlers.APIMonitorPod)
	goji.Post("/api/constellation/:podName/failover", handlers.APIFailover)

	goji.Get("/api/pod/:podName", handlers.APIGetPod)
	goji.Put("/api/pod/:podName", handlers.APIMonitorPod)
	goji.Put("/api/pod/:podName/addslave", handlers.APIAddSlave)
	goji.Delete("/api/pod/:podName", handlers.APIRemovePod)
	goji.Get("/api/pod/:podName/master", handlers.APIGetMaster)
	goji.Get("/api/pod/:podName/slaves", handlers.APIGetSlaves)

	goji.Post("/api/node/clone", handlers.Clone) // Needs moved to the node tree
	goji.Get("/api/node/:name", handlers.GetNodeJSON)

	goji.Get("/static/*", handlers.Static) // Needs moved? instance tree?
	//goji.Abandon(middleware.Logger)

	goji.Serve()

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/types"
	"log"
	"os"

	"golang.org/x/tools/go/loader"
)

func main() {
	var conf loader.Config
	_, err := conf.FromArgs(os.Args[1:], false)
	if err != nil {
		log.Fatal(err)
	}
	program, err := conf.Load()
	if err != nil {
		log.Fatal(err)
	}
	for _, createdPackage := range program.Imported {
		walkPackage(program, createdPackage)
	}
	for _, createdPackage := range program.Created {
		walkPackage(program, createdPackage)
	}
}

func walkPackage(program *loader.Program, info *loader.PackageInfo) {
	for _, file := range info.Files {
		v := &visitor{
			program,
			info,
		}
		ast.Walk(v, file)
	}
}

type visitor struct {
	program *loader.Program
	pkg     *loader.PackageInfo
}

func isMap(t types.Type) bool {
	_, ok := t.(*types.Map)
	return ok
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	switch stmt := node.(type) {
	case *ast.RangeStmt:
		if isMap(v.pkg.TypeOf(stmt.X)) {
			fmt.Println(v.program.Fset.Position(node.Pos()))
		}
	default:
	}
	return v
}
<commit_msg>Simplify Visit<commit_after>package main

import (
	"fmt"
	"go/ast"
	"go/types"
	"log"
	"os"

	"golang.org/x/tools/go/loader"
)

func main() {
	var conf loader.Config
	_, err := conf.FromArgs(os.Args[1:], false)
	if err != nil {
		log.Fatal(err)
	}
	program, err := conf.Load()
	if err != nil {
		log.Fatal(err)
	}
	for _, createdPackage := range program.Imported {
		walkPackage(program, createdPackage)
	}
	for _, createdPackage := range program.Created {
		walkPackage(program, createdPackage)
	}
}

func walkPackage(program *loader.Program, info *loader.PackageInfo) {
	for _, file := range info.Files {
		v := &visitor{
			program,
			info,
		}
		ast.Walk(v, file)
	}
}

type visitor struct {
	program *loader.Program
	pkg     *loader.PackageInfo
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	if stmt, isRange := node.(*ast.RangeStmt); isRange {
		if _, isMap := v.pkg.TypeOf(stmt.X).(*types.Map); isMap {
			fmt.Println(v.program.Fset.Position(node.Pos()))
		}
	}
	return v
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/alienth/fastlyctl/util"
	"github.com/alienth/go-fastly"
	"github.com/juju/ratelimit"
	"github.com/urfave/cli"
	"gopkg.in/mcuadros/go-syslog.v2"
)

// getIPs takes in an haproxy log line and returns the client IP and the CDN
// connecting IP which is in the captured request header. For us, the CDN
// connecting IP happens to be the 8th captured request header.
func getIPs(logLine string) (*net.IP, *net.IP) {
	if logLine == "" {
		return nil, nil
	}
	// This string parsing stuff was lifted from TPS
	var a, b int
	if a = strings.Index(logLine, "]:") + 3; a == -1 {
		return nil, nil
	}
	if b = strings.Index(logLine[a:], ":"); b == -1 {
		return nil, nil
	}
	clientIPString := logLine[a : a+b]
	clientIP := net.ParseIP(clientIPString)
	if clientIP == nil {
		return nil, nil
	}

	logLine = logLine[a+b:]
	// The first curly-braced block contains our request headers
	if a = strings.Index(logLine, "{"); a == -1 {
		return &clientIP, nil
	}
	if b = strings.Index(logLine[a:], "}"); b == -1 {
		return &clientIP, nil
	}
	bracketedHeaders := logLine[a : a+b]
	headers := strings.Split(bracketedHeaders, "|")
	if len(headers) < 7 {
		return &clientIP, nil
	}
	ipString := headers[7]
	cdnIP := net.ParseIP(ipString)
	if cdnIP == nil {
		return &clientIP, nil
	}
	return &clientIP, &cdnIP
}

func getLogTimestamp(logLine string) int64 {
	timestampStr := strings.Split(logLine, " ")[6]
	ts, _ := time.Parse("[02/Jan/2006:15:04:05.999]", timestampStr)

	return (ts.Unix())
}

type ipList struct {
	ips  []net.IP
	nets []net.IPNet
}

func readIPList(filename string) (*ipList, error) {
	var list ipList
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(strings.TrimSpace(line), "#") == 0 {
			continue
		}
		if strings.TrimSpace(line) == "" {
			continue
		}
		if strings.Contains(line, "/") {
			_, n, err := net.ParseCIDR(line)
			if err != nil {
				return nil, fmt.Errorf("Unable to parse CIDR.\nLine:\n%s\nError:\n%s\n", line, err)
			}
			if n != nil {
				list.nets = append(list.nets, *n)
			}
		} else {
			ip := net.ParseIP(line)
			if ip != nil {
				list.ips = append(list.ips, ip)
			} else {
				return nil, fmt.Errorf("Unable to parse IP address in list: %s\n", line)
			}
		}

	}
	return &list, nil
}

func (l *ipList) contains(checkIP *net.IP) bool {
	if l == nil {
		return false
	}
	for _, ip := range l.ips {
		if ip.Equal(*checkIP) {
			return true
		}
	}
	for _, net := range l.nets {
		if net.Contains(*checkIP) {
			return true
		}
	}

	return false
}

type ipRate struct {
	ip      *net.IP
	bucket  *ratelimit.Bucket
	entries []*util.ACLEntry

	FirstHit    int64 `json:"first_hit,omitempty"`
	LastHit     int64 `json:"last_hit,omitempty"`
	LastLimit   int64 `json:"last_limit,omitempty"`
	Hits        int   `json:"hits,omitempty"`
	Strikes     int   `json:"strikes,omitempty"`
	Expire      int64 `json:"-"`
	LimitExpire int64 `json:"limit_expire,omitempty"`
}

func (ipr *ipRate) New(ip *net.IP) {
	ipr.ip = ip
	ipr.bucket = ratelimit.NewBucket(time.Duration(3)*time.Minute, 180)
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
}

// Records a hit and returns true if it is over limit.
func (ipr *ipRate) Hit() bool {
	_, isSoonerThanMaxWait := ipr.bucket.TakeMaxDuration(1, 0)
	if ipr.FirstHit == 0 {
		ipr.FirstHit = time.Now().Unix()
	}
	ipr.LastHit = time.Now().Unix()
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
	return isSoonerThanMaxWait
}

// Limit adds an IP to a fastly edge ACL
func (ipr *ipRate) Limit() error {

	// remove me
	if time.Now().Unix()-ipr.LastLimit < 3600 {
		return nil
	}

	service, err := util.GetServiceByNameOrID(client, "teststackoverflow.com")
	if err != nil {
		return err
	}

	acl, err := util.NewACL(client, service.ID, "ratelimit")
	if err != nil {
		return err
	}

	// TODO this is heavy.. cache? do we even need?(see below)
	entries, err := acl.ListEntries()
	if err != nil {
		return err
	}

	// TODO This won't typically happen in normal conditions.. should we care?
	for _, e := range entries {
		if ipr.ip.String() == e.IP {
			fmt.Printf("IP %s is already limited.\n", e.IP)
			json.Unmarshal([]byte(e.Comment), ipr)
			ipr.LastLimit = time.Now().Unix()
			ipr.Strikes += 1
			ipr.Expire = time.Now().Add(time.Duration(24) * time.Hour).Unix()
			comment, err := json.Marshal(ipr)
			if err != nil {
				return err
			}
			entry := &util.ACLEntry{Client: client, ID: e.ID, ACLID: e.ACLID, ServiceID: service.ID, Comment: string(comment)}
			ipr.entries = append(ipr.entries, entry)
			// TODO: Update the entry?
			return nil
		}
	}

	fmt.Printf("Limiting IP %s\n", ipr.ip.String())
	ipr.LastLimit = time.Now().Unix()
	ipr.Strikes += 1
	ipr.LimitExpire = time.Now().Add(time.Duration(5) * time.Minute).Unix()
	ipr.Expire = time.Now().Add(time.Duration(24) * time.Hour).Unix()
	comment, err := json.Marshal(ipr)
	if err != nil {
		return err
	}
	entry, err := util.NewACLEntry(client, service.ID, "ratelimit", ipr.ip.String(), 0, string(comment), false)
	if err != nil {
		return err
	}
	entry.Add()
	ipr.entries = append(ipr.entries, entry)
	return nil
}

// Removes an IP from ratelimits
func (ipr *ipRate) RemoveLimit() error {
	if len(ipr.entries) > 0 {
		fmt.Printf("Unlimiting IP %s\n", ipr.ip.String())
		for i, entry := range ipr.entries {
			entry.Remove()
			ipr.entries[i] = nil
		}
		ipr.entries = ipr.entries[:0]
	}
	return nil
}

type hitMap struct {
	sync.RWMutex
	m map[string]*ipRate
}

func expireRecords(hits *hitMap) {
	for {
		hits.Lock()
		for ip, ipr := range hits.m {
			if ipr.Expire < time.Now().Unix() {
				ipr.RemoveLimit()
				delete(hits.m, ip)
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(60) * time.Second)
	}
}

func expireLimits(hits *hitMap) {
	for {
		hits.Lock()
		for _, ipr := range hits.m {
			if ipr.LimitExpire < time.Now().Unix() {
				ipr.RemoveLimit()
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(15) * time.Second)
	}
}

var client *fastly.Client

func main() {
	app := cli.NewApp()
	app.Name = "fastly-ratelimit"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "whitelist, w",
			Usage: "Read `FILE` containing addresses which should whitelisted from any bans.",
		},
		cli.StringFlag{
			Name:  "listen, l",
			Usage: "Specify listen `ADDRESS:PORT`.",
			Value: "0.0.0.0:514",
		},
		cli.StringFlag{
			Name:   "fastly-key, K",
			Usage:  "Fastly API Key. Can be read from 'fastly_key' file in CWD.",
			EnvVar: "FASTLY_KEY",
			Value:  util.GetFastlyKey(),
		},
	}
	app.Action = func(c *cli.Context) error {
		client, _ = fastly.NewClient(c.GlobalString("fastly-key"))
		channel := make(syslog.LogPartsChannel)
		handler := syslog.NewChannelHandler(channel)

		server := syslog.NewServer()
		server.SetFormat(syslog.RFC3164)
		server.SetHandler(handler)
		if err := server.ListenUDP(c.GlobalString("listen")); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to listen: %s\n", err), -1)
		}
		if err := server.Boot(); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to start server: %s\n", err), -1)
		}

		var whitelist *ipList
		var err error
		whitelistFile := c.GlobalString("whitelist")
		if whitelistFile != "" {
			if whitelist, err = readIPList(whitelistFile); err != nil {
				return cli.NewExitError(fmt.Sprintf("Error reading whitelist file: %s", err), -1)
			}
		}

		var hits = hitMap{m: make(map[string]*ipRate)}
		go expireRecords(&hits)
		go expireLimits(&hits)
		go func(channel syslog.LogPartsChannel) {
			for logParts := range channel {
				var line string
				var ok bool
				if line, ok = logParts["content"].(string); !ok || line == "" {
					continue
				}
				clientIP, cdnIP := getIPs(line)
				if cdnIP == nil || clientIP == nil {
					continue
				}
				var ipr *ipRate
				var found bool
				hits.Lock()
				if ipr, found = hits.m[cdnIP.String()]; !found {
					ipr = &ipRate{}
					ipr.New(cdnIP)
					hits.m[cdnIP.String()] = ipr
				}
				hits.Unlock()
				overLimit := ipr.Hit()
				if !overLimit {
					if whitelist.contains(cdnIP) {
						//fmt.Println("but is whitelisted so we don't care.")
					} else {
						if err := ipr.Limit(); err != nil {
							fmt.Printf("Error limiting IP: %s", err)
						}
					}
				}
			}
		}(channel)

		server.Wait()

		return nil
	}
	app.Run(os.Args)
}
<commit_msg>Refactor a bit to support existing ACL entries better.<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/alienth/fastlyctl/util"
	"github.com/alienth/go-fastly"
	"github.com/juju/ratelimit"
	"github.com/urfave/cli"
	"gopkg.in/mcuadros/go-syslog.v2"
)

// getIPs takes in an haproxy log line and returns the client IP and the CDN
// connecting IP which is in the captured request header. For us, the CDN
// connecting IP happens to be the 8th captured request header.
func getIPs(logLine string) (*net.IP, *net.IP) {
	if logLine == "" {
		return nil, nil
	}
	// This string parsing stuff was lifted from TPS
	var a, b int
	if a = strings.Index(logLine, "]:") + 3; a == -1 {
		return nil, nil
	}
	if b = strings.Index(logLine[a:], ":"); b == -1 {
		return nil, nil
	}
	clientIPString := logLine[a : a+b]
	clientIP := net.ParseIP(clientIPString)
	if clientIP == nil {
		return nil, nil
	}

	logLine = logLine[a+b:]
	// The first curly-braced block contains our request headers
	if a = strings.Index(logLine, "{"); a == -1 {
		return &clientIP, nil
	}
	if b = strings.Index(logLine[a:], "}"); b == -1 {
		return &clientIP, nil
	}
	bracketedHeaders := logLine[a : a+b]
	headers := strings.Split(bracketedHeaders, "|")
	if len(headers) < 7 {
		return &clientIP, nil
	}
	ipString := headers[7]
	cdnIP := net.ParseIP(ipString)
	if cdnIP == nil {
		return &clientIP, nil
	}
	return &clientIP, &cdnIP
}

func getLogTimestamp(logLine string) int64 {
	timestampStr := strings.Split(logLine, " ")[6]
	ts, _ := time.Parse("[02/Jan/2006:15:04:05.999]", timestampStr)

	return (ts.Unix())
}

type ipList struct {
	ips  []net.IP
	nets []net.IPNet
}

func readIPList(filename string) (*ipList, error) {
	var list ipList
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(strings.TrimSpace(line), "#") == 0 {
			continue
		}
		if strings.TrimSpace(line) == "" {
			continue
		}
		if strings.Contains(line, "/") {
			_, n, err := net.ParseCIDR(line)
			if err != nil {
				return nil, fmt.Errorf("Unable to parse CIDR.\nLine:\n%s\nError:\n%s\n", line, err)
			}
			if n != nil {
				list.nets = append(list.nets, *n)
			}
		} else {
			ip := net.ParseIP(line)
			if ip != nil {
				list.ips = append(list.ips, ip)
			} else {
				return nil, fmt.Errorf("Unable to parse IP address in list: %s\n", line)
			}
		}

	}
	return &list, nil
}

func (l *ipList) contains(checkIP *net.IP) bool {
	if l == nil {
		return false
	}
	for _, ip := range l.ips {
		if ip.Equal(*checkIP) {
			return true
		}
	}
	for _, net := range l.nets {
		if net.Contains(*checkIP) {
			return true
		}
	}

	return false
}

type ipRate struct {
	ip      *net.IP
	bucket  *ratelimit.Bucket
	entries []*util.ACLEntry

	FirstHit    int64 `json:"first_hit,omitempty"`
	LastHit     int64 `json:"last_hit,omitempty"`
	LastLimit   int64 `json:"last_limit,omitempty"`
	Hits        int   `json:"hits,omitempty"`
	Strikes     int   `json:"strikes,omitempty"`
	Expire      int64 `json:"-"`
	LimitExpire int64 `json:"limit_expire,omitempty"`
}

func (ipr *ipRate) New(ip *net.IP) {
	ipr.ip = ip
	ipr.bucket = ratelimit.NewBucket(time.Duration(3)*time.Minute, 180)
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
}

// Records a hit and returns true if it is over limit.
func (ipr *ipRate) Hit() bool {
	_, isSoonerThanMaxWait := ipr.bucket.TakeMaxDuration(1, 0)
	if ipr.FirstHit == 0 {
		ipr.FirstHit = time.Now().Unix()
	}
	ipr.LastHit = time.Now().Unix()
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
	return isSoonerThanMaxWait
}

// Limit adds an IP to a fastly edge ACL
func (ipr *ipRate) Limit() error {

	// remove me
	if time.Now().Unix()-ipr.LastLimit < 3600 {
		return nil
	}

	service, err := util.GetServiceByNameOrID(client, "teststackoverflow.com")
	if err != nil {
		return err
	}

	acl, err := util.NewACL(client, service.ID, "ratelimit")
	if err != nil {
		return err
	}

	// TODO this is heavy.. cache? do we even need?(see below)
	entries, err := acl.ListEntries()
	if err != nil {
		return err
	}

	var entry *util.ACLEntry

	for _, e := range entries {
		if ipr.ip.String() == e.IP {
			fmt.Printf("IP %s is already limited.\n", e.IP)
			json.Unmarshal([]byte(e.Comment), ipr)
			entry = &util.ACLEntry{Client: client, ID: e.ID, ACLID: e.ACLID, ServiceID: service.ID}
			continue
		}
	}

	ipr.LastLimit = time.Now().Unix()
	ipr.Strikes += 1
	ipr.LimitExpire = time.Now().Add(time.Duration(5) * time.Minute).Unix()
	ipr.Expire = time.Now().Add(time.Duration(24) * time.Hour).Unix()
	comment, err := json.Marshal(ipr)
	if err != nil {
		return err
	}
	if entry == nil {
		entry, err := util.NewACLEntry(client, service.ID, "ratelimit", ipr.ip.String(), 0, string(comment), false)
		if err != nil {
			return err
		}
		entry.Add()
	} else {
		entry.Comment = string(comment)
		//		entry.Update()
	}

	ipr.entries = append(ipr.entries, entry)
	return nil
}

// Removes an IP from ratelimits
func (ipr *ipRate) RemoveLimit() error {
	if len(ipr.entries) > 0 {
		fmt.Printf("Unlimiting IP %s\n", ipr.ip.String())
		for i, entry := range ipr.entries {
			entry.Remove()
			ipr.entries[i] = nil
		}
		ipr.entries = ipr.entries[:0]
	}
	return nil
}

type hitMap struct {
	sync.RWMutex
	m map[string]*ipRate
}

func expireRecords(hits *hitMap) {
	for {
		hits.Lock()
		for ip, ipr := range hits.m {
			if ipr.Expire < time.Now().Unix() {
				ipr.RemoveLimit()
				delete(hits.m, ip)
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(60) * time.Second)
	}
}

func expireLimits(hits *hitMap) {
	for {
		hits.Lock()
		for _, ipr := range hits.m {
			if ipr.LimitExpire < time.Now().Unix() {
				ipr.RemoveLimit()
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(15) * time.Second)
	}
}

var client *fastly.Client

func main() {
	app := cli.NewApp()
	app.Name = "fastly-ratelimit"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "whitelist, w",
			Usage: "Read `FILE` containing addresses which should whitelisted from any bans.",
		},
		cli.StringFlag{
			Name:  "listen, l",
			Usage: "Specify listen `ADDRESS:PORT`.",
			Value: "0.0.0.0:514",
		},
		cli.StringFlag{
			Name:   "fastly-key, K",
			Usage:  "Fastly API Key. Can be read from 'fastly_key' file in CWD.",
			EnvVar: "FASTLY_KEY",
			Value:  util.GetFastlyKey(),
		},
	}
	app.Action = func(c *cli.Context) error {
		client, _ = fastly.NewClient(c.GlobalString("fastly-key"))
		channel := make(syslog.LogPartsChannel)
		handler := syslog.NewChannelHandler(channel)

		server := syslog.NewServer()
		server.SetFormat(syslog.RFC3164)
		server.SetHandler(handler)
		if err := server.ListenUDP(c.GlobalString("listen")); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to listen: %s\n", err), -1)
		}
		if err := server.Boot(); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to start server: %s\n", err), -1)
		}

		var whitelist *ipList
		var err error
		whitelistFile := c.GlobalString("whitelist")
		if whitelistFile != "" {
			if whitelist, err = readIPList(whitelistFile); err != nil {
				return cli.NewExitError(fmt.Sprintf("Error reading whitelist file: %s", err), -1)
			}
		}

		var hits = hitMap{m: make(map[string]*ipRate)}
		go expireRecords(&hits)
		go expireLimits(&hits)
		go func(channel syslog.LogPartsChannel) {
			for logParts := range channel {
				var line string
				var ok bool
				if line, ok = logParts["content"].(string); !ok || line == "" {
					continue
				}
				clientIP, cdnIP := getIPs(line)
				if cdnIP == nil || clientIP == nil {
					continue
				}
				var ipr *ipRate
				var found bool
				hits.Lock()
				if ipr, found = hits.m[cdnIP.String()]; !found {
					ipr = &ipRate{}
					ipr.New(cdnIP)
					hits.m[cdnIP.String()] = ipr
				}
				hits.Unlock()
				overLimit := ipr.Hit()
				if !overLimit {
					if whitelist.contains(cdnIP) {
						//fmt.Println("but is whitelisted so we don't care.")
					} else {
						if err := ipr.Limit(); err != nil {
							fmt.Printf("Error limiting IP: %s", err)
						}
					}
				}
			}
		}(channel)

		server.Wait()

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


<commit_before>package main

import (
	"fmt"
	. "github.com/eaciit/hdc/hive"
	"time"
)

var h *Hive
var q string

type Sample7 struct {
	Code        string `tag_name:"code"`
	Description string `tag_name:"description"`
	Total_emp   int    `tag_name:"total_emp"`
	Salary      int    `tag_name:"salary"`
}

type SampleParse struct {
	Code        string    `tag_name:"code"`
	Description string    `tag_name:"description"`
	Total_emp   int       `tag_name:"total_emp"`
	Salary      int       `tag_name:"salary"`
	Date        time.Time `tag_name:"date"`
}

func main() {
	h = HiveConfig("192.168.0.223:10000", "default", "developer", "b1gD@T@", "")
	q = "select * from sample_07 limit 20;"

	//for now this function just provide  csv type
	TestParseOutput()

	//Exec Query and Process with DoSomething Function PerLine | EXECPERLINE only support for csv or tsv
	h.OutputType = "csv"
	TestExecPerLine()

	// Exec Query and wait until all line fetched | EXEC only support for csv or tsv
	h.OutputType = "tsv"
	TestExec()
}

func DoSomething(res string) {
	tmp := Sample7{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)
}

func TestExec() {
	res, e := h.Exec(q)

	if e != nil {
		fmt.Printf("error: \n%v\n", e)
	} else {
		fmt.Println(res)
	}
}

func TestExecPerLine() {
	e := h.ExecLine(q, DoSomething)

	if e != nil {
		fmt.Printf("error: \n%v\n", e)
	}
}

func TestParseOutput() {
	h.Header = []string{"code", "description", "total_emp", "salary", "Date"}

	h.OutputType = "csv"
	h.DateFormat = "YYYY-MM-DD"
	res := "'00-0000','All Occupations CSV','134354250','40690','2014-05-01'"
	tmp := SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	h.OutputType = "tsv"
	h.DateFormat = "YYYY-MMM-DD"
	res = "00-0000\tAll Occupations TSV\t134354250\t40690\t2014-Dec-05"
	tmp = SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	//try to parse json with different line
	h.OutputType = "json"
	res = "{ \"code\" : \"00-0000\" , \"description\" : \"All Occupations JSON\" "
	tmp = SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	res = ", \"total_emp\" : 134354, \"salary\" : 40690,\"Date\" : \"2012-04-23T18:25:43Z\" },{ \"code\" : \"00-0000\""
	tmp = SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)
}
<commit_msg>test interface<commit_after>package main

import (
	"fmt"
	. "github.com/hdc/yanda15/hdc/hive"
	"time"
)

var h *Hive
var q string

type Sample7 struct {
	Code        string `tag_name:"code"`
	Description string `tag_name:"description"`
	Total_emp   int    `tag_name:"total_emp"`
	Salary      int    `tag_name:"salary"`
}

type SampleParse struct {
	Code        string    `tag_name:"code"`
	Description string    `tag_name:"description"`
	Total_emp   int       `tag_name:"total_emp"`
	Salary      int       `tag_name:"salary"`
	Date        time.Time `tag_name:"date"`
}

func main() {
	h = HiveConfig("192.168.0.223:10000", "default", "developer", "b1gD@T@", "")
	q = "select * from sample_07 limit 20;"

	//for now this function just provide  csv type
	TestParseOutput()

	//Exec Query and Process with DoSomething Function PerLine | EXECPERLINE only support for csv or tsv
	h.OutputType = "csv"
	TestExecPerLine()

	// Exec Query and wait until all line fetched | EXEC only support for csv or tsv
	h.OutputType = "tsv"
	TestExec()
}

func DoSomething(res string) {
	tmp := Sample7{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)
}

func TestExec() {
	res, e := h.Exec(q)

	if e != nil {
		fmt.Printf("error: \n%v\n", e)
	} else {
		fmt.Println(res)
	}
}

func TestExecPerLine() {
	e := h.ExecLine(q, DoSomething)

	if e != nil {
		fmt.Printf("error: \n%v\n", e)
	}
}

func TestParseOutput() {
	h.Header = []string{"code", "description", "total_emp", "salary", "Date"}

	h.OutputType = "csv"
	h.DateFormat = "YYYY-MM-DD"
	res := "'00-0000','All Occupations CSV','134354250','40690','2014-05-01'"
	tmp := SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	h.OutputType = "tsv"
	h.DateFormat = "YYYY-MMM-DD"
	res = "00-0000\tAll Occupations TSV\t134354250\t40690\t2014-Dec-05"
	tmp = SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	//try to parse json with different line
	h.OutputType = "json"
	res = "{ \"code\" : \"00-0000\" , \"description\" : \"All Occupations JSON\" "
	tmp = SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	res = ", \"total_emp\" : 134354, \"salary\" : 40690,\"Date\" : \"2012-04-23T18:25:43Z\" },{ \"code\" : \"00-2222\""
	tmp = SampleParse{}
	h.ParseOutput(res, &tmp)
	fmt.Println(tmp)

	res = ",\"description\" : \"All Occupations INTERFACE\" , \"total_emp\" : 222, \"salary\" : 2222,\"Date\" : \"2012-05-23T18:25:43Z\" }"
	var tmpx interface{}
	h.ParseOutput(res, &tmpx)
	fmt.Println(tmpx)

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
)

type MenuItem struct {
	Name string
	Link string
}

type SiteConfig struct {
	Title string
	Logo  string
	Style string
}

type Header struct {
	Style    string
	Title    string
	Logo     string
	HomePath string
}

type Page struct {
	Header Header
	Menu   string
	Body   string
}

func generateMenu(files []os.FileInfo, homePath string) string {
	var m []MenuItem

	if homePath != "" {
		m = append(m, MenuItem{Name: ".", Link: "index.html"})
		m = append(m, MenuItem{Name: "..", Link: "../index.html"})
	}
	m = append(m, MenuItem{Name: "home", Link: homePath + "index.html"})

	for _, f := range files {
		if strings.HasPrefix(f.Name(), "index") {
			continue
		}
		fname := strings.Split(f.Name(), ".")[0]

		if f.IsDir() {
			m = append(m, MenuItem{Name: fname, Link: fname + "/index.html"})
		} else {
			m = append(m, MenuItem{Name: fname, Link: fname + ".html"})
		}
	}

	ret := &bytes.Buffer{}
	if err := menuTemplate.Execute(ret, m); err != nil {
		fmt.Println(err)
	}
	return ret.String()
}

func parseBody(file string) string {
	body, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
	}

	imageParser := regexp.MustCompile(`\[\[(.*?)\]\]`)
	linkParser := regexp.MustCompile(`\((.*)\)\[(.*?)\]`)

	imaged := imageParser.ReplaceAllString(string(body), "")
	linked := linkParser.ReplaceAllString(imaged, "$1")

	return linked
}

func generatePage(site Page) string {
	ret := &bytes.Buffer{}
	if err := siteTemplate.Execute(ret, site); err != nil {
		fmt.Println(err)
	}
	return ret.String()
}

func generateSite(header Header, inputDir string, outputDir string) {
	files, _ := ioutil.ReadDir(inputDir)
	menu := generateMenu(files, header.HomePath)

	for _, f := range files {
		if f.IsDir() {
			if err := os.Mkdir(outputDir+"/"+f.Name(), 0755); err != nil {
				fmt.Println(err)
			}

			header.HomePath = header.HomePath + "../"
			generateSite(header, inputDir+"/"+f.Name(), outputDir+"/"+f.Name())
			continue
		}

		fname := strings.Split(f.Name(), ".")[0]
		body := parseBody(inputDir + "/" + f.Name())

		page := generatePage(Page{
			Header: header,
			Menu:   menu,
			Body:   string(body),
		})

		if err := ioutil.WriteFile(outputDir+"/"+fname+".html", []byte(page), 0755); err != nil {
			fmt.Println(err)
		}
	}

}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		usage()
		os.Exit(1)
	}
	siteDir := strings.Split(flag.Arg(0), "/")[0]

	var swagConfig SiteConfig
	config, err := ioutil.ReadFile("swag.conf")
	if err != nil {
		fmt.Println(err)
	}
	dec := json.NewDecoder(bytes.NewReader(config))
	if err := dec.Decode(&swagConfig); err != nil {
		fmt.Println(err)
	}

	outputDir := siteDir + ".static"
	if err := os.RemoveAll(outputDir); err != nil {
		fmt.Println(err)
	}
	if err := os.Mkdir(outputDir, 0755); err != nil {
		fmt.Println(err)
	}

	style, err := ioutil.ReadFile(swagConfig.Style)
	if err != nil {
		fmt.Println(err)
	}

	logo, err := ioutil.ReadFile(swagConfig.Logo)
	if err != nil {
		fmt.Println(err)
	}

	generateSite(Header{
		Style:    string(style),
		Logo:     string(logo),
		Title:    swagConfig.Title,
		HomePath: "",
	}, siteDir, outputDir)
}

func usage() {
	fmt.Println("Usage: swag <site_dir>")
}

var menuTemplate = template.Must(template.New("menu").Parse(menuASCII))

const menuASCII = `
--{{range .}}| {{.Name}} {{end}}|--
`

var siteTemplate = template.Must(template.New("site").Parse(siteSkeleton))

const siteSkeleton = `

<title>{{.Header.Title}}</title>

{{.Header.Logo}}
{{.Menu}}

{{.Body}}

Powered by swag

`
<commit_msg>Feature: Automatic index generation.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
)

type MenuItem struct {
	Name string
	Link string
}

type SiteConfig struct {
	Title string
	Logo  string
	Style string
}

type Header struct {
	Style    string
	Title    string
	Logo     string
	HomePath string
}

type Page struct {
	Header Header
	Menu   string
	Body   string
}

func generateMenu(files []os.FileInfo, homePath string) string {
	var m []MenuItem

	if homePath != "" {
		m = append(m, MenuItem{Name: ".", Link: "index.html"})
		m = append(m, MenuItem{Name: "..", Link: "../index.html"})
	}
	m = append(m, MenuItem{Name: "home", Link: homePath + "index.html"})

	for _, f := range files {
		if strings.HasPrefix(f.Name(), "index") {
			continue
		}
		fname := strings.Split(f.Name(), ".")[0]

		if f.IsDir() {
			m = append(m, MenuItem{Name: fname, Link: fname + "/index.html"})
		} else {
			m = append(m, MenuItem{Name: fname, Link: fname + ".html"})
		}
	}

	ret := &bytes.Buffer{}
	if err := menuTemplate.Execute(ret, m); err != nil {
		fmt.Println(err)
	}
	return ret.String()
}

func generateIndex(header Header, menu string, files []os.FileInfo, outputDir string) {
	var m []MenuItem

	for _, f := range files {
		if strings.HasPrefix(f.Name(), "index") {
			continue
		}
		fname := strings.Split(f.Name(), ".")[0]

		if f.IsDir() {
			m = append(m, MenuItem{Name: fname + "/", Link: fname + "/index.html"})
		} else {
			m = append(m, MenuItem{Name: fname, Link: fname + ".html"})
		}
	}

	body := &bytes.Buffer{}
	if err := indexTemplate.Execute(body, m); err != nil {
		fmt.Println(err)
	}

	page := generatePage(Page{
		Header: header,
		Menu:   menu,
		Body:   body.String(),
	})

	if err := ioutil.WriteFile(outputDir+"/index.html", []byte(page), 0644); err != nil {
		fmt.Println(err)
	}
}

func parseBody(file string) string {
	body, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println(err)
	}

	imageParser := regexp.MustCompile(`\[\[(.*?)\]\]`)
	linkParser := regexp.MustCompile(`\((.*)\)\[(.*?)\]`)

	imaged := imageParser.ReplaceAllString(string(body), "")
	linked := linkParser.ReplaceAllString(imaged, "$1")

	return linked
}

func generatePage(site Page) string {
	ret := &bytes.Buffer{}
	if err := pageTemplate.Execute(ret, site); err != nil {
		fmt.Println(err)
	}
	return ret.String()
}

func generateSite(header Header, inputDir string, outputDir string) {
	files, _ := ioutil.ReadDir(inputDir)
	var menu string

	if !containsFile(files, "index") {
		files, _ = ioutil.ReadDir(inputDir)
		menu = generateMenu([]os.FileInfo{}, header.HomePath)
		generateIndex(header, menu, files, outputDir)
	} else {
		menu = generateMenu(files, header.HomePath)
	}

	for _, f := range files {
		if f.IsDir() {
			if err := os.Mkdir(outputDir+"/"+f.Name(), 0755); err != nil {
				fmt.Println(err)
			}

			header.HomePath = header.HomePath + "../"
			generateSite(header, inputDir+"/"+f.Name(), outputDir+"/"+f.Name())
			continue
		}

		fname := strings.Split(f.Name(), ".")[0]
		body := parseBody(inputDir + "/" + f.Name())

		page := generatePage(Page{
			Header: header,
			Menu:   menu,
			Body:   string(body),
		})

		if err := ioutil.WriteFile(outputDir+"/"+fname+".html", []byte(page), 0755); err != nil {
			fmt.Println(err)
		}
	}

}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		usage()
		os.Exit(1)
	}
	siteDir := strings.Split(flag.Arg(0), "/")[0]

	var swagConfig SiteConfig
	config, err := ioutil.ReadFile("swag.conf")
	if err != nil {
		fmt.Println(err)
	}
	dec := json.NewDecoder(bytes.NewReader(config))
	if err := dec.Decode(&swagConfig); err != nil {
		fmt.Println(err)
	}

	outputDir := siteDir + ".static"
	if err := os.RemoveAll(outputDir); err != nil {
		fmt.Println(err)
	}
	if err := os.Mkdir(outputDir, 0755); err != nil {
		fmt.Println(err)
	}

	style, err := ioutil.ReadFile(swagConfig.Style)
	if err != nil {
		fmt.Println(err)
	}

	logo, err := ioutil.ReadFile(swagConfig.Logo)
	if err != nil {
		fmt.Println(err)
	}

	generateSite(Header{
		Style:    string(style),
		Logo:     string(logo),
		Title:    swagConfig.Title,
		HomePath: "",
	}, siteDir, outputDir)
}

func containsFile(s []os.FileInfo, e string) bool {
	for _, a := range s {
		if strings.Split(a.Name(), ".")[0] == e {
			return true
		}
	}
	return false
}

func usage() {
	fmt.Println("Usage: swag <site_dir>")
}

var menuTemplate = template.Must(template.New("menu").Parse(menuASCII))

const menuASCII = `
--{{range .}}| {{.Name}} {{end}}|--
`

var indexTemplate = template.Must(template.New("index").Parse(indexSkeleton))

const indexSkeleton = `
{{range .}}* {{.Name}}

{{end}}
`

var pageTemplate = template.Must(template.New("site").Parse(pageSkeleton))

const pageSkeleton = `

<title>{{.Header.Title}}</title>

{{.Header.Logo}}
{{.Menu}}

{{.Body}}

Powered by swag

`
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/kkserver/kk-job/job"
	"github.com/kkserver/kk-lib/kk"
	"log"
	"os"
	"os/exec"
	"runtime/debug"
	"time"
)

func help() {
	fmt.Println("kk-job-slave <name> <127.0.0.1:8700> <kk.job.> <token> <workdir> <processCount>")
}

func request(sendRequest func(message *kk.Message, timeout time.Duration) *kk.Message, to string, timeout time.Duration, data interface{}, result interface{}) error {

	log.Printf("[REQUEST] %s ...\n", to)

	var b, _ = json.Marshal(data)
	var v = kk.Message{"REQUEST", "", to, "text/json", b}
	var r = sendRequest(&v, timeout)

	log.Printf("[REQUEST] %s %s\n", to, r.String())

	if r == nil {
		return errors.New(fmt.Sprintf("TO: %s fail", to))
	}

	if r.Method != "REQUEST" {
		return errors.New(fmt.Sprintf("TO: %s %s", to, r.String()))
	}

	if r.Type == "text/json" || r.Type == "application/json" {
		return json.Unmarshal(r.Content, result)
	}

	return nil
}

func createJSONFile(data interface{}, path string) error {

	os.Remove(path)

	fd, err := os.Create(path)

	if err != nil {
		return err
	}

	defer fd.Close()

	os.Chmod(path, 0777)

	b, err := json.Marshal(data)

	if err == nil {

		var n = 0

		for n != len(b) {

			n, err := fd.Write(b)

			if err != nil {
				log.Println("[FAIL] " + err.Error())
				return err
			}

			if n != len(b) {
				b = b[n:]
			} else {
				break
			}
		}
	} else {
		log.Println("[FAIL] " + err.Error())
	}

	return nil
}

func createTextFile(text string, path string) error {

	os.Remove(path)

	fd, err := os.Create(path)

	if err != nil {
		return err
	}

	os.Chmod(path, 0777)

	defer fd.Close()

	b := []byte(text)

	var n = 0

	for n != len(b) {

		n, err := fd.Write(b)

		if err != nil {
			return err
		}

		if n != len(b) {
			b = b[n:]
		} else {
			break
		}
	}

	return nil
}

func createShellFile(options map[string]interface{}, path string, cmd string) error {

	os.Remove(path)

	fd, err := os.Create(path)

	if err != nil {
		return err
	}

	os.Chmod(path, 0777)

	defer fd.Close()

	fd.WriteString("#/bin/sh\n\n")

	for key, value := range options {
		var option, ok = value.(map[string]interface{})
		if ok {
			v, ok := option["value"]
			if ok {
				b, _ := json.Marshal(v)
				fmt.Fprintf(fd, "export %s=%s\n", key, string(b))
			}

		}
	}

	fd.WriteString("\n\n")

	fd.WriteString(cmd)

	fd.WriteString("\n")

	return nil
}

func writeLogFile(tag string, log string, path string) error {

	fd, err := os.OpenFile(path, os.O_APPEND, os.ModePerm)

	if err != nil {

		fd, err = os.Create(path)

		if err != nil {
			return err
		}

		os.Chmod(path, 0777)

	}

	defer fd.Close()

	fd.WriteString(fmt.Sprintf("[%s][%s] %s", tag, time.Now().String(), log))

	return nil
}

type LogWriter struct {
	fd          *os.File
	tag         string
	token       string
	jobId       int64
	version     int
	baseURL     string
	sendRequest func(message *kk.Message, timeout time.Duration) *kk.Message
	line        *bytes.Buffer
}

func NewLogWriter(path string, tag string, token string, jobId int64, version int, baseURL string, sendRequest func(message *kk.Message, timeout time.Duration) *kk.Message) (log *LogWriter, err error) {
	var v = LogWriter{}
	var e error = nil

	v.fd, e = os.OpenFile(path, os.O_APPEND, 0)

	if e != nil {

		v.fd, e = os.Create(path)

		if e != nil {
			return nil, e
		}

		os.Chmod(path, 0777)

	}

	v.tag = tag
	v.token = token
	v.jobId = jobId
	v.version = version
	v.baseURL = baseURL
	v.sendRequest = sendRequest
	v.line = bytes.NewBuffer(nil)

	return &v, nil
}

func (L *LogWriter) Close() error {
	return L.fd.Close()
}

func (L *LogWriter) Write(p []byte) (n int, err error) {

	for _, c := range p {
		if c == '\n' {
			var r = job.JobVersionLogTaskResult{}

			request(L.sendRequest, L.baseURL+"job/slave/log", time.Second, map[string]interface{}{
				"token":   L.token,
				"jobId":   fmt.Sprintf("%d", L.jobId),
				"version": fmt.Sprintf("%d", L.version),
				"tag":     L.tag,
				"log":     L.line.String()}, &r)

			L.line.Reset()
		} else if c == 0x1b {

		} else {
			L.line.WriteByte(c)
		}
	}

	return L.fd.Write(p)
}

func main() {

	log.SetFlags(log.Llongfile | log.LstdFlags)

	var args = os.Args
	var name string = ""
	var address string = ""
	var baseURL string = ""
	var token string = ""
	var workdir string = ""
	var processCount int = 10

	if len(args) > 6 {
		name = args[1]
		address = args[2]
		baseURL = args[3]
		token = args[4]
		workdir = args[5]
		processCount, _ = ParseInt(args[6], 10, 0)
	} else {
		help()
		return
	}

	var sendRequest, _ = kk.TCPClientRequestConnect(name, address, map[string]interface{}{"exclusive": true})

	var process map[string]*kk.Dispatch = map[string]*kk.Dispatch{}
	var online func() = nil

	online = func() {

		var result = job.JobSlaveOnlineTaskResult{}

		var err = request(sendRequest, baseURL+"job/slave/online", time.Second, map[string]interface{}{"token": token}, &result)

		if err != nil {
			log.Println(err)
		} else if result.Errno == 0 {
			createJSONFile(result.Slave, workdir+"/job/slave.json")
		} else {
			log.Println(result.Errmsg)
		}

		kk.GetDispatchMain().AsyncDelay(func() {

			go online()

		}, time.Second*6)

	}

	var jobProcess func() = nil

	jobProcess = func() {

		var result = job.JobSlaveProcessTaskResult{}

		var err = request(sendRequest, baseURL+"job/slave/process", time.Second, map[string]interface{}{"token": token}, &result)

		if err != nil {

			log.Println(err)

			kk.GetDispatchMain().AsyncDelay(func() {
				go jobProcess()
			}, time.Second*6)

		} else if result.Errno == 0 {

			if result.Version != nil {

				var name = fmt.Sprintf("%s/job/%d_%d/", workdir, result.Version.JobId, result.Version.Version)

				_, err := os.Stat(name)

				if err != nil {

					var p = kk.NewDispatch()

					process[name] = p

					var exit = func() {

						kk.GetDispatchMain().Async(func() {

							p.Break()

							delete(process, name)

							kk.GetDispatchMain().AsyncDelay(func() {
								go jobProcess()
							}, time.Second*6)

						})

					}

					var fail = func(err error) {

						log.Println("[FAIL] " + err.Error())

						debug.PrintStack()

						createTextFile(err.Error(), name+"fail")

						var fail = job.JobVersionFailTaskResult{}

						request(sendRequest, baseURL+"job/slave/fail", time.Second, map[string]interface{}{
							"token":      token,
							"jobId":      fmt.Sprintf("%d", result.Version.JobId),
							"version":    fmt.Sprintf("%d", result.Version.Version),
							"statusText": err.Error()}, &fail)

						exit()

					}

					p.Async(func() {

						err := os.Mkdir(name, 0777)

						if err != nil {
							fail(err)
							return
						}

						err = createJSONFile(result.Job, name+"job.json")

						if err != nil {
							fail(err)
							return
						}

						err = createJSONFile(result.Slave, name+"slave.json")

						if err != nil {
							fail(err)
							return
						}

						err = createJSONFile(result.Version, name+"version.json")

						if err != nil {
							fail(err)
							return
						}

						var options = map[string]interface{}{}

						if result.Job.Options != "" {
							json.Unmarshal([]byte(result.Job.Options), &options)
						}

						if result.Version.Options != "" {

							var opts = map[string]interface{}{}

							json.Unmarshal([]byte(result.Job.Options), &opts)

							for key, value := range opts {
								opt, ok := options[key]
								if ok {
									m, ok := opt.(map[string]interface{})
									if ok {
										m[key] = value
									}
								}
							}
						}

						err = createShellFile(options, name+"run.sh", workdir+"/run.sh")

						if err != nil {
							fail(err)
							return
						}

						cmd := exec.Command("/bin/sh", "-c", name+"run.sh")

						cmd.Dir = name

						stdout, err := NewLogWriter(name+"info.log", "INFO", token, result.Version.JobId, result.Version.Version, baseURL, sendRequest)

						cmd.Stderr = stdout
						cmd.Stdout = stdout

						defer stdout.Close()

						err = cmd.Start()

						if err != nil {
							fail(err)
							return
						}

						err = cmd.Wait()

						if err != nil {
							log.Println(workdir + "/run.sh")
							fail(err)
							return
						}

						var r = job.JobVersionOKTaskResult{}

						request(sendRequest, baseURL+"job/slave/ok", time.Second, map[string]interface{}{
							"token":   token,
							"jobId":   fmt.Sprintf("%d", result.Version.JobId),
							"version": fmt.Sprintf("%d", result.Version.Version)}, &r)

						writeLogFile("INFO", "EXIT", name+"info.log")

						exit()
					})

					kk.GetDispatchMain().Async(func() {

						if len(process) < processCount {
							go jobProcess()
						}

					})

					return
				}

				var fail = job.JobVersionFailTaskResult{}

				request(sendRequest, baseURL+"job/slave/fail", time.Second, map[string]interface{}{
					"token":   token,
					"jobId":   fmt.Sprintf("%d", result.Version.JobId),
					"version": fmt.Sprintf("%d", result.Version.Version)}, &fail)

				log.Printf("[FAIL] jobId:%d version:%d %s\n", result.Version.JobId, result.Version.Version)

				kk.GetDispatchMain().AsyncDelay(func() {
					go jobProcess()
				}, time.Second*6)

			} else {
				kk.GetDispatchMain().AsyncDelay(func() {
					go jobProcess()
				}, time.Second*6)
			}

		} else {

			log.Println(result.Errmsg)

			kk.GetDispatchMain().AsyncDelay(func() {
				go jobProcess()
			}, time.Second*6)
		}
	}

	kk.GetDispatchMain().AsyncDelay(func() {

		go func() {

			var result = job.JobSlaveLoginTask{}

			var err = request(sendRequest, baseURL+"job/slave/login", time.Second, map[string]interface{}{"token": token}, &result)

			if err != nil {

				log.Println("[FAIL] " + err.Error())

				kk.GetDispatchMain().Break()

				return
			}

			go online()

			go jobProcess()

		}()

	}, time.Second)

	kk.DispatchMain()

	for _, v := range process {
		v.Break()
	}

	log.Println("[EXIT]")

}
<commit_msg>update<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/kkserver/kk-job/job"
	"github.com/kkserver/kk-lib/kk"
	"log"
	"os"
	"os/exec"
	"runtime/debug"
	"strconv"
	"time"
)

func help() {
	fmt.Println("kk-job-slave <name> <127.0.0.1:8700> <kk.job.> <token> <workdir> <processCount>")
}

func request(sendRequest func(message *kk.Message, timeout time.Duration) *kk.Message, to string, timeout time.Duration, data interface{}, result interface{}) error {

	log.Printf("[REQUEST] %s ...\n", to)

	var b, _ = json.Marshal(data)
	var v = kk.Message{"REQUEST", "", to, "text/json", b}
	var r = sendRequest(&v, timeout)

	log.Printf("[REQUEST] %s %s\n", to, r.String())

	if r == nil {
		return errors.New(fmt.Sprintf("TO: %s fail", to))
	}

	if r.Method != "REQUEST" {
		return errors.New(fmt.Sprintf("TO: %s %s", to, r.String()))
	}

	if r.Type == "text/json" || r.Type == "application/json" {
		return json.Unmarshal(r.Content, result)
	}

	return nil
}

func createJSONFile(data interface{}, path string) error {

	os.Remove(path)

	fd, err := os.Create(path)

	if err != nil {
		return err
	}

	defer fd.Close()

	os.Chmod(path, 0777)

	b, err := json.Marshal(data)

	if err == nil {

		var n = 0

		for n != len(b) {

			n, err := fd.Write(b)

			if err != nil {
				log.Println("[FAIL] " + err.Error())
				return err
			}

			if n != len(b) {
				b = b[n:]
			} else {
				break
			}
		}
	} else {
		log.Println("[FAIL] " + err.Error())
	}

	return nil
}

func createTextFile(text string, path string) error {

	os.Remove(path)

	fd, err := os.Create(path)

	if err != nil {
		return err
	}

	os.Chmod(path, 0777)

	defer fd.Close()

	b := []byte(text)

	var n = 0

	for n != len(b) {

		n, err := fd.Write(b)

		if err != nil {
			return err
		}

		if n != len(b) {
			b = b[n:]
		} else {
			break
		}
	}

	return nil
}

func createShellFile(options map[string]interface{}, path string, cmd string) error {

	os.Remove(path)

	fd, err := os.Create(path)

	if err != nil {
		return err
	}

	os.Chmod(path, 0777)

	defer fd.Close()

	fd.WriteString("#/bin/sh\n\n")

	for key, value := range options {
		var option, ok = value.(map[string]interface{})
		if ok {
			v, ok := option["value"]
			if ok {
				b, _ := json.Marshal(v)
				fmt.Fprintf(fd, "export %s=%s\n", key, string(b))
			}

		}
	}

	fd.WriteString("\n\n")

	fd.WriteString(cmd)

	fd.WriteString("\n")

	return nil
}

func writeLogFile(tag string, log string, path string) error {

	fd, err := os.OpenFile(path, os.O_APPEND, os.ModePerm)

	if err != nil {

		fd, err = os.Create(path)

		if err != nil {
			return err
		}

		os.Chmod(path, 0777)

	}

	defer fd.Close()

	fd.WriteString(fmt.Sprintf("[%s][%s] %s", tag, time.Now().String(), log))

	return nil
}

type LogWriter struct {
	fd          *os.File
	tag         string
	token       string
	jobId       int64
	version     int
	baseURL     string
	sendRequest func(message *kk.Message, timeout time.Duration) *kk.Message
	line        *bytes.Buffer
}

func NewLogWriter(path string, tag string, token string, jobId int64, version int, baseURL string, sendRequest func(message *kk.Message, timeout time.Duration) *kk.Message) (log *LogWriter, err error) {
	var v = LogWriter{}
	var e error = nil

	v.fd, e = os.OpenFile(path, os.O_APPEND, 0)

	if e != nil {

		v.fd, e = os.Create(path)

		if e != nil {
			return nil, e
		}

		os.Chmod(path, 0777)

	}

	v.tag = tag
	v.token = token
	v.jobId = jobId
	v.version = version
	v.baseURL = baseURL
	v.sendRequest = sendRequest
	v.line = bytes.NewBuffer(nil)

	return &v, nil
}

func (L *LogWriter) Close() error {
	return L.fd.Close()
}

func (L *LogWriter) Write(p []byte) (n int, err error) {

	for _, c := range p {
		if c == '\n' {
			var r = job.JobVersionLogTaskResult{}

			request(L.sendRequest, L.baseURL+"job/slave/log", time.Second, map[string]interface{}{
				"token":   L.token,
				"jobId":   fmt.Sprintf("%d", L.jobId),
				"version": fmt.Sprintf("%d", L.version),
				"tag":     L.tag,
				"log":     L.line.String()}, &r)

			L.line.Reset()
		} else if c == 0x1b {

		} else {
			L.line.WriteByte(c)
		}
	}

	return L.fd.Write(p)
}

func main() {

	log.SetFlags(log.Llongfile | log.LstdFlags)

	var args = os.Args
	var name string = ""
	var address string = ""
	var baseURL string = ""
	var token string = ""
	var workdir string = ""
	var processCount int = 10

	if len(args) > 6 {
		name = args[1]
		address = args[2]
		baseURL = args[3]
		token = args[4]
		workdir = args[5]
		processCount, _ = strconv.Atoi(args[6])
	} else {
		help()
		return
	}

	var sendRequest, _ = kk.TCPClientRequestConnect(name, address, map[string]interface{}{"exclusive": true})

	var process map[string]*kk.Dispatch = map[string]*kk.Dispatch{}
	var online func() = nil

	online = func() {

		var result = job.JobSlaveOnlineTaskResult{}

		var err = request(sendRequest, baseURL+"job/slave/online", time.Second, map[string]interface{}{"token": token}, &result)

		if err != nil {
			log.Println(err)
		} else if result.Errno == 0 {
			createJSONFile(result.Slave, workdir+"/job/slave.json")
		} else {
			log.Println(result.Errmsg)
		}

		kk.GetDispatchMain().AsyncDelay(func() {

			go online()

		}, time.Second*6)

	}

	var jobProcess func() = nil

	jobProcess = func() {

		var result = job.JobSlaveProcessTaskResult{}

		var err = request(sendRequest, baseURL+"job/slave/process", time.Second, map[string]interface{}{"token": token}, &result)

		if err != nil {

			log.Println(err)

			kk.GetDispatchMain().AsyncDelay(func() {
				go jobProcess()
			}, time.Second*6)

		} else if result.Errno == 0 {

			if result.Version != nil {

				var name = fmt.Sprintf("%s/job/%d_%d/", workdir, result.Version.JobId, result.Version.Version)

				_, err := os.Stat(name)

				if err != nil {

					var p = kk.NewDispatch()

					process[name] = p

					var exit = func() {

						kk.GetDispatchMain().Async(func() {

							p.Break()

							delete(process, name)

							kk.GetDispatchMain().AsyncDelay(func() {
								go jobProcess()
							}, time.Second*6)

						})

					}

					var fail = func(err error) {

						log.Println("[FAIL] " + err.Error())

						debug.PrintStack()

						createTextFile(err.Error(), name+"fail")

						var fail = job.JobVersionFailTaskResult{}

						request(sendRequest, baseURL+"job/slave/fail", time.Second, map[string]interface{}{
							"token":      token,
							"jobId":      fmt.Sprintf("%d", result.Version.JobId),
							"version":    fmt.Sprintf("%d", result.Version.Version),
							"statusText": err.Error()}, &fail)

						exit()

					}

					p.Async(func() {

						err := os.Mkdir(name, 0777)

						if err != nil {
							fail(err)
							return
						}

						err = createJSONFile(result.Job, name+"job.json")

						if err != nil {
							fail(err)
							return
						}

						err = createJSONFile(result.Slave, name+"slave.json")

						if err != nil {
							fail(err)
							return
						}

						err = createJSONFile(result.Version, name+"version.json")

						if err != nil {
							fail(err)
							return
						}

						var options = map[string]interface{}{}

						if result.Job.Options != "" {
							json.Unmarshal([]byte(result.Job.Options), &options)
						}

						if result.Version.Options != "" {

							var opts = map[string]interface{}{}

							json.Unmarshal([]byte(result.Job.Options), &opts)

							for key, value := range opts {
								opt, ok := options[key]
								if ok {
									m, ok := opt.(map[string]interface{})
									if ok {
										m[key] = value
									}
								}
							}
						}

						err = createShellFile(options, name+"run.sh", workdir+"/run.sh")

						if err != nil {
							fail(err)
							return
						}

						cmd := exec.Command("/bin/sh", "-c", name+"run.sh")

						cmd.Dir = name

						stdout, err := NewLogWriter(name+"info.log", "INFO", token, result.Version.JobId, result.Version.Version, baseURL, sendRequest)

						cmd.Stderr = stdout
						cmd.Stdout = stdout

						defer stdout.Close()

						err = cmd.Start()

						if err != nil {
							fail(err)
							return
						}

						err = cmd.Wait()

						if err != nil {
							log.Println(workdir + "/run.sh")
							fail(err)
							return
						}

						var r = job.JobVersionOKTaskResult{}

						request(sendRequest, baseURL+"job/slave/ok", time.Second, map[string]interface{}{
							"token":   token,
							"jobId":   fmt.Sprintf("%d", result.Version.JobId),
							"version": fmt.Sprintf("%d", result.Version.Version)}, &r)

						writeLogFile("INFO", "EXIT", name+"info.log")

						exit()
					})

					kk.GetDispatchMain().Async(func() {

						if len(process) < processCount {
							go jobProcess()
						}

					})

					return
				}

				var fail = job.JobVersionFailTaskResult{}

				request(sendRequest, baseURL+"job/slave/fail", time.Second, map[string]interface{}{
					"token":   token,
					"jobId":   fmt.Sprintf("%d", result.Version.JobId),
					"version": fmt.Sprintf("%d", result.Version.Version)}, &fail)

				log.Printf("[FAIL] jobId:%d version:%d %s\n", result.Version.JobId, result.Version.Version)

				kk.GetDispatchMain().AsyncDelay(func() {
					go jobProcess()
				}, time.Second*6)

			} else {
				kk.GetDispatchMain().AsyncDelay(func() {
					go jobProcess()
				}, time.Second*6)
			}

		} else {

			log.Println(result.Errmsg)

			kk.GetDispatchMain().AsyncDelay(func() {
				go jobProcess()
			}, time.Second*6)
		}
	}

	kk.GetDispatchMain().AsyncDelay(func() {

		go func() {

			var result = job.JobSlaveLoginTask{}

			var err = request(sendRequest, baseURL+"job/slave/login", time.Second, map[string]interface{}{"token": token}, &result)

			if err != nil {

				log.Println("[FAIL] " + err.Error())

				kk.GetDispatchMain().Break()

				return
			}

			go online()

			go jobProcess()

		}()

	}, time.Second)

	kk.DispatchMain()

	for _, v := range process {
		v.Break()
	}

	log.Println("[EXIT]")

}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/urfave/cli"

	u "fluorescences/utils"
)

func main() {

	app := cli.NewApp()
	app.Name = "Fluorescences"
	app.Usage = "An art gallery blog"
	app.Version = "RC1"
	app.Copyright = "(c) 2016 Tech Janitor"

	app.Commands = []cli.Command{
		{
			Name:  "start",
			Usage: "start the server",
			Action: func(c *cli.Context) error {
				name := c.Args().Get(0)
				address := c.Args().Get(1)
				port := c.Args().Get(2)
				start(name, address, port)
				return nil
			},
		},
		{
			Name:  "init",
			Usage: "initialize a component for the first time",
			Subcommands: []cli.Command{
				{
					Name:  "data",
					Usage: "initialize the boilerplate data",
					Action: func(c *cli.Context) error {
						name := c.Args().Get(0)
						return u.InitData(name)
					},
				},
				{
					Name:  "user",
					Usage: "initialize a user",
					Action: func(c *cli.Context) error {
						name := c.Args().Get(0)
						user := c.Args().Get(1)
						if user == "" {
							return cli.NewExitError("username required", 1)
						}
						return u.InitUser(name, user)
					},
				},
				{
					Name:  "secret",
					Usage: "initialize the HMAC secret",
					Action: func(c *cli.Context) error {
						name := c.Args().Get(0)
						return u.InitSecret(name)
					},
				},
			},
		},
	}

	app.Run(os.Args)

}
<commit_msg>multi-tenant<commit_after>package main

import (
	"os"

	"github.com/urfave/cli"

	u "fluorescences/utils"
)

func main() {

	app := cli.NewApp()
	app.Name = "Fluorescences"
	app.Usage = "An art gallery blog"
	app.Version = "RC1"
	app.Copyright = "(c) 2016 Tech Janitor"

	app.Commands = []cli.Command{
		{
			Name:  "start",
			Usage: "start the server",
			Action: func(c *cli.Context) error {
				name := c.Args().Get(0)
				address := c.Args().Get(1)
				port := c.Args().Get(2)
				start(name, address, port)
				return nil
			},
		},
		{
			Name:  "init",
			Usage: "initialize a component for the first time",
			Subcommands: []cli.Command{
				{
					Name:  "data",
					Usage: "initialize the boilerplate data",
					Action: func(c *cli.Context) error {
						name := c.Args().Get(0)
						if name == "" {
							return cli.NewExitError("tenant name required", 1)
						}

						return u.InitData(name)
					},
				},
				{
					Name:  "user",
					Usage: "initialize a user",
					Action: func(c *cli.Context) error {
						name := c.Args().Get(0)
						if name == "" {
							return cli.NewExitError("tenant name required", 1)
						}

						user := c.Args().Get(1)
						if user == "" {
							return cli.NewExitError("username required", 1)
						}

						return u.InitUser(name, user)
					},
				},
				{
					Name:  "secret",
					Usage: "initialize the HMAC secret",
					Action: func(c *cli.Context) error {
						name := c.Args().Get(0)
						if name == "" {
							return cli.NewExitError("tenant name required", 1)
						}

						return u.InitSecret(name)
					},
				},
			},
		},
	}

	app.Run(os.Args)

}
<|endoftext|>