<commit_before>package wav

import (
  bin "encoding/binary"
  "os"
  "bufio"
  "fmt"
)

type WavData struct {
  bChunkID [4]byte         // B
  ChunkSize uint32       // L
  bFormat [4]byte          // B

  bSubchunk1ID [4]byte     // B
  Subchunk1Size uint32   // L
  AudioFormat uint16     // L
  NumChannels uint16     // L
  SampleRate uint32      // L
  ByteRate uint32        // L
  BlockAlign uint16      // L
  BitsPerSample uint16   // L

  bSubchunk2ID [4]byte     // B
  Subchunk2Size uint32   // L
  data []byte             // L
}

func ReadWavData( fn string ) (wav WavData) {
  ftotal, err := os.OpenFile(fn, os.O_RDONLY, 0)
  if err != nil {
    fmt.Printf( "Error opening\n" )
  }
  file := bufio.NewReader(ftotal)

  bin.Read( file, bin.BigEndian, &wav.bChunkID )
  bin.Read( file, bin.LittleEndian, &wav.ChunkSize )
  bin.Read( file, bin.BigEndian, &wav.bFormat )

  bin.Read( file, bin.BigEndian, &wav.bSubchunk1ID )
  bin.Read( file, bin.LittleEndian, &wav.Subchunk1Size )
  bin.Read( file, bin.LittleEndian, &wav.AudioFormat )
  bin.Read( file, bin.LittleEndian, &wav.NumChannels )
  bin.Read( file, bin.LittleEndian, &wav.SampleRate )
  bin.Read( file, bin.LittleEndian, &wav.ByteRate )
  bin.Read( file, bin.LittleEndian, &wav.BlockAlign )
  bin.Read( file, bin.LittleEndian, &wav.BitsPerSample )

  bin.Read( file, bin.BigEndian, &wav.bSubchunk2ID )
  bin.Read( file, bin.LittleEndian, &wav.Subchunk2Size )

  wav.data = make( []byte, wav.Subchunk2Size )
  bin.Read( file, bin.LittleEndian, &wav.data )

  /*
   *   fmt.Printf( "\n" )
   *   fmt.Printf( "ChunkID*: %s\n", ChunkID )
   *   fmt.Printf( "ChunkSize: %d\n", ChunkSize )
   *   fmt.Printf( "Format: %s\n", Format )
   *   fmt.Printf( "\n" )
   *   fmt.Printf( "Subchunk1ID: %s\n", Subchunk1ID )
   *   fmt.Printf( "Subchunk1Size: %d\n", Subchunk1Size )
   *   fmt.Printf( "AudioFormat: %d\n", AudioFormat )
   *   fmt.Printf( "NumChannels: %d\n", NumChannels )
   *   fmt.Printf( "SampleRate: %d\n", SampleRate )
   *   fmt.Printf( "ByteRate: %d\n", ByteRate )
   *   fmt.Printf( "BlockAlign: %d\n", BlockAlign )
   *   fmt.Printf( "BitsPerSample: %d\n", BitsPerSample )
   *   fmt.Printf( "\n" )
   *   fmt.Printf( "Subchunk2ID: %s\n", Subchunk2ID )
   *   fmt.Printf( "Subchunk2Size: %d\n", Subchunk2Size )
   *   fmt.Printf( "NumSamples: %d\n", Subchunk2Size / uint32(NumChannels) / uint32(BitsPerSample/8) )
   *   fmt.Printf( "\ndata: %v\n", len(data) )
   *   fmt.Printf( "\n\n" )
   */
  return
}

const (
  mid16 uint16 = 1>>2
  big16 uint16 = 1>>1
  big32 uint32 = 65535
)

func btou( b []byte ) (u []uint16) {
  u = make( []uint16, len(b)/2 )
  for i,_ := range u {
    val := uint16(b[i*2])
    val += uint16(b[i*2+1])<<8
    u[i] = val
  }
  return
}

func btoi16( b []byte ) (u []int16) {
  u = make( []int16, len(b)/2 )
  for i,_ := range u {
    val := int16(b[i*2])
    val += int16(b[i*2+1])<<8
    u[i] = val
  }
  return
}

func btof32( b []byte ) (f []float32) {
  u := btoi16(b)
  f = make([]float32, len(u))
  for i,v := range u {
    f[i] = float32(v)/float32(32768)
  }
  return
}

func utob( u []uint16 ) (b []byte) {
  b = make( []byte, len(u)*2 )
  for i,val := range u {
    lo := byte(val)
    hi := byte(val>>8)
    b[i*2] = lo
    b[i*2+1] = hi
  }
  return
}

<commit_msg>Added fix for metatags in the wav<commit_after>package wav

import (
  bin "encoding/binary"
  "os"
  "fmt"
  "bufio"
)

type WavData struct {
  ChunkID [4]byte        // B
  ChunkSize uint32       // L
  Format [4]byte         // B

  AudioFormat uint16     // L
  NumChannels uint16     // L
  SampleRate uint32      // L
  ByteRate uint32        // L
  BlockAlign uint16      // L
  BitsPerSample uint16   // L

  Data []byte            // L
}

func ReadWavData( fn string ) (wav WavData) {
  file, err := os.OpenFile(fn, os.O_RDONLY, 0)
  if err != nil {
    fmt.Printf( "Error opening\n" )
  }

  var SubchunkID [4]byte
  var SubchunkSize uint32
  var SubchunkStart int64

  var file_buf *bufio.Reader

  bin.Read( file, bin.BigEndian, &wav.ChunkID )
  bin.Read( file, bin.LittleEndian, &wav.ChunkSize )
  bin.Read( file, bin.BigEndian, &wav.Format )

  for {
    bin.Read( file, bin.BigEndian, &SubchunkID )
    bin.Read( file, bin.LittleEndian, &SubchunkSize )

    SubchunkStart, err = file.Seek(0, 1)
    if err != nil {
      fmt.Printf( "Error opening\n" )
    }
    file_buf = bufio.NewReader(file)

    switch string(SubchunkID[:4]) {
      case "fmt ":
        bin.Read( file_buf, bin.LittleEndian, &wav.AudioFormat )
        bin.Read( file_buf, bin.LittleEndian, &wav.NumChannels )
        bin.Read( file_buf, bin.LittleEndian, &wav.SampleRate )
        bin.Read( file_buf, bin.LittleEndian, &wav.ByteRate )
        bin.Read( file_buf, bin.LittleEndian, &wav.BlockAlign )
        bin.Read( file_buf, bin.LittleEndian, &wav.BitsPerSample )

      case "data":
        wav.Data = make( []byte, SubchunkSize )
        bin.Read( file_buf, bin.LittleEndian, &wav.Data )
        return
    }
    file.Seek(SubchunkStart + int64(SubchunkSize), 0)
  }

  /*
   *   fmt.Printf( "\n" )
   *   fmt.Printf( "ChunkID*: %s\n", ChunkID )
   *   fmt.Printf( "ChunkSize: %d\n", ChunkSize )
   *   fmt.Printf( "Format: %s\n", Format )
   *   fmt.Printf( "\n" )
   *   fmt.Printf( "Subchunk1ID: %s\n", Subchunk1ID )
   *   fmt.Printf( "Subchunk1Size: %d\n", Subchunk1Size )
   *   fmt.Printf( "AudioFormat: %d\n", AudioFormat )
   *   fmt.Printf( "NumChannels: %d\n", NumChannels )
   *   fmt.Printf( "SampleRate: %d\n", SampleRate )
   *   fmt.Printf( "ByteRate: %d\n", ByteRate )
   *   fmt.Printf( "BlockAlign: %d\n", BlockAlign )
   *   fmt.Printf( "BitsPerSample: %d\n", BitsPerSample )
   *   fmt.Printf( "\n" )
   *   fmt.Printf( "Subchunk2ID: %s\n", Subchunk2ID )
   *   fmt.Printf( "Subchunk2Size: %d\n", Subchunk2Size )
   *   fmt.Printf( "NumSamples: %d\n", Subchunk2Size / uint32(NumChannels) / uint32(BitsPerSample/8) )
   *   fmt.Printf( "\ndata: %v\n", len(data) )
   *   fmt.Printf( "\n\n" )
   */
  return
}

const (
  mid16 uint16 = 1>>2
  big16 uint16 = 1>>1
  big32 uint32 = 65535
)

func btou( b []byte ) (u []uint16) {
  u = make( []uint16, len(b)/2 )
  for i,_ := range u {
    val := uint16(b[i*2])
    val += uint16(b[i*2+1])<<8
    u[i] = val
  }
  return
}

func btoi16( b []byte ) (u []int16) {
  u = make( []int16, len(b)/2 )
  for i,_ := range u {
    val := int16(b[i*2])
    val += int16(b[i*2+1])<<8
    u[i] = val
  }
  return
}

func btof32( b []byte ) (f []float32) {
  u := btoi16(b)
  f = make([]float32, len(u))
  for i,v := range u {
    f[i] = float32(v)/float32(32768)
  }
  return
}

func utob( u []uint16 ) (b []byte) {
  b = make( []byte, len(u)*2 )
  for i,val := range u {
    lo := byte(val)
    hi := byte(val>>8)
    b[i*2] = lo
    b[i*2+1] = hi
  }
  return
}

<|endoftext|>


<commit_before>package main

// Slack outgoing webhooks are handled here. Requests come in and are run through
// the markov chain to generate a response, which is sent back to Slack.
//
// Create an outgoing webhook in your Slack here:
// https://my.slack.com/services/new/outgoing-webhook

import (
	"encoding/json"
	"log"
	"strconv"
	"math/rand"
	"net/http"
	"strings"
	"time"
)

type WebhookResponse struct {
	Username string `json:"username"`
	Text     string `json:"text"`
}

func init() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		incomingText := r.PostFormValue("text")
		if incomingText != "" && r.PostFormValue("user_id") != "" && r.PostFormValue("user_id") != "USLACKBOT"{
			text := parseText(incomingText)
			//log.Printf("Handling incoming request: %s", text)

			if text != "" {
				markovChain.Write(text)
			}

			go func() {
				markovChain.Save(stateFile)
			}()

			if rand.Intn(100) < responseChance || strings.Contains(strings.ToLower(incomingText), strings.ToLower(botUsername)) {
				var response WebhookResponse
				response.Username = botUsername
				if strings.Contains(incomingText, "TG") && strings.HasPrefix(strings.ToLower(incomingText), strings.ToLower(botUsername)) {
					if strings.Contains(incomingText, "poil") {
						responseChance -= 1
					} else{
						responseChance -= 5
					}
					if responseChance < 0 {
						responseChance = 0
					}
					response.Text = "Okay :( je suis à "+strconv.Itoa(responseChance)+"%"
				} else if strings.Contains(incomingText, "BS") && strings.HasPrefix(strings.ToLower(incomingText), strings.ToLower(botUsername)) {
					if strings.Contains(incomingText, "poil") {
						responseChance += 1
					} else{
						responseChance += 5
					}
					if responseChance > 100 {
						responseChance = 100
					}
					response.Text = "Okay :D je suis à "+strconv.Itoa(responseChance)+"%"
				} else if strings.Contains(incomingText, "moral") && strings.HasPrefix(strings.ToLower(incomingText), strings.ToLower(botUsername)) {
					response.Text = "Environ "+strconv.Itoa(responseChance)+"% mon capitaine !"
				} else {
					response.Text = markovChain.Generate(numWords)
				}
				//log.Printf("Sending response: %s", response.Text)

				b, err := json.Marshal(response)
				if err != nil {
					log.Fatal(err)
				}

				time.Sleep(5 * time.Second)
				w.Write(b)
			}
		}
	})
}

func StartServer(addr string) {
	log.Printf("Starting HTTP server on %s", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Refacto & prevent bot from saving mentioned sentences<commit_after>package main

// Slack outgoing webhooks are handled here. Requests come in and are run through
// the markov chain to generate a response, which is sent back to Slack.
//
// Create an outgoing webhook in your Slack here:
// https://my.slack.com/services/new/outgoing-webhook

import (
  "encoding/json"
  "log"
  "strconv"
  "math/rand"
  "net/http"
  "strings"
  "time"
)

// WebhookResponse is an unexported type that contains a struct presenting a
// message response
type WebhookResponse struct {
  Username string `json:"username"`
  Text     string `json:"text"`
}

// StartServer starts the http server
func StartServer(address string) {
  log.Printf("Starting HTTP server on %s", address)
  error := http.ListenAndServe(address, nil)
  if error != nil {
    log.Fatal("ListenAndServe: ", error)
  }
}

// computeResponseChance handle the increment/decrement of responseChance
func computeResponseChance(responseChance int, increment int, fineIncrement bool) {
  var newResponseChance int

  if fineIncrement {
    newResponseChance = responseChance + increment
  } else {
    newResponseChance = responseChance + (increment * 5)
  }

  if newResponseChance < 0 {
    return 0
  } else if newResponseChance > 100 {
    return 100
  }

  return newResponseChance
}

func init() {
  http.HandleFunc("/", func(httpResponse http.ResponseWriter, httpRequest *http.Request) {
    isEmpty    := len(strings.TrimSpace(httpRequest.PostFormValue("text"))) == 0
    isSlackbot := httpRequest.PostFormValue("user_id") != "USLACKBOT"

    if isEmpty || isSlackbot || httpRequest.PostFormValue("user_id") != "" {
      return
    }

    text             := parseText(httpRequest.PostFormValue("text"))
    lowerText        := strings.ToLower(text)
    lowerBotUserName := strings.ToLower(botUsername)
    botMentionned    := strings.Contains(lowerText, lowerBotUserName)
    botDirectTalk    := strings.HasPrefix(lowerText, lowerBotUserName)

    if rand.Intn(100) < responseChance || botMentionned {
      response := WebhookResponse{Username: botUsername}

      if botDirectTalk && strings.Contains(lowerText, "TG") {
        responseChance = computeResponseChance(responseChance, -1, strings.Contains(lowerText, "poil"))
        response.Text = "Okay :( je suis à "+strconv.Itoa(responseChance)+"%"
      } else if botDirectTalk && strings.Contains(lowerText, "BS") {
        responseChance = computeResponseChance(responseChance, 1, strings.Contains(lowerText, "poil"))
        response.Text = "Okay :D je suis à "+strconv.Itoa(responseChance)+"%"
      } else if botDirectTalk && strings.Contains(lowerText, "moral") {
        response.Text = "Environ "+strconv.Itoa(responseChance)+"% mon capitaine !"
      } else {
        if (!botMentionned) {
          markovChain.Write(text)

          go func() {
            markovChain.Save(stateFile)
          }()
        }

        response.Text = markovChain.Generate(numWords)
      }

      generatedResponse, error := json.Marshal(response)
      if error != nil {
        log.Fatal(error)
      }

      time.Sleep(5 * time.Second)
      httpResponse.Write(generatedResponse)
    }
  })
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"github.com/coopernurse/gorp"
	_ "github.com/mattn/go-sqlite3"
	"github.com/nfnt/resize"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"image"
	"image/jpeg"
	"image/png"
	"net/http"
	"strconv"
	"strings"
)

type Item struct {
	Id     int32 `db:"_id"`
	Artist string
	Title  string
	Genre  string
}

func main() {
	goji.Get("/database.json", databaseJsonHandler)
	goji.Get("/genre.json", genreJsonHandler)
	goji.Get("/artist.json", artistJsonHandler)
	goji.Get("/jacket", jacketImageHandler)
	goji.Get("/*", http.FileServer(http.Dir("public")))
	goji.Serve()
}

func databaseJsonHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	genre := req.URL.Query().Get("genre")
	artist := req.URL.Query().Get("artist")
	encoder := json.NewEncoder(res)
	encoder.Encode(getDatabase(genre, artist))
}

func genreJsonHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	encoder := json.NewEncoder(res)
	encoder.Encode(getGenre())
}

func artistJsonHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	genre := req.URL.Query().Get("genre")
	encoder := json.NewEncoder(res)
	encoder.Encode(getArtist(genre))
}

func jacketImageHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	id := req.URL.Query().Get("id")
	width, err := strconv.ParseUint(req.URL.Query().Get("width"), 10, 0)
	if err != nil {
		width = 0
	}
	height, err := strconv.ParseUint(req.URL.Query().Get("height"), 10, 0)
	if err != nil {
		height = 0
	}
	buffer := getImage(id, uint(width), uint(height))
	res.Header().Set("Content-Type", "image/jpeg")
	res.Header().Set("Content-Length", strconv.Itoa(len(buffer)))
	if _, err := res.Write(buffer); err != nil {
		panic(err.Error())
	}
}

func escapeKeyword(keyword string) string {
	return strings.Replace(keyword, "'", "''", -1)
}

func getDatabase(genre string, artist string) []Item {
	_genre := escapeKeyword(genre)
	if _genre == "" {
		_genre = "%"
	}
	_artist := escapeKeyword(artist)
	if _artist == "" {
		_artist = "%"
	}

	dbmap := openDb()
	defer dbmap.Db.Close()
	var items []Item
	_, _ = dbmap.Select(&items, "SELECT * FROM database WHERE genre LIKE '"+_genre+"' AND artist LIKE '"+_artist+"' ORDER BY artist ASC, title ASC")
	return items
}

func getGenre() []string {
	dbmap := openDb()
	defer dbmap.Db.Close()

	var items []Item
	_, _ = dbmap.Select(&items, "SELECT DISTINCT genre FROM database ORDER BY genre ASC")
	var genre []string
	for _, item := range items {
		genre = append(genre, item.Genre)
	}
	return genre
}

func getArtist(genre string) []string {
	_genre := escapeKeyword(genre)
	if _genre == "" {
		_genre = "%"
	}

	dbmap := openDb()
	defer dbmap.Db.Close()

	var items []Item
	_, _ = dbmap.Select(&items, "SELECT DISTINCT artist FROM database WHERE genre LIKE '"+_genre+"' ORDER BY artist ASC")
	var artist []string
	for _, item := range items {
		artist = append(artist, item.Artist)
	}
	return artist
}

func getImage(id string, width, height uint) []byte {
	dbmap := openDb()
	defer dbmap.Db.Close()

	var jacket [][]byte
	_, _ = dbmap.Select(&jacket, "SELECT DISTINCT jacket FROM database WHERE _id == '"+id+"'")
	r := bytes.NewReader(jacket[0])
	var img image.Image
	img, err := jpeg.Decode(r)
	if err != nil {
		img, err = png.Decode(r)
		if err != nil {
			return jacket[0]
		}
	}
	resized := resize.Resize(width, height, img, resize.Lanczos3)
	buf := new(bytes.Buffer)
	if err := jpeg.Encode(buf, resized, nil); err != nil {
		panic(err.Error())
	}
	return buf.Bytes()
}

func openDb() *gorp.DbMap {
	db, err := sql.Open("sqlite3", "./CDDatabase.db3")
	if err != nil {
		panic(err.Error())
	}
	return &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
}
<commit_msg>Fix jacket image binary data decode<commit_after>package main

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"github.com/coopernurse/gorp"
	_ "github.com/mattn/go-sqlite3"
	"github.com/nfnt/resize"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"net/http"
	"strconv"
	"strings"
)

type Item struct {
	Id     int32 `db:"_id"`
	Artist string
	Title  string
	Genre  string
}

func main() {
	image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)
	image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)
	image.RegisterFormat("gif", "gif", gif.Decode, gif.DecodeConfig)

	goji.Get("/database.json", databaseJsonHandler)
	goji.Get("/genre.json", genreJsonHandler)
	goji.Get("/artist.json", artistJsonHandler)
	goji.Get("/jacket", jacketImageHandler)
	goji.Get("/*", http.FileServer(http.Dir("public")))
	goji.Serve()
}

func databaseJsonHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	genre := req.URL.Query().Get("genre")
	artist := req.URL.Query().Get("artist")
	encoder := json.NewEncoder(res)
	encoder.Encode(getDatabase(genre, artist))
}

func genreJsonHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	encoder := json.NewEncoder(res)
	encoder.Encode(getGenre())
}

func artistJsonHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	genre := req.URL.Query().Get("genre")
	encoder := json.NewEncoder(res)
	encoder.Encode(getArtist(genre))
}

func jacketImageHandler(ctx web.C, res http.ResponseWriter, req *http.Request) {
	id := req.URL.Query().Get("id")
	width, err := strconv.ParseUint(req.URL.Query().Get("width"), 10, 0)
	if err != nil {
		width = 0
	}
	height, err := strconv.ParseUint(req.URL.Query().Get("height"), 10, 0)
	if err != nil {
		height = 0
	}
	buffer := getImage(id, uint(width), uint(height))
	res.Header().Set("Content-Type", "image/jpeg")
	res.Header().Set("Content-Length", strconv.Itoa(len(buffer)))
	if _, err := res.Write(buffer); err != nil {
		panic(err.Error())
	}
}

func escapeKeyword(keyword string) string {
	return strings.Replace(keyword, "'", "''", -1)
}

func getDatabase(genre string, artist string) []Item {
	_genre := escapeKeyword(genre)
	if _genre == "" {
		_genre = "%"
	}
	_artist := escapeKeyword(artist)
	if _artist == "" {
		_artist = "%"
	}

	dbmap := openDb()
	defer dbmap.Db.Close()
	var items []Item
	_, _ = dbmap.Select(&items, "SELECT * FROM database WHERE genre LIKE '"+_genre+"' AND artist LIKE '"+_artist+"' ORDER BY artist ASC, title ASC")
	return items
}

func getGenre() []string {
	dbmap := openDb()
	defer dbmap.Db.Close()

	var items []Item
	_, _ = dbmap.Select(&items, "SELECT DISTINCT genre FROM database ORDER BY genre ASC")
	var genre []string
	for _, item := range items {
		genre = append(genre, item.Genre)
	}
	return genre
}

func getArtist(genre string) []string {
	_genre := escapeKeyword(genre)
	if _genre == "" {
		_genre = "%"
	}

	dbmap := openDb()
	defer dbmap.Db.Close()

	var items []Item
	_, _ = dbmap.Select(&items, "SELECT DISTINCT artist FROM database WHERE genre LIKE '"+_genre+"' ORDER BY artist ASC")
	var artist []string
	for _, item := range items {
		artist = append(artist, item.Artist)
	}
	return artist
}

func getImage(id string, width, height uint) []byte {
	dbmap := openDb()
	defer dbmap.Db.Close()

	var jacket [][]byte
	_, _ = dbmap.Select(&jacket, "SELECT DISTINCT jacket FROM database WHERE _id == '"+id+"'")
	r := bytes.NewReader(jacket[0])
	var img image.Image
	img, format, err := image.Decode(r)
	if err != nil {
		panic(err.Error())
	}
	resized := resize.Resize(width, height, img, resize.Lanczos3)
	buf := new(bytes.Buffer)
	if err := jpeg.Encode(buf, resized, nil); err != nil {
		panic(err.Error())
	}
	return buf.Bytes()
}

func openDb() *gorp.DbMap {
	db, err := sql.Open("sqlite3", "./CDDatabase.db3")
	if err != nil {
		panic(err.Error())
	}
	return &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
}
<|endoftext|>


<commit_before>/*
 Webserver
*/

package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	r "github.com/dancannon/gorethink"
	"github.com/gorilla/context"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/mssola/user_agent"
	"log"
	"net/http"
	"net/url"
	"strconv"
)

func startServer() {
	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
	router.HandleFunc("/", redirectToDefault)
	router.HandleFunc(`/{board:\w+}`, addTrailingSlash)

	index := router.PathPrefix(`/{board:\w+}/`).Subrouter()
	index.HandleFunc("/", wrapHandler(false, boardPage))
	index.HandleFunc(`/{thread:\d+}`, wrapHandler(false, threadPage))

	api := router.PathPrefix("/api").Subrouter()
	posts := api.PathPrefix(`/{board:\w+}/`).Subrouter()
	posts.HandleFunc("/", wrapHandler(true, boardPage))
	posts.HandleFunc(`/{thread:\d+}`, wrapHandler(true, threadPage))

	// Serve static assets
	if config.Hard.HTTP.ServeStatic {
		// TODO: Apply headers, depending on debug mode
		router.PathPrefix("/").Handler(http.FileServer(http.Dir("./www")))
	}

	// Infer IP from header, if configured to
	var handler http.Handler
	if config.Hard.HTTP.TrustProxies {
		handler = handlers.ProxyHeaders(router)
	} else {
		handler = router
	}
	handler = getIdent(handler)

	log.Println("Listening on " + config.Hard.HTTP.Addr)
	http.ListenAndServe(config.Hard.HTTP.Addr, handler)
}

// Attach client access rights to request
func getIdent(handler http.Handler) http.Handler {
	fn := func(res http.ResponseWriter, req *http.Request) {
		context.Set(req, "ident", lookUpIdent(req.RemoteAddr))

		// Call the next handler in the chain
		handler.ServeHTTP(res, req)
	}

	return http.HandlerFunc(fn)
}

// Redirects to frontpage, if set, or the default board
func redirectToDefault(res http.ResponseWriter, req *http.Request) {
	if config.Frontpage != "" {
		http.ServeFile(res, req, config.Frontpage)
	} else {
		http.Redirect(res, req, "/"+config.Boards.Default+"/", 302)
	}
}

// Redirects `/board` to `/board/`. The client parses the URL to determine what
// page it is on. So we need the trailing slash for easier board determination
// and consistency.
func addTrailingSlash(res http.ResponseWriter, req *http.Request) {
	http.Redirect(res, req, "/"+mux.Vars(req)["board"]+"/", 301)
}

type handlerFunction func(http.ResponseWriter, *http.Request)
type handlerWrapper func(bool, http.ResponseWriter, *http.Request)

// wrapHandler returns a function with the first bool argument already assigned
func wrapHandler(json bool, handler handlerWrapper) handlerFunction {
	return func(res http.ResponseWriter, req *http.Request) {
		handler(json, res, req)
	}
}

// handles `/board/` page requests
func boardPage(jsonOnly bool, res http.ResponseWriter, req *http.Request) {
	in := indexPage{res: res, req: req, json: jsonOnly}
	board := mux.Vars(req)["board"]

	in.validate = func() bool {
		return canAccessBoard(board, in.ident)
	}

	in.getCounter = func() (counter int) {
		rGet(r.Table("main").
			Get("histCounts").
			Field("board").
			Default(0),
		).
			One(counter)
		return
	}

	in.getPostData = func() []byte {
		data := NewReader(board, in.ident).GetBoard()
		encoded, err := json.Marshal(data)
		throw(err)
		return encoded
	}

	in.process(board)
}

// Handles `/board/thread` requests
func threadPage(jsonOnly bool, res http.ResponseWriter, req *http.Request) {
	in := indexPage{res: res, req: req, json: jsonOnly}
	vars := mux.Vars(req)
	board := vars["board"]
	id, err := strconv.Atoi(vars["thread"])
	throw(err)
	in.lastN = detectLastN(req)

	in.validate = func() bool {
		return canAccessThread(id, board, in.ident)
	}

	in.getCounter = func() (counter int) {
		rGet(getThread(id).Field("histCtr")).One(&counter)
		return
	}

	in.getPostData = func() []byte {
		data := NewReader(board, in.ident).GetThread(id, in.lastN)
		encoded, err := json.Marshal(data)
		throw(err)
		return encoded
	}

	in.process(board)
}

// Stores common variables anf methods for both board and thread pages
type indexPage struct {
	res         http.ResponseWriter
	req         *http.Request
	validate    func() bool
	getCounter  func() int    // Progress counter used for building etags
	getPostData func() []byte // Post model JSON data
	lastN       int
	json        bool // Serve HTML from template or just JSON
	isMobile    bool
	template    templateStore
	ident       Ident
}

// Shared logic for handling both board and thread pages
func (in *indexPage) process(board string) {
	in.ident = extractIdent(in.res, in.req)
	if !in.validate() {
		send404(in.res)
		return
	}
	in.isMobile = user_agent.New(in.req.UserAgent()).Mobile()

	// Choose template to use
	if !in.json {
		if !in.isMobile {
			in.template = resources["index"]
		} else {
			in.template = resources["mobile"]
		}
	}
	if in.validateEtag() {
		postData := in.getPostData()
		if in.json { //Only the JSON
			in.res.Write(postData)
			return
		}

		// Concatenate the page together and write to client
		parts := in.template.Parts
		html := new(bytes.Buffer)
		html.Write(parts[0])
		html.Write(in.getPostData())
		html.Write(parts[1])
		html.Write(loginCredentials(in.ident))
		html.Write(parts[2])
		in.res.Write(html.Bytes())
	}
}

// Build an etag and check if it mathces the one provided by the client. If yes,
// send 304 and return false, otherwise set headers and return true.
func (in *indexPage) validateEtag() bool {
	etag := in.buildEtag()
	if config.Hard.Debug {
		setHeaders(in.res, noCacheHeaders)
		return true
	}
	hasAuth := in.ident.Auth != ""
	if hasAuth {
		etag += "-" + in.ident.Auth
	}
	if in.lastN != 0 {
		etag += fmt.Sprintf("-last%v", in.lastN)
	}

	// Etags match. No need to rerender.
	if ifNoneMatch, ok := in.req.Header["If-None-Match"]; ok {
		for _, clientEtag := range ifNoneMatch {
			if clientEtag == etag {
				in.res.WriteHeader(304)
				return false
			}
		}
	}

	setHeaders(in.res, vanillaHeaders)
	in.res.Header().Set("ETag", etag)
	if hasAuth {
		in.res.Header().Add("Cache-Control", ", private")
	}
	return true
}

// Build the main part of the etag
func (in *indexPage) buildEtag() string {
	etag := "W/" + strconv.Itoa(in.getCounter())
	if !in.json {
		etag += "-" + in.template.Hash
		if in.isMobile {
			etag += "-mobile"
		}
	}
	return etag
}

// Read client Identity struct, which was attached to the requests further
// upstream
func extractIdent(res http.ResponseWriter, req *http.Request) Ident {
	ident, ok := context.Get(req, "ident").(Ident)
	if !ok {
		res.WriteHeader(500)
		throw(errors.New("Failed Ident type assertion"))
	}
	return ident
}

func notFoundHandler(res http.ResponseWriter, req *http.Request) {
	send404(res)
}

func send404(res http.ResponseWriter) {
	res.WriteHeader(404)
	copyFile("www/404.html", res)
}

var noCacheHeaders = stringMap{
	"X-Frame-Options": "sameorigin",
	"Expires":         "Thu, 01 Jan 1970 00:00:00 GMT",
	"Cache-Control":   "no-cache, no-store",
}
var vanillaHeaders = stringMap{
	"Content-Type":    "text/html; charset=UTF-8",
	"X-Frame-Options": "sameorigin",
	"Cache-Control":   "max-age=0, must-revalidate",
	"Expires":         "Fri, 01 Jan 1990 00:00:00 GMT",
}

func setHeaders(res http.ResponseWriter, headers stringMap) {
	for key, val := range headers {
		res.Header().Set(key, val)
	}
}

// Inject staff login credentials, if any. These will be used to download the
// moderation JS client bundle.
func loginCredentials(ident Ident) []byte {
	// TODO: Inject the variables for our new login system

	return []byte{}
}

// Validate the client's last N posts to display setting
func detectLastN(req *http.Request) int {
	parsed, err := url.ParseRequestURI(req.RequestURI)
	throw(err)
	lastNSlice, ok := parsed.Query()["lastN"]
	if ok && len(lastNSlice) > 0 {
		lastN, err := strconv.Atoi(lastNSlice[0])
		throw(err)
		if lastN >= 5 && lastN <= 500 {
			return lastN
		}
	}
	return 0
}
<commit_msg>Serve configurations<commit_after>/*
 Webserver
*/

package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	r "github.com/dancannon/gorethink"
	"github.com/gorilla/context"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/mssola/user_agent"
	"log"
	"net/http"
	"net/url"
	"strconv"
)

func startServer() {
	router := mux.NewRouter()
	router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
	router.StrictSlash(true)
	router.HandleFunc("/", redirectToDefault)

	index := router.PathPrefix(`/{board:\w+}`).Subrouter()
	index.HandleFunc("/", wrapHandler(false, boardPage))
	index.HandleFunc(`/{thread:\d+}`, wrapHandler(false, threadPage))

	api := router.PathPrefix("/api").Subrouter()
	api.HandleFunc("/config", serveConfigs)
	posts := api.PathPrefix(`/{board:\w+}`).Subrouter()
	posts.HandleFunc("/", wrapHandler(true, boardPage))
	posts.HandleFunc(`/{thread:\d+}`, wrapHandler(true, threadPage))

	// Serve static assets
	if config.Hard.HTTP.ServeStatic {
		// TODO: Apply headers, depending on debug mode
		router.PathPrefix("/").Handler(http.FileServer(http.Dir("./www")))
	}

	// Infer IP from header, if configured to
	var handler http.Handler
	if config.Hard.HTTP.TrustProxies {
		handler = handlers.ProxyHeaders(router)
	} else {
		handler = router
	}
	handler = getIdent(handler)

	log.Println("Listening on " + config.Hard.HTTP.Addr)
	http.ListenAndServe(config.Hard.HTTP.Addr, handler)
}

// Attach client access rights to request
func getIdent(handler http.Handler) http.Handler {
	fn := func(res http.ResponseWriter, req *http.Request) {
		context.Set(req, "ident", lookUpIdent(req.RemoteAddr))

		// Call the next handler in the chain
		handler.ServeHTTP(res, req)
	}

	return http.HandlerFunc(fn)
}

// Redirects to frontpage, if set, or the default board
func redirectToDefault(res http.ResponseWriter, req *http.Request) {
	if config.Frontpage != "" {
		http.ServeFile(res, req, config.Frontpage)
	} else {
		http.Redirect(res, req, "/"+config.Boards.Default+"/", 302)
	}
}

type handlerFunction func(http.ResponseWriter, *http.Request)
type handlerWrapper func(bool, http.ResponseWriter, *http.Request)

// wrapHandler returns a function with the first bool argument already assigned
func wrapHandler(json bool, handler handlerWrapper) handlerFunction {
	return func(res http.ResponseWriter, req *http.Request) {
		handler(json, res, req)
	}
}

// handles `/board/` page requests
func boardPage(jsonOnly bool, res http.ResponseWriter, req *http.Request) {
	in := indexPage{res: res, req: req, json: jsonOnly}
	board := mux.Vars(req)["board"]

	in.validate = func() bool {
		return canAccessBoard(board, in.ident)
	}

	in.getCounter = func() (counter int) {
		rGet(r.Table("main").
			Get("histCounts").
			Field("board").
			Default(0),
		).
			One(counter)
		return
	}

	in.getPostData = func() []byte {
		data := NewReader(board, in.ident).GetBoard()
		encoded, err := json.Marshal(data)
		throw(err)
		return encoded
	}

	in.process(board)
}

// Handles `/board/thread` requests
func threadPage(jsonOnly bool, res http.ResponseWriter, req *http.Request) {
	in := indexPage{res: res, req: req, json: jsonOnly}
	vars := mux.Vars(req)
	board := vars["board"]
	id, err := strconv.Atoi(vars["thread"])
	throw(err)
	in.lastN = detectLastN(req)

	in.validate = func() bool {
		return canAccessThread(id, board, in.ident)
	}

	in.getCounter = func() (counter int) {
		rGet(getThread(id).Field("histCtr")).One(&counter)
		return
	}

	in.getPostData = func() []byte {
		data := NewReader(board, in.ident).GetThread(id, in.lastN)
		encoded, err := json.Marshal(data)
		throw(err)
		return encoded
	}

	in.process(board)
}

// Stores common variables anf methods for both board and thread pages
type indexPage struct {
	res         http.ResponseWriter
	req         *http.Request
	validate    func() bool
	getCounter  func() int    // Progress counter used for building etags
	getPostData func() []byte // Post model JSON data
	lastN       int
	json        bool // Serve HTML from template or just JSON
	isMobile    bool
	template    templateStore
	ident       Ident
}

// Shared logic for handling both board and thread pages
func (in *indexPage) process(board string) {
	in.ident = extractIdent(in.res, in.req)
	if !in.validate() {
		send404(in.res)
		return
	}
	in.isMobile = user_agent.New(in.req.UserAgent()).Mobile()

	// Choose template to use
	if !in.json {
		if !in.isMobile {
			in.template = resources["index"]
		} else {
			in.template = resources["mobile"]
		}
	}
	if in.validateEtag() {
		postData := in.getPostData()
		if in.json { //Only the JSON
			in.res.Write(postData)
			return
		}

		// Concatenate the page together and write to client
		parts := in.template.Parts
		html := new(bytes.Buffer)
		html.Write(parts[0])
		html.Write(in.getPostData())
		html.Write(parts[1])
		html.Write(loginCredentials(in.ident))
		html.Write(parts[2])
		in.res.Write(html.Bytes())
	}
}

// Build an etag and check if it mathces the one provided by the client. If yes,
// send 304 and return false, otherwise set headers and return true.
func (in *indexPage) validateEtag() bool {
	etag := in.buildEtag()
	if config.Hard.Debug {
		setHeaders(in.res, noCacheHeaders)
		return true
	}
	hasAuth := in.ident.Auth != ""
	if hasAuth {
		etag += "-" + in.ident.Auth
	}
	if in.lastN != 0 {
		etag += fmt.Sprintf("-last%v", in.lastN)
	}

	// Etags match. No need to rerender.
	if ifNoneMatch, ok := in.req.Header["If-None-Match"]; ok {
		for _, clientEtag := range ifNoneMatch {
			if clientEtag == etag {
				in.res.WriteHeader(304)
				return false
			}
		}
	}

	setHeaders(in.res, vanillaHeaders)
	in.res.Header().Set("ETag", etag)
	if hasAuth {
		in.res.Header().Add("Cache-Control", ", private")
	}
	return true
}

// Build the main part of the etag
func (in *indexPage) buildEtag() string {
	etag := "W/" + strconv.Itoa(in.getCounter())
	if !in.json {
		etag += "-" + in.template.Hash
		if in.isMobile {
			etag += "-mobile"
		}
	}
	return etag
}

// Read client Identity struct, which was attached to the requests further
// upstream
func extractIdent(res http.ResponseWriter, req *http.Request) Ident {
	ident, ok := context.Get(req, "ident").(Ident)
	if !ok {
		res.WriteHeader(500)
		throw(errors.New("Failed Ident type assertion"))
	}
	return ident
}

func notFoundHandler(res http.ResponseWriter, req *http.Request) {
	send404(res)
}

func send404(res http.ResponseWriter) {
	res.WriteHeader(404)
	copyFile("www/404.html", res)
}

var noCacheHeaders = stringMap{
	"X-Frame-Options": "sameorigin",
	"Expires":         "Thu, 01 Jan 1970 00:00:00 GMT",
	"Cache-Control":   "no-cache, no-store",
}
var vanillaHeaders = stringMap{
	"Content-Type":    "text/html; charset=UTF-8",
	"X-Frame-Options": "sameorigin",
	"Cache-Control":   "max-age=0, must-revalidate",
	"Expires":         "Fri, 01 Jan 1990 00:00:00 GMT",
}

func setHeaders(res http.ResponseWriter, headers stringMap) {
	for key, val := range headers {
		res.Header().Set(key, val)
	}
}

// Inject staff login credentials, if any. These will be used to download the
// moderation JS client bundle.
func loginCredentials(ident Ident) []byte {
	// TODO: Inject the variables for our new login system

	return []byte{}
}

// Validate the client's last N posts to display setting
func detectLastN(req *http.Request) int {
	parsed, err := url.ParseRequestURI(req.RequestURI)
	throw(err)
	lastNSlice, ok := parsed.Query()["lastN"]
	if ok && len(lastNSlice) > 0 {
		lastN, err := strconv.Atoi(lastNSlice[0])
		throw(err)
		if lastN >= 5 && lastN <= 500 {
			return lastN
		}
	}
	return 0
}

// Serve public configuration information as JSON
func serveConfigs(res http.ResponseWriter, req *http.Request) {
	data, err := json.Marshal(clientConfig)
	throw(err)
	res.Write(data)
}
<|endoftext|>


<commit_before>// Copyright 2013 Matthew Baird
// 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 elastigo

import (
	"encoding/json"
	"fmt"

	. "github.com/araddon/gou"
)

var (
	_ = DEBUG
)

// A bool (and/or) clause
type BoolClause string

// Filter clause is either a boolClause or FilterOp
type FilterClause interface {
	String() string
}

// A wrapper to allow for custom serialization
type FilterWrap struct {
	boolClause string
	filters    []interface{}
}

func NewFilterWrap() *FilterWrap {
	return &FilterWrap{filters: make([]interface{}, 0), boolClause: "and"}
}

func (f *FilterWrap) String() string {
	return fmt.Sprintf(`fopv: %d:%v`, len(f.filters), f.filters)
}

// Custom marshalling to support the query dsl
func (f *FilterWrap) addFilters(fl []interface{}) {
	if len(fl) > 1 {
		fc := fl[0]
		switch fc.(type) {
		case BoolClause, string:
			f.boolClause = fc.(string)
			fl = fl[1:]
		}
	}
	f.filters = append(f.filters, fl...)
}

// Custom marshalling to support the query dsl
func (f *FilterWrap) MarshalJSON() ([]byte, error) {
	var root interface{}
	if len(f.filters) > 1 {
		root = map[string]interface{}{f.boolClause: f.filters}
	} else if len(f.filters) == 1 {
		root = f.filters[0]
	}
	return json.Marshal(root)
}

/*
	"filter": {
		"range": {
		  "@timestamp": {
		    "from": "2012-12-29T16:52:48+00:00",
		    "to": "2012-12-29T17:52:48+00:00"
		  }
		}
	}
	"filter": {
	    "missing": {
	        "field": "repository.name"
	    }
	}

	"filter" : {
	    "terms" : {
	        "user" : ["kimchy", "elasticsearch"],
	        "execution" : "bool",
	        "_cache": true
	    }
	}

	"filter" : {
	    "term" : { "user" : "kimchy"}
	}

	"filter" : {
	    "and" : [
	        {
	            "range" : {
	                "postDate" : {
	                    "from" : "2010-03-01",
	                    "to" : "2010-04-01"
	                }
	            }
	        },
	        {
	            "prefix" : { "name.second" : "ba" }
	        }
	    ]
	}

*/

// Filter Operation
//
//   Filter().Term("user","kimchy")
//
//   // we use variadics to allow n arguments, first is the "field" rest are values
//   Filter().Terms("user", "kimchy", "elasticsearch")
//
//   Filter().Exists("repository.name")
//
func Filter() *FilterOp {
	return &FilterOp{}
}

func CompoundFilter(fl ...interface{}) *FilterWrap {
	FilterVal := NewFilterWrap()
	FilterVal.addFilters(fl)
	return FilterVal
}

type FilterOp struct {
	curField   string
	TermsMap   map[string][]interface{}          `json:"terms,omitempty"`
	Range      map[string]map[string]interface{} `json:"range,omitempty"`
	Exist      map[string]string                 `json:"exists,omitempty"`
	MissingVal map[string]string                 `json:"missing,omitempty"`
}

// A range is a special type of Filter operation
//
//    Range().Exists("repository.name")
func Range() *FilterOp {
	return &FilterOp{Range: make(map[string]map[string]interface{})}
}

func (f *FilterOp) Field(fld string) *FilterOp {
	f.curField = fld
	if _, ok := f.Range[fld]; !ok {
		m := make(map[string]interface{})
		f.Range[fld] = m
	}
	return f
}

// Filter Terms
//
//   Filter().Terms("user","kimchy")
//
//   // we use variadics to allow n arguments, first is the "field" rest are values
//   Filter().Terms("user", "kimchy", "elasticsearch")
//
func (f *FilterOp) Terms(field string, values ...interface{}) *FilterOp {
	if len(f.TermsMap) == 0 {
		f.TermsMap = make(map[string][]interface{})
	}
	for _, val := range values {
		f.TermsMap[field] = append(f.TermsMap[field], val)
	}

	return f
}
func (f *FilterOp) From(from string) *FilterOp {
	f.Range[f.curField]["from"] = from
	return f
}
func (f *FilterOp) To(to string) *FilterOp {
	f.Range[f.curField]["to"] = to
	return f
}
func (f *FilterOp) Gt(gt interface{}) *FilterOp {
	f.Range[f.curField]["gt"] = gt
	return f
}
func (f *FilterOp) Lt(lt interface{}) *FilterOp {
	f.Range[f.curField]["lt"] = lt
	return f
}
func (f *FilterOp) Exists(name string) *FilterOp {
	f.Exist = map[string]string{"field": name}
	return f
}
func (f *FilterOp) Missing(name string) *FilterOp {
	f.MissingVal = map[string]string{"field": name}
	return f
}

// Add another Filterop, "combines" two filter ops into one
func (f *FilterOp) Add(fop *FilterOp) *FilterOp {
	// TODO, this is invalid, refactor
	if len(fop.Exist) > 0 {
		f.Exist = fop.Exist
	}
	if len(fop.MissingVal) > 0 {
		f.MissingVal = fop.MissingVal
	}
	if len(fop.Range) > 0 {
		f.Range = fop.Range
	}
	return f
}
<commit_msg>Add setter method for boolean filter in dsl<commit_after>// Copyright 2013 Matthew Baird
// 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 elastigo

import (
	"encoding/json"
	"fmt"

	. "github.com/araddon/gou"
)

var (
	_ = DEBUG
)

// A bool (and/or) clause
type BoolClause string

// Filter clause is either a boolClause or FilterOp
type FilterClause interface {
	String() string
}

// A wrapper to allow for custom serialization
type FilterWrap struct {
	boolClause string
	filters    []interface{}
}

func NewFilterWrap() *FilterWrap {
	return &FilterWrap{filters: make([]interface{}, 0), boolClause: "and"}
}

func (f *FilterWrap) String() string {
	return fmt.Sprintf(`fopv: %d:%v`, len(f.filters), f.filters)
}

// Bool sets the type of boolean filter to use.
// Accepted values are "and" and "or".
func (f *FilterWrap) Bool(s string) {
	f.boolClause = s
}

// Custom marshalling to support the query dsl
func (f *FilterWrap) addFilters(fl []interface{}) {
	if len(fl) > 1 {
		fc := fl[0]
		switch fc.(type) {
		case BoolClause, string:
			f.boolClause = fc.(string)
			fl = fl[1:]
		}
	}
	f.filters = append(f.filters, fl...)
}

// Custom marshalling to support the query dsl
func (f *FilterWrap) MarshalJSON() ([]byte, error) {
	var root interface{}
	if len(f.filters) > 1 {
		root = map[string]interface{}{f.boolClause: f.filters}
	} else if len(f.filters) == 1 {
		root = f.filters[0]
	}
	return json.Marshal(root)
}

/*
	"filter": {
		"range": {
		  "@timestamp": {
		    "from": "2012-12-29T16:52:48+00:00",
		    "to": "2012-12-29T17:52:48+00:00"
		  }
		}
	}
	"filter": {
	    "missing": {
	        "field": "repository.name"
	    }
	}

	"filter" : {
	    "terms" : {
	        "user" : ["kimchy", "elasticsearch"],
	        "execution" : "bool",
	        "_cache": true
	    }
	}

	"filter" : {
	    "term" : { "user" : "kimchy"}
	}

	"filter" : {
	    "and" : [
	        {
	            "range" : {
	                "postDate" : {
	                    "from" : "2010-03-01",
	                    "to" : "2010-04-01"
	                }
	            }
	        },
	        {
	            "prefix" : { "name.second" : "ba" }
	        }
	    ]
	}

*/

// Filter Operation
//
//   Filter().Term("user","kimchy")
//
//   // we use variadics to allow n arguments, first is the "field" rest are values
//   Filter().Terms("user", "kimchy", "elasticsearch")
//
//   Filter().Exists("repository.name")
//
func Filter() *FilterOp {
	return &FilterOp{}
}

func CompoundFilter(fl ...interface{}) *FilterWrap {
	FilterVal := NewFilterWrap()
	FilterVal.addFilters(fl)
	return FilterVal
}

type FilterOp struct {
	curField   string
	TermsMap   map[string][]interface{}          `json:"terms,omitempty"`
	Range      map[string]map[string]interface{} `json:"range,omitempty"`
	Exist      map[string]string                 `json:"exists,omitempty"`
	MissingVal map[string]string                 `json:"missing,omitempty"`
}

// A range is a special type of Filter operation
//
//    Range().Exists("repository.name")
func Range() *FilterOp {
	return &FilterOp{Range: make(map[string]map[string]interface{})}
}

func (f *FilterOp) Field(fld string) *FilterOp {
	f.curField = fld
	if _, ok := f.Range[fld]; !ok {
		m := make(map[string]interface{})
		f.Range[fld] = m
	}
	return f
}

// Filter Terms
//
//   Filter().Terms("user","kimchy")
//
//   // we use variadics to allow n arguments, first is the "field" rest are values
//   Filter().Terms("user", "kimchy", "elasticsearch")
//
func (f *FilterOp) Terms(field string, values ...interface{}) *FilterOp {
	if len(f.TermsMap) == 0 {
		f.TermsMap = make(map[string][]interface{})
	}
	for _, val := range values {
		f.TermsMap[field] = append(f.TermsMap[field], val)
	}

	return f
}
func (f *FilterOp) From(from string) *FilterOp {
	f.Range[f.curField]["from"] = from
	return f
}
func (f *FilterOp) To(to string) *FilterOp {
	f.Range[f.curField]["to"] = to
	return f
}
func (f *FilterOp) Gt(gt interface{}) *FilterOp {
	f.Range[f.curField]["gt"] = gt
	return f
}
func (f *FilterOp) Lt(lt interface{}) *FilterOp {
	f.Range[f.curField]["lt"] = lt
	return f
}
func (f *FilterOp) Exists(name string) *FilterOp {
	f.Exist = map[string]string{"field": name}
	return f
}
func (f *FilterOp) Missing(name string) *FilterOp {
	f.MissingVal = map[string]string{"field": name}
	return f
}

// Add another Filterop, "combines" two filter ops into one
func (f *FilterOp) Add(fop *FilterOp) *FilterOp {
	// TODO, this is invalid, refactor
	if len(fop.Exist) > 0 {
		f.Exist = fop.Exist
	}
	if len(fop.MissingVal) > 0 {
		f.MissingVal = fop.MissingVal
	}
	if len(fop.Range) > 0 {
		f.Range = fop.Range
	}
	return f
}
<|endoftext|>


<commit_before>package licensee

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/flexiant/concerto/utils"
	"github.com/flexiant/concerto/webservice"
	"os"
	"text/tabwriter"
	"time"
)

type LicenseeReport struct {
	Id            string     `json:"id"`
	Year          int        `json:"year"`
	Month         time.Month `json:"month"`
	StartTime     time.Time  `json:"start_time"`
	EndTime       time.Time  `json:"end_time"`
	ServerSeconds float32    `json:"server_seconds"`
	Closed        bool       `json:"closed"`
}

func cmdList(c *cli.Context) {
	var licenseeReports []LicenseeReport

	webservice, err := webservice.NewWebService()
	utils.CheckError(err)

	data, err := webservice.Get("/v1/licensee/reports")
	utils.CheckError(err)

	err = json.Unmarshal(data, &licenseeReports)
	utils.CheckError(err)

	w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
	fmt.Fprintln(w, "ID\tYEAR\tMONTH\tSTART TIME\tEND TIME\tSERVER SECONDS\tCLOSED\r")

	for _, lr := range licenseeReports {
		fmt.Fprintf(w, "%s\t%s\t%d\t%g\t%d\t%s\t%s\n", lr.Id, lr.Year, lr.Month, lr.StartTime, lr.EndTime, lr.ServerSeconds, lr.Closed)
	}

	w.Flush()
}

func cmdShow(c *cli.Context) {
	// utils.FlagsRequired(c, []string{"id"})
	// var sp ServerPlan

	// webservice, err := webservice.NewWebService()
	// utils.CheckError(err)

	// data, err := webservice.Get(fmt.Sprintf("/v1/cloud/server_plans/%s", c.String("id")))
	// utils.CheckError(err)

	// err = json.Unmarshal(data, &sp)
	// utils.CheckError(err)

	// w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
	// fmt.Fprintln(w, "ID\tNAME\tMEMORY\tCPUS\tSTORAGE\tLOCATION ID\tCLOUD PROVIDER ID\r")
	// fmt.Fprintf(w, "%s\t%s\t%d\t%g\t%d\t%s\t%s\n", sp.Id, sp.Name, sp.Memory, sp.CPUs, sp.Storage, sp.LocationId, sp.CloudProviderId)

	// w.Flush()
}

func SubCommands() []cli.Command {
	return []cli.Command{
		{
			Name:   "list",
			Usage:  "Returns a list of reports for all the servers in the system",
			Action: cmdList,
		},
		{
			Name:   "show",
			Usage:  "Returns details about a particular report associated to any account group of the tenant.",
			Action: cmdShow,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "id",
					Usage: "Report id",
				},
			},
		},
	}
}
<commit_msg>fixed licensee reports<commit_after>package licensee

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/flexiant/concerto/utils"
	"github.com/flexiant/concerto/webservice"
	"os"
	"text/tabwriter"
	"time"
)

type LicenseeReport struct {
	Id            string     `json:"id"`
	Year          int        `json:"year"`
	Month         time.Month `json:"month"`
	StartTime     time.Time  `json:"start_time"`
	EndTime       time.Time  `json:"end_time"`
	ServerSeconds float32    `json:"server_seconds"`
	Closed        bool       `json:"closed"`
	Li            []Lines    `json:"lines"`
}

type Lines struct {
	Id                string    `json:"_id"`
	Commissioned_at   time.Time `json:"commissioned_at"`
	Decommissioned_at time.Time `json:"decommissioned_at"`
	Instance_id       string    `json:"instance_id"`
	Instance_name     string    `json:"instance_name"`
	Instance_fqdn     string    `json:"instance_fqdn"`
	Consumption       float32   `json:"consumption"`
}

func cmdList(c *cli.Context) {
	var licenseeReports []LicenseeReport

	webservice, err := webservice.NewWebService()
	utils.CheckError(err)

	data, err := webservice.Get("/v1/licensee/reports")
	utils.CheckError(err)

	err = json.Unmarshal(data, &licenseeReports)
	utils.CheckError(err)

	w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
	fmt.Fprintln(w, "ID\tYEAR\tMONTH\tSTART TIME\tEND TIME\tSERVER SECONDS\tCLOSED\r")

	for _, lr := range licenseeReports {
		fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%g\t%t\n", lr.Id, lr.Year, lr.Month, lr.StartTime, lr.EndTime, lr.ServerSeconds, lr.Closed)
	}

	w.Flush()
}

func cmdShow(c *cli.Context) {
	var vals LicenseeReport

	utils.FlagsRequired(c, []string{"id"})

	webservice, err := webservice.NewWebService()
	utils.CheckError(err)

	data, err := webservice.Get(fmt.Sprintf("/v1/licensee/reports/%s", c.String("id")))
	utils.CheckError(err)

	err = json.Unmarshal(data, &vals)
	utils.CheckError(err)

	w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)

	fmt.Fprintln(w, "REPORT ID\tYEAR\tMONTH\tSTART TIME\tEND TIME\tSERVER SECONDS\tCLOSED\r")
	fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%g\t%t\n", vals.Id, vals.Year, vals.Month, vals.StartTime, vals.EndTime, vals.ServerSeconds, vals.Closed)

	fmt.Fprintln(w, "LINES:\r")
	fmt.Fprintln(w, "ID\tCOMMISSIONED AT\tDECOMMISSIONED AT\tINSTANCE ID\tINSTANCE NAME\tINSTANCE FQDN\tCONSUMPTION\r")

	for _, l := range vals.Li {
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%g\n", l.Id, l.Commissioned_at, l.Decommissioned_at, l.Instance_id, l.Instance_name, l.Instance_fqdn, l.Consumption)
	}
	w.Flush()

}

func SubCommands() []cli.Command {
	return []cli.Command{
		{
			Name:   "list",
			Usage:  "Returns a list of reports for all the servers in the system",
			Action: cmdList,
		},
		{
			Name:   "show",
			Usage:  "Returns details about a particular report associated to any account group of the tenant.",
			Action: cmdShow,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "id",
					Usage: "Report id",
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2013, Cong Ding. All rights reserved.
//
// 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.
//
// author: Cong Ding <dinggnu@gmail.com>
//
package logging

import (
	"fmt"
	"strings"
)

// pre-defined formats
const (
	BasicFormat = "%s [%s] %s - %s\n name,levelname,asctime,message"
	RichFormat  = "%s [%s] %d %s - %d - %s:%s:%d - %s\n name, levelname, seqid, asctime, thread, filename, funcName, lineno, message"
)

// generate log string from the format setting
func (logger *logging) genLog(level Level, message string) string {
	format := strings.Split(logger.format, "\n")
	if len(format) != 2 {
		return "logging format error"
	}
	args := strings.Split(format[1], ",")
	fs := make([]interface{}, len(args))
	r := new(record)
	r.message = message
	r.level = level
	for k, v := range args {
		fs[k] = fields[strings.TrimSpace(v)](logger, r)
	}
	return fmt.Sprintf(format[0], fs...)
}
<commit_msg>align debug level and time<commit_after>// Copyright 2013, Cong Ding. All rights reserved.
//
// 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.
//
// author: Cong Ding <dinggnu@gmail.com>
//
package logging

import (
	"fmt"
	"strings"
)

// pre-defined formats
const (
	BasicFormat = "%s [%6s] %30s - %s\n name,levelname,asctime,message"
	RichFormat  = "%s [%6s] %d %30s - %d - %s:%s:%d - %s\n name, levelname, seqid, asctime, thread, filename, funcName, lineno, message"
)

// generate log string from the format setting
func (logger *logging) genLog(level Level, message string) string {
	format := strings.Split(logger.format, "\n")
	if len(format) != 2 {
		return "logging format error"
	}
	args := strings.Split(format[1], ",")
	fs := make([]interface{}, len(args))
	r := new(record)
	r.message = message
	r.level = level
	for k, v := range args {
		fs[k] = fields[strings.TrimSpace(v)](logger, r)
	}
	return fmt.Sprintf(format[0], fs...)
}
<|endoftext|>


<commit_before>package filter

// Match returns true if the given object matches the given filter.
func Match(obj any, clauses []Clause) bool {
	match := true

	for _, clause := range clauses {
		value := ValueOf(obj, clause.Field)
		clauseMatch := value == clause.Value

		if clause.Operator == "ne" {
			clauseMatch = !clauseMatch
		}

		// Finish out logic
		if clause.Not {
			clauseMatch = !clauseMatch
		}

		switch clause.PrevLogical {
		case "and":
			match = match && clauseMatch
		case "or":
			match = match || clauseMatch
		default:
			panic("unexpected clause operator")
		}
	}

	return match
}
<commit_msg>lxd/filter: Support case insensitive and regular expresions<commit_after>package filter

import (
	"reflect"
	"regexp"
	"strings"
)

// Match returns true if the given object matches the given filter.
func Match(obj any, clauses []Clause) bool {
	match := true

	for _, clause := range clauses {
		value := ValueOf(obj, clause.Field)
		var clauseMatch bool

		// If 'value' is type of string try to test value as a regexp
		// Comparison is case insensitive
		if reflect.ValueOf(value).Kind() == reflect.String {
			regexpValue := clause.Value
			if !(strings.Contains(regexpValue, "^") || strings.Contains(regexpValue, "$")) {
				regexpValue = "^" + regexpValue + "$"
			}

			r, err := regexp.Compile("(?i)" + regexpValue)
			// If not regexp compatible use original value.
			if err != nil {
				clauseMatch = strings.EqualFold(value.(string), clause.Value)
			} else {
				clauseMatch = r.MatchString(value.(string))
			}
		} else {
			clauseMatch = value == clause.Value
		}

		if clause.Operator == "ne" {
			clauseMatch = !clauseMatch
		}

		// Finish out logic
		if clause.Not {
			clauseMatch = !clauseMatch
		}

		switch clause.PrevLogical {
		case "and":
			match = match && clauseMatch
		case "or":
			match = match || clauseMatch
		default:
			panic("unexpected clause operator")
		}
	}

	return match
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/spf13/cobra"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/validate"
)

type cmdRecover struct {
	global *cmdGlobal
}

func (c *cmdRecover) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = "recover"
	cmd.Short = "Recover missing instances and volumes from existing and unknown storage pools"
	cmd.Long = `Description:
	Recover missing instances and volumes from existing and unknown storage pools

  This command is mostly used for disaster recovery. It will ask you about unknown storage pools and attempt to
  access them, along with existing storage pools, and identify any missing instances and volumes that exist on the
  pools but are not in the LXD database. It will then offer to recreate these database records.
`
	cmd.RunE = c.Run

	return cmd
}

func (c *cmdRecover) Run(cmd *cobra.Command, args []string) error {
	// Quick checks.
	if len(args) > 0 {
		return fmt.Errorf("Invalid arguments")
	}

	d, err := lxd.ConnectLXDUnix("", nil)
	if err != nil {
		return err
	}

	server, _, err := d.GetServer()
	if err != nil {
		return err
	}

	isClustered := d.IsClustered()

	// Get list of existing storage pools to scan.
	existingPools, err := d.GetStoragePools()
	if err != nil {
		return fmt.Errorf("Failed getting existing storage pools: %w", err)
	}

	fmt.Print("This LXD server currently has the following storage pools:\n")
	for _, existingPool := range existingPools {
		fmt.Printf(" - %s (backend=%q, source=%q)\n", existingPool.Name, existingPool.Driver, existingPool.Config["source"])
	}

	unknownPools := make([]api.StoragePoolsPost, 0, len(existingPools))

	// Build up a list of unknown pools to scan.
	// We don't offer this option if the server is clustered because we don't allow creating storage pools on
	// an individual server when clustered.
	if !isClustered {
		var supportedDriverNames []string

		for {
			addUnknownPool, err := cli.AskBool("Would you like to recover another storage pool? (yes/no) [default=no]: ", "no")
			if err != nil {
				return err
			}

			if !addUnknownPool {
				break
			}

			// Get available storage drivers if not done already.
			if supportedDriverNames == nil {
				for _, supportedDriver := range server.Environment.StorageSupportedDrivers {
					supportedDriverNames = append(supportedDriverNames, supportedDriver.Name)
				}
			}

			unknownPool := api.StoragePoolsPost{
				StoragePoolPut: api.StoragePoolPut{
					Config: make(map[string]string),
				},
			}

			unknownPool.Name, err = cli.AskString("Name of the storage pool: ", "", validate.Required(func(value string) error {
				if value == "" {
					return fmt.Errorf("Pool name cannot be empty")
				}

				for _, p := range unknownPools {
					if value == p.Name {
						return fmt.Errorf("Storage pool %q is already on recover list", value)
					}
				}

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

			unknownPool.Driver, err = cli.AskString(fmt.Sprintf("Name of the storage backend (%s): ", strings.Join(supportedDriverNames, ", ")), "", validate.IsOneOf(supportedDriverNames...))
			if err != nil {
				return err
			}

			unknownPool.Config["source"], err = cli.AskString("Source of the storage pool (block device, volume group, dataset, path, ... as applicable): ", "", validate.IsNotEmpty)
			if err != nil {
				return err
			}

			for {
				var configKey, configValue string
				_, _ = cli.AskString("Additional storage pool configuration property (KEY=VALUE, empty when done): ", "", validate.Optional(func(value string) error {
					configParts := strings.SplitN(value, "=", 2)
					if len(configParts) < 2 {
						return fmt.Errorf("Config option should be in the format KEY=VALUE")
					}

					configKey = configParts[0]
					configValue = configParts[1]

					return nil
				}))

				if configKey == "" {
					break
				}

				unknownPool.Config[configKey] = configValue
			}

			unknownPools = append(unknownPools, unknownPool)
		}
	}

	fmt.Printf("The recovery process will be scanning the following storage pools:\n")
	for _, p := range existingPools {
		fmt.Printf(" - EXISTING: %q (backend=%q, source=%q)\n", p.Name, p.Driver, p.Config["source"])
	}

	for _, p := range unknownPools {
		fmt.Printf(" - NEW: %q (backend=%q, source=%q)\n", p.Name, p.Driver, p.Config["source"])
	}

	proceed, err := cli.AskBool("Would you like to continue with scanning for lost volumes? (yes/no) [default=yes]: ", "yes")
	if err != nil {
		return err
	}

	if !proceed {
		return nil
	}

	fmt.Print("Scanning for unknown volumes...\n")

	// Send /internal/recover/validate request to LXD.
	reqValidate := internalRecoverValidatePost{
		Pools: make([]api.StoragePoolsPost, 0, len(existingPools)+len(unknownPools)),
	}

	// Add existing pools to request.
	for _, p := range existingPools {
		reqValidate.Pools = append(reqValidate.Pools, api.StoragePoolsPost{
			Name: p.Name, // Only send existing pool name, the rest will be looked up on server.
		})
	}

	// Add unknown pools to request.
	reqValidate.Pools = append(reqValidate.Pools, unknownPools...)

	for {
		resp, _, err := d.RawQuery("POST", "/internal/recover/validate", reqValidate, "")
		if err != nil {
			return fmt.Errorf("Failed validation request: %w", err)
		}

		var res internalRecoverValidateResult

		err = resp.MetadataAsStruct(&res)
		if err != nil {
			return fmt.Errorf("Failed parsing validation response: %w", err)
		}

		if len(res.UnknownVolumes) > 0 {
			fmt.Print("The following unknown volumes have been found:\n")
			for _, unknownVol := range res.UnknownVolumes {
				fmt.Printf(" - %s %q on pool %q in project %q (includes %d snapshots)\n", strings.Title(unknownVol.Type), unknownVol.Name, unknownVol.Pool, unknownVol.Project, unknownVol.SnapshotCount)
			}
		}

		if len(res.DependencyErrors) > 0 {
			fmt.Print("You are currently missing the following:\n")

			for _, depErr := range res.DependencyErrors {
				fmt.Printf(" - %s\n", depErr)
			}

			_, _ = cli.AskString("Please create those missing entries and then hit ENTER: ", "", validate.Optional())
		} else {
			if len(res.UnknownVolumes) <= 0 {
				fmt.Print("No unknown volumes found. Nothing to do.\n")
				return nil
			}

			break // Dependencies met.
		}
	}

	proceed, err = cli.AskBool("Would you like those to be recovered? (yes/no) [default=no]: ", "no")
	if err != nil {
		return err
	}

	if !proceed {
		return nil
	}

	fmt.Print("Starting recovery...\n")

	// Send /internal/recover/import request to LXD.
	// Don't lint next line with gosimple. It says we should convert reqValidate directly to an internalRecoverImportPost
	// because their types are identical. This is less clear and will not work if either type changes in the future.
	reqImport := internalRecoverImportPost{ //nolint:gosimple
		Pools: reqValidate.Pools,
	}

	_, _, err = d.RawQuery("POST", "/internal/recover/import", reqImport, "")
	if err != nil {
		return fmt.Errorf("Failed import request: %w", err)
	}

	return nil
}
<commit_msg>lxd: Replace deprecated strings.Title.<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/spf13/cobra"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/validate"
)

type cmdRecover struct {
	global *cmdGlobal
}

func (c *cmdRecover) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = "recover"
	cmd.Short = "Recover missing instances and volumes from existing and unknown storage pools"
	cmd.Long = `Description:
	Recover missing instances and volumes from existing and unknown storage pools

  This command is mostly used for disaster recovery. It will ask you about unknown storage pools and attempt to
  access them, along with existing storage pools, and identify any missing instances and volumes that exist on the
  pools but are not in the LXD database. It will then offer to recreate these database records.
`
	cmd.RunE = c.Run

	return cmd
}

func (c *cmdRecover) Run(cmd *cobra.Command, args []string) error {
	// Quick checks.
	if len(args) > 0 {
		return fmt.Errorf("Invalid arguments")
	}

	d, err := lxd.ConnectLXDUnix("", nil)
	if err != nil {
		return err
	}

	server, _, err := d.GetServer()
	if err != nil {
		return err
	}

	isClustered := d.IsClustered()

	// Get list of existing storage pools to scan.
	existingPools, err := d.GetStoragePools()
	if err != nil {
		return fmt.Errorf("Failed getting existing storage pools: %w", err)
	}

	fmt.Print("This LXD server currently has the following storage pools:\n")
	for _, existingPool := range existingPools {
		fmt.Printf(" - %s (backend=%q, source=%q)\n", existingPool.Name, existingPool.Driver, existingPool.Config["source"])
	}

	unknownPools := make([]api.StoragePoolsPost, 0, len(existingPools))

	// Build up a list of unknown pools to scan.
	// We don't offer this option if the server is clustered because we don't allow creating storage pools on
	// an individual server when clustered.
	if !isClustered {
		var supportedDriverNames []string

		for {
			addUnknownPool, err := cli.AskBool("Would you like to recover another storage pool? (yes/no) [default=no]: ", "no")
			if err != nil {
				return err
			}

			if !addUnknownPool {
				break
			}

			// Get available storage drivers if not done already.
			if supportedDriverNames == nil {
				for _, supportedDriver := range server.Environment.StorageSupportedDrivers {
					supportedDriverNames = append(supportedDriverNames, supportedDriver.Name)
				}
			}

			unknownPool := api.StoragePoolsPost{
				StoragePoolPut: api.StoragePoolPut{
					Config: make(map[string]string),
				},
			}

			unknownPool.Name, err = cli.AskString("Name of the storage pool: ", "", validate.Required(func(value string) error {
				if value == "" {
					return fmt.Errorf("Pool name cannot be empty")
				}

				for _, p := range unknownPools {
					if value == p.Name {
						return fmt.Errorf("Storage pool %q is already on recover list", value)
					}
				}

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

			unknownPool.Driver, err = cli.AskString(fmt.Sprintf("Name of the storage backend (%s): ", strings.Join(supportedDriverNames, ", ")), "", validate.IsOneOf(supportedDriverNames...))
			if err != nil {
				return err
			}

			unknownPool.Config["source"], err = cli.AskString("Source of the storage pool (block device, volume group, dataset, path, ... as applicable): ", "", validate.IsNotEmpty)
			if err != nil {
				return err
			}

			for {
				var configKey, configValue string
				_, _ = cli.AskString("Additional storage pool configuration property (KEY=VALUE, empty when done): ", "", validate.Optional(func(value string) error {
					configParts := strings.SplitN(value, "=", 2)
					if len(configParts) < 2 {
						return fmt.Errorf("Config option should be in the format KEY=VALUE")
					}

					configKey = configParts[0]
					configValue = configParts[1]

					return nil
				}))

				if configKey == "" {
					break
				}

				unknownPool.Config[configKey] = configValue
			}

			unknownPools = append(unknownPools, unknownPool)
		}
	}

	fmt.Printf("The recovery process will be scanning the following storage pools:\n")
	for _, p := range existingPools {
		fmt.Printf(" - EXISTING: %q (backend=%q, source=%q)\n", p.Name, p.Driver, p.Config["source"])
	}

	for _, p := range unknownPools {
		fmt.Printf(" - NEW: %q (backend=%q, source=%q)\n", p.Name, p.Driver, p.Config["source"])
	}

	proceed, err := cli.AskBool("Would you like to continue with scanning for lost volumes? (yes/no) [default=yes]: ", "yes")
	if err != nil {
		return err
	}

	if !proceed {
		return nil
	}

	fmt.Print("Scanning for unknown volumes...\n")

	// Send /internal/recover/validate request to LXD.
	reqValidate := internalRecoverValidatePost{
		Pools: make([]api.StoragePoolsPost, 0, len(existingPools)+len(unknownPools)),
	}

	// Add existing pools to request.
	for _, p := range existingPools {
		reqValidate.Pools = append(reqValidate.Pools, api.StoragePoolsPost{
			Name: p.Name, // Only send existing pool name, the rest will be looked up on server.
		})
	}

	// Add unknown pools to request.
	reqValidate.Pools = append(reqValidate.Pools, unknownPools...)

	for {
		resp, _, err := d.RawQuery("POST", "/internal/recover/validate", reqValidate, "")
		if err != nil {
			return fmt.Errorf("Failed validation request: %w", err)
		}

		var res internalRecoverValidateResult

		err = resp.MetadataAsStruct(&res)
		if err != nil {
			return fmt.Errorf("Failed parsing validation response: %w", err)
		}

		if len(res.UnknownVolumes) > 0 {
			fmt.Print("The following unknown volumes have been found:\n")
			for _, unknownVol := range res.UnknownVolumes {
				fmt.Printf(" - %s %q on pool %q in project %q (includes %d snapshots)\n", cases.Title(language.English).String(unknownVol.Type), unknownVol.Name, unknownVol.Pool, unknownVol.Project, unknownVol.SnapshotCount)
			}
		}

		if len(res.DependencyErrors) > 0 {
			fmt.Print("You are currently missing the following:\n")

			for _, depErr := range res.DependencyErrors {
				fmt.Printf(" - %s\n", depErr)
			}

			_, _ = cli.AskString("Please create those missing entries and then hit ENTER: ", "", validate.Optional())
		} else {
			if len(res.UnknownVolumes) <= 0 {
				fmt.Print("No unknown volumes found. Nothing to do.\n")
				return nil
			}

			break // Dependencies met.
		}
	}

	proceed, err = cli.AskBool("Would you like those to be recovered? (yes/no) [default=no]: ", "no")
	if err != nil {
		return err
	}

	if !proceed {
		return nil
	}

	fmt.Print("Starting recovery...\n")

	// Send /internal/recover/import request to LXD.
	// Don't lint next line with gosimple. It says we should convert reqValidate directly to an internalRecoverImportPost
	// because their types are identical. This is less clear and will not work if either type changes in the future.
	reqImport := internalRecoverImportPost{ //nolint:gosimple
		Pools: reqValidate.Pools,
	}

	_, _, err = d.RawQuery("POST", "/internal/recover/import", reqImport, "")
	if err != nil {
		return fmt.Errorf("Failed import request: %w", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/lxd/network/acl"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/version"
)

var networkACLsCmd = APIEndpoint{
	Path: "network-acls",

	Get:  APIEndpointAction{Handler: networkACLsGet, AccessHandler: allowProjectPermission("networks", "view")},
	Post: APIEndpointAction{Handler: networkACLsPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

var networkACLCmd = APIEndpoint{
	Path: "network-acls/{name}",

	Delete: APIEndpointAction{Handler: networkACLDelete, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Get:    APIEndpointAction{Handler: networkACLGet, AccessHandler: allowProjectPermission("networks", "view")},
	Put:    APIEndpointAction{Handler: networkACLPut, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Post:   APIEndpointAction{Handler: networkACLPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

// API endpoints.

// List Network ACLs.
func networkACLsGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	recursion := util.IsRecursionRequest(r)

	// Get list of Network ACLs.
	aclNames, err := d.cluster.GetNetworkACLs(projectName)
	if err != nil {
		return response.InternalError(err)
	}

	resultString := []string{}
	resultMap := []api.NetworkACL{}
	for _, aclName := range aclNames {
		if !recursion {
			resultString = append(resultString, fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, aclName))
		} else {
			netACL, err := acl.LoadByName(d.State(), projectName, aclName)
			if err != nil {
				continue
			}

			netACLInfo := netACL.Info()
			netACLInfo.UsedBy, _ = netACL.UsedBy() // Ignore errors in UsedBy, will return nil.

			resultMap = append(resultMap, *netACLInfo)
		}
	}

	if !recursion {
		return response.SyncResponse(true, resultString)
	}

	return response.SyncResponse(true, resultMap)
}

// Create Network ACL.
func networkACLsPost(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	req := api.NetworkACLsPost{}

	// Parse the request into a record.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	_, err = acl.LoadByName(d.State(), projectName, req.Name)
	if err == nil {
		return response.BadRequest(fmt.Errorf("The network ACL already exists"))
	}

	err = acl.Create(d.State(), projectName, &req)
	if err != nil {
		return response.SmartError(err)
	}

	url := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, req.Name)
	return response.SyncResponseLocation(true, nil, url)
}

// Delete Network ACL.
func networkACLDelete(d *Daemon, r *http.Request) response.Response {
	return response.NotImplemented(nil)
}

// Show Network ACL.
func networkACLGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	info := netACL.Info()
	info.UsedBy, err = netACL.UsedBy()
	if err != nil {
		return response.SmartError(err)
	}

	return response.SyncResponseETag(true, info, netACL.Etag())
}

// Update Network ACL.
func networkACLPut(d *Daemon, r *http.Request) response.Response {
	return response.NotImplemented(nil)
}

// Rename Network ACL.
func networkACLPost(d *Daemon, r *http.Request) response.Response {
	return response.NotImplemented(nil)
}
<commit_msg>lxd/network/acls: Implements networkACLPut function<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/lxd/network/acl"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/version"
)

var networkACLsCmd = APIEndpoint{
	Path: "network-acls",

	Get:  APIEndpointAction{Handler: networkACLsGet, AccessHandler: allowProjectPermission("networks", "view")},
	Post: APIEndpointAction{Handler: networkACLsPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

var networkACLCmd = APIEndpoint{
	Path: "network-acls/{name}",

	Delete: APIEndpointAction{Handler: networkACLDelete, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Get:    APIEndpointAction{Handler: networkACLGet, AccessHandler: allowProjectPermission("networks", "view")},
	Put:    APIEndpointAction{Handler: networkACLPut, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Post:   APIEndpointAction{Handler: networkACLPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

// API endpoints.

// List Network ACLs.
func networkACLsGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	recursion := util.IsRecursionRequest(r)

	// Get list of Network ACLs.
	aclNames, err := d.cluster.GetNetworkACLs(projectName)
	if err != nil {
		return response.InternalError(err)
	}

	resultString := []string{}
	resultMap := []api.NetworkACL{}
	for _, aclName := range aclNames {
		if !recursion {
			resultString = append(resultString, fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, aclName))
		} else {
			netACL, err := acl.LoadByName(d.State(), projectName, aclName)
			if err != nil {
				continue
			}

			netACLInfo := netACL.Info()
			netACLInfo.UsedBy, _ = netACL.UsedBy() // Ignore errors in UsedBy, will return nil.

			resultMap = append(resultMap, *netACLInfo)
		}
	}

	if !recursion {
		return response.SyncResponse(true, resultString)
	}

	return response.SyncResponse(true, resultMap)
}

// Create Network ACL.
func networkACLsPost(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	req := api.NetworkACLsPost{}

	// Parse the request into a record.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	_, err = acl.LoadByName(d.State(), projectName, req.Name)
	if err == nil {
		return response.BadRequest(fmt.Errorf("The network ACL already exists"))
	}

	err = acl.Create(d.State(), projectName, &req)
	if err != nil {
		return response.SmartError(err)
	}

	url := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, req.Name)
	return response.SyncResponseLocation(true, nil, url)
}

// Delete Network ACL.
func networkACLDelete(d *Daemon, r *http.Request) response.Response {
	return response.NotImplemented(nil)
}

// Show Network ACL.
func networkACLGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	info := netACL.Info()
	info.UsedBy, err = netACL.UsedBy()
	if err != nil {
		return response.SmartError(err)
	}

	return response.SyncResponseETag(true, info, netACL.Etag())
}

// Update Network ACL.
func networkACLPut(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	// Get the existing Network ACL.
	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	// Validate the ETag.
	err = util.EtagCheck(r, netACL.Etag())
	if err != nil {
		return response.PreconditionFailed(err)
	}

	req := api.NetworkACLPut{}

	// Decode the request.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	err = netACL.Update(&req)
	if err != nil {
		return response.SmartError(err)
	}

	return response.EmptySyncResponse
}

// Rename Network ACL.
func networkACLPost(d *Daemon, r *http.Request) response.Response {
	return response.NotImplemented(nil)
}
<|endoftext|>


<commit_before>package make

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"os/exec"
)

func (Site *Site) ActionDestroyDatabases() {
	var dbDeleteCount int
	for _, database := range Site.DatabasesGet() {
		sqlQuery := fmt.Sprintf("DROP DATABASE %v;", database)
		sqlUser := fmt.Sprintf("--user=%v", Site.database.getUser())
		sqlPass := fmt.Sprintf("--password=%v", Site.database.getPass())
		_, err := exec.Command("mysql", sqlUser, sqlPass, "-e", sqlQuery).Output()
		if err == nil {
			log.Infoln("Dropped database", database)
			dbDeleteCount++
		} else {
			log.Warnln("Could not drop database", database, err)
		}
	}
	if dbDeleteCount == 0 {
		log.Warnln("No database was found")
	} else {
		log.Infof("%v databases were removed", dbDeleteCount)
	}
}

func (Site *Site) ActionDestroyAlias() {
	Site.AliasUninstall()
}

func (Site *Site) ActionDestroyVhost() {
	Site.VhostUninstall()
}

func (Site *Site) ActionDestroyPermissions() {
	privateFilesPath := Site.Path + "/" + Site.Name + ".latest/sites/" + Site.Name

	chmodErr := os.Chmod(privateFilesPath, 0777)
	if chmodErr != nil {
		log.Warnf("Could not set permissions of %v to %v: %v", privateFilesPath, "0777", chmodErr)
	} else {
		log.Infof("Set permissions of %v to %v", privateFilesPath, "0777")
	}
}

func (Site *Site) ActionDestroySym() {
	Site.SymUninstall(Site.Timestamp)
}

func (Site *Site) ActionDestroyFiles() {
	_, statErr := os.Stat(Site.Path)
	if statErr == nil {
		err := os.RemoveAll(Site.Path)
		if err != nil {
			log.Warnf("Could not remove file system for %v at %v\n", Site.Name, Site.Path)
		} else {
			log.Infof("Removed file system for %v at %v\n", Site.Name, Site.Path)
		}
	} else {
		log.Warnln("Site directory was not found: ", Site.Path)
	}
}

func (Site *Site) ActionDestroy() {
	// Destroy will remove all traces of said site.
	Site.ActionDestroyDatabases()
	Site.ActionDestroyAlias()
	Site.ActionDestroyVhost()
	Site.ActionDestroyPermissions()
	Site.ActionDestroyFiles()
	Site.ActionDestroySym()
}
<commit_msg>Add support for multiple builds in context to site removal.<commit_after>package make

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"os/exec"
)

func (Site *Site) ActionDestroyDatabases() {
	var dbDeleteCount int
	for _, database := range Site.DatabasesGet() {
		sqlQuery := fmt.Sprintf("DROP DATABASE %v;", database)
		sqlUser := fmt.Sprintf("--user=%v", Site.database.getUser())
		sqlPass := fmt.Sprintf("--password=%v", Site.database.getPass())
		_, err := exec.Command("mysql", sqlUser, sqlPass, "-e", sqlQuery).Output()
		if err == nil {
			log.Infoln("Dropped database", database)
			dbDeleteCount++
		} else {
			log.Warnln("Could not drop database", database, err)
		}
	}
	if dbDeleteCount == 0 {
		log.Warnln("No database was found")
	} else {
		log.Infof("%v databases were removed", dbDeleteCount)
	}
}

func (Site *Site) ActionDestroyAlias() {
	Site.AliasUninstall()
}

func (Site *Site) ActionDestroyVhost() {
	Site.VhostUninstall()
}

func (Site *Site) ActionDestroyPermissions() {
	privateFilesPath := Site.Path
	_, statErr := os.Stat(privateFilesPath)
	if statErr == nil {
		files, _ := ioutil.ReadDir(privateFilesPath)
		for _, file := range files {
			privateFilesPathTarget := privateFilesPath + "/" + file.Name() + "/sites/" + Site.Name
			chmodErr := os.Chmod(privateFilesPathTarget, 0777)
			if chmodErr != nil {
				log.Warnf("Could not set permissions of %v to %v: %v", privateFilesPathTarget, "0777", chmodErr)
			} else {
				log.Infof("Set permissions of %v to %v", privateFilesPathTarget, "0777")
			}
		}
	} else {
		log.Warnln("Could not find target folders", privateFilesPath)
	}
}

func (Site *Site) ActionDestroySym() {
	Site.SymUninstall(Site.Timestamp)
}

func (Site *Site) ActionDestroyFiles() {
	_, statErr := os.Stat(Site.Path)
	if statErr == nil {
		err := os.RemoveAll(Site.Path)
		if err != nil {
			log.Warnf("Could not remove file system for %v at %v\n", Site.Name, Site.Path)
		} else {
			log.Infof("Removed file system for %v at %v\n", Site.Name, Site.Path)
		}
	} else {
		log.Warnln("Site directory was not found: ", Site.Path)
	}
}

func (Site *Site) ActionDestroy() {
	// Destroy will remove all traces of said site.
	Site.ActionDestroyDatabases()
	Site.ActionDestroyAlias()
	Site.ActionDestroyVhost()
	Site.ActionDestroyPermissions()
	Site.ActionDestroyFiles()
	Site.ActionDestroySym()
}
<|endoftext|>


<commit_before>// Package marathon is the Marathon implementation for a Panamax Remote Adapter.
package marathon // import "github.com/CenturyLinkLabs/panamax-marathon-adapter/marathon"

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

	"github.com/CenturyLinkLabs/gomarathon"
	"github.com/CenturyLinkLabs/panamax-marathon-adapter/api"
	"github.com/satori/go.uuid"
)

// Creates a client connection to Marathon on the provided endpoint.
func newClient(endpoint string) *gomarathon.Client {
	url := endpoint
	if endpoint != "" {
		url = endpoint
	}
	log.Printf("Marathon Endpoint: %s", url)
	c, err := gomarathon.NewClient(url, nil)
	if err != nil {
		log.Fatal(err)
	}
	return c
}

type gomarathonClientAbstractor interface {
	ListApps() (*gomarathon.Response, error)
	GetApp(string) (*gomarathon.Response, error)
	GetAppTasks(string) (*gomarathon.Response, error)
	CreateApp(*gomarathon.Application) (*gomarathon.Response, error)
	CreateGroup(*gomarathon.Group) (*gomarathon.Response, error)
	DeleteApp(string) (*gomarathon.Response, error)
	DeleteGroup(string) (*gomarathon.Response, error)
}

type marathonAdapter struct {
	client      gomarathonClientAbstractor
	conv        PanamaxServiceConverter
	generateUID func() string
}

func NewMarathonAdapter(endpoint string) *marathonAdapter {
	adapter := new(marathonAdapter)
	adapter.client = newClient(endpoint)
	adapter.conv = new(MarathonConverter)
	adapter.generateUID = func() string { return fmt.Sprintf("%s", uuid.NewV4()) }
	return adapter
}

func (m *marathonAdapter) GetServices() ([]*api.Service, *api.Error) {
	var apiErr *api.Error

	response, err := m.client.ListApps()
	if err != nil {
		apiErr = api.NewError(http.StatusNotFound, err.Error())
	}
	return m.conv.convertToServices(response.Apps), apiErr
}

func (m *marathonAdapter) GetService(id string) (*api.Service, *api.Error) {
	var apiErr *api.Error

	response, err := m.client.GetApp(m.sanitizeMarathonAppURL(id))
	if err != nil {
		apiErr = api.NewError(http.StatusNotFound, err.Error())
	}
	return m.conv.convertToService(response.App), apiErr
}

func (m *marathonAdapter) CreateServices(services []*api.Service) ([]*api.Service, *api.Error) {
	var apiErr *api.Error
	var deployments = make([]deployment, len(services))
	g := m.generateUID()

	dependents := m.findDependencies(services)
	for i := range services {
		if dependents[services[i].Name] != 0 {
			services[i].Deployment.Count = 1
		}

		m.prepareServiceForDeployment(g, services[i])
		deployments[i] = createDeployment(services[i], m.client)
	}

	myGroup := new(deploymentGroup)
	myGroup.deployments = deployments

	status := deployGroup(myGroup, DEPLOY_TIMEOUT)

	switch status.code {
	case FAIL:
		apiErr = api.NewError(http.StatusConflict, "Group deployment failed.")
	case TIMEOUT:
		apiErr = api.NewError(http.StatusInternalServerError, "Group deployment timed out.")
	}

	return services, apiErr
}

func (m *marathonAdapter) UpdateService(s *api.Service) *api.Error {
	return nil
}

func (m *marathonAdapter) DestroyService(id string) *api.Error {
	var apiErr *api.Error
	group, _ := splitServiceId(id, ".")

	_, err := m.client.DeleteApp(m.sanitizeMarathonAppURL(id))
	if err != nil {
		apiErr = api.NewError(http.StatusNotFound, err.Error())
	}

	m.client.DeleteGroup(group) // Remove group if possible we dont care about error or return.

	return apiErr
}

func (m *marathonAdapter) prepareServiceForDeployment(group string, service *api.Service) {
	var serviceName = service.Name

	service.Id = fmt.Sprintf("%s.%s", group, serviceName)
	service.Name = fmt.Sprintf("/%s/%s", group, serviceName)
	service.ActualState = "deployed"
}

func (m *marathonAdapter) sanitizeMarathonAppURL(id string) string {
	group, service := splitServiceId(id, ".")
	return fmt.Sprintf("%s/%s", strings.ToLower(group), strings.ToLower(service))
}

func (m *marathonAdapter) findDependencies(services []*api.Service) map[string]int {
	var deps = make(map[string]int)
	for s := range services {
		for l := range services[s].Links {
			deps[services[s].Links[l].Name] = 1
		}
	}

	return deps
}
<commit_msg>Sanitize service name to remove invalid chars.<commit_after>// Package marathon is the Marathon implementation for a Panamax Remote Adapter.
package marathon // import "github.com/CenturyLinkLabs/panamax-marathon-adapter/marathon"

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

	"github.com/CenturyLinkLabs/gomarathon"
	"github.com/CenturyLinkLabs/panamax-marathon-adapter/api"
	"github.com/satori/go.uuid"
)

// Creates a client connection to Marathon on the provided endpoint.
func newClient(endpoint string) *gomarathon.Client {
	url := endpoint
	if endpoint != "" {
		url = endpoint
	}
	log.Printf("Marathon Endpoint: %s", url)
	c, err := gomarathon.NewClient(url, nil)
	if err != nil {
		log.Fatal(err)
	}
	return c
}

func sanitizeServiceName(name string) string {
	name = strings.Replace(name, " ", "", -1)
	name = strings.Replace(name, "-", "", -1)
	name = strings.Replace(name, "_", "", -1)
	name = strings.Replace(name, ",", "", -1)
	return name
}

type gomarathonClientAbstractor interface {
	ListApps() (*gomarathon.Response, error)
	GetApp(string) (*gomarathon.Response, error)
	GetAppTasks(string) (*gomarathon.Response, error)
	CreateApp(*gomarathon.Application) (*gomarathon.Response, error)
	CreateGroup(*gomarathon.Group) (*gomarathon.Response, error)
	DeleteApp(string) (*gomarathon.Response, error)
	DeleteGroup(string) (*gomarathon.Response, error)
}

type marathonAdapter struct {
	client      gomarathonClientAbstractor
	conv        PanamaxServiceConverter
	generateUID func() string
}

func NewMarathonAdapter(endpoint string) *marathonAdapter {
	adapter := new(marathonAdapter)
	adapter.client = newClient(endpoint)
	adapter.conv = new(MarathonConverter)
	adapter.generateUID = func() string { return fmt.Sprintf("%s", uuid.NewV4()) }
	return adapter
}

func (m *marathonAdapter) GetServices() ([]*api.Service, *api.Error) {
	var apiErr *api.Error

	response, err := m.client.ListApps()
	if err != nil {
		apiErr = api.NewError(http.StatusNotFound, err.Error())
	}
	return m.conv.convertToServices(response.Apps), apiErr
}

func (m *marathonAdapter) GetService(id string) (*api.Service, *api.Error) {
	var apiErr *api.Error

	response, err := m.client.GetApp(m.sanitizeMarathonAppURL(id))
	if err != nil {
		apiErr = api.NewError(http.StatusNotFound, err.Error())
	}
	return m.conv.convertToService(response.App), apiErr
}

func (m *marathonAdapter) CreateServices(services []*api.Service) ([]*api.Service, *api.Error) {
	var apiErr *api.Error
	var deployments = make([]deployment, len(services))
	g := m.generateUID()

	dependents := m.findDependencies(services)
	for i := range services {
		if dependents[services[i].Name] != 0 {
			services[i].Deployment.Count = 1
		}

		m.prepareServiceForDeployment(g, services[i])
		deployments[i] = createDeployment(services[i], m.client)
	}

	myGroup := new(deploymentGroup)
	myGroup.deployments = deployments

	status := deployGroup(myGroup, DEPLOY_TIMEOUT)

	switch status.code {
	case FAIL:
		apiErr = api.NewError(http.StatusConflict, "Group deployment failed.")
	case TIMEOUT:
		apiErr = api.NewError(http.StatusInternalServerError, "Group deployment timed out.")
	}

	return services, apiErr
}

func (m *marathonAdapter) UpdateService(s *api.Service) *api.Error {
	return nil
}

func (m *marathonAdapter) DestroyService(id string) *api.Error {
	var apiErr *api.Error
	group, _ := splitServiceId(id, ".")

	_, err := m.client.DeleteApp(m.sanitizeMarathonAppURL(id))
	if err != nil {
		apiErr = api.NewError(http.StatusNotFound, err.Error())
	}

	m.client.DeleteGroup(group) // Remove group if possible we dont care about error or return.

	return apiErr
}

func (m *marathonAdapter) prepareServiceForDeployment(group string, service *api.Service) {
	var serviceName = sanitizeServiceName(service.Name)

	service.Id = fmt.Sprintf("%s.%s", group, serviceName)
	service.Name = fmt.Sprintf("/%s/%s", group, serviceName)
	service.ActualState = "deployed"
}

func (m *marathonAdapter) sanitizeMarathonAppURL(id string) string {
	group, service := splitServiceId(id, ".")
	return fmt.Sprintf("%s/%s", strings.ToLower(group), strings.ToLower(service))
}

func (m *marathonAdapter) findDependencies(services []*api.Service) map[string]int {
	var deps = make(map[string]int)
	for s := range services {
		for l := range services[s].Links {
			deps[services[s].Links[l].Name] = 1
		}
	}

	return deps
}
<|endoftext|>


<commit_before>package matrix

// Implementation of a matrix over GF(2).  Used to find
// linear combinations of rows which are zero.
type BitMatrix struct {
	// size of matrix
	n uint

	// ids of rows (in the order in which they were added)
	ids []interface{}

	// matrix rows
	rows []row
}

type row struct {
	// A bit vector of 2*n bits.  The first n bits are a combination
	// of original rows.  The second n bits mark which original rows
	// were combined to make this one.
	bits bitVec

	// The column that we pivot with. == bits.firstBit()
	pivot uint
}

// Return a new matrix which can handle indexes 0 <= i < n.
func NewBitMatrix(n uint) *BitMatrix {
	return &BitMatrix{n, make([]interface{}, 0, n), make([]row, 0, n)}
}

func (m *BitMatrix) Rows() uint {
	return uint(len(m.rows))
}

// Adds the vector with the given set indexes (indexes may appear multiple
// times - an index is set if it appears an odd number of times).
// If there is a linear combination of the added rows that xor to the zero
// vector, addRow returns the identities of those vectors.  Otherwise returns nil.
func (m *BitMatrix) AddRow(idxs []uint, id interface{}) []interface{} {
	m.ids = append(m.ids, id)
	bits := newBitVec(2*m.n)
	for _, i := range idxs {
		bits.toggleBit(i)
	}
	if bits.empty() {
		// we've been passed the all-zero vector
		return []interface{}{id}
	}
	for _, r := range m.rows {
		if bits.getBit(r.pivot) {
			bits.xor(r.bits)
		}
	}
	p := bits.firstBit()
	if p < m.n {
		bits.setBit(m.n+uint(len(m.rows)))
		m.rows = append(m.rows, row{bits, p})
		return nil
	}

	// found a linear combination of vectors that generates the 0 vector.
	a := []interface{}{id}
	for i := uint(0); i < m.n; i++ {
		if bits.getBit(m.n + i) {
			a = append(a, m.ids[i])
		}
	}
	// Note: we don't add this vector to the list of rows, as it
	// is linearly dependent.
	return a
}
<commit_msg>Fix bug in when we add id to the id list.<commit_after>package matrix

// Implementation of a matrix over GF(2).  Used to find
// linear combinations of rows which are zero.
type BitMatrix struct {
	// size of matrix
	n uint

	// ids of rows (in the order in which they were added)
	ids []interface{}

	// matrix rows
	rows []row
}

type row struct {
	// A bit vector of 2*n bits.  The first n bits are a combination
	// of original rows.  The second n bits mark which original rows
	// were combined to make this one.
	bits bitVec

	// The column that we pivot with. == bits.firstBit()
	pivot uint
}

// Return a new matrix which can handle indexes 0 <= i < n.
func NewBitMatrix(n uint) *BitMatrix {
	return &BitMatrix{n, make([]interface{}, 0, n), make([]row, 0, n)}
}

func (m *BitMatrix) Rows() uint {
	return uint(len(m.rows))
}

// Adds the vector with the given set indexes (indexes may appear multiple
// times - an index is set if it appears an odd number of times).
// If there is a linear combination of the added rows that xor to the zero
// vector, addRow returns the identities of those vectors.  Otherwise returns nil.
func (m *BitMatrix) AddRow(idxs []uint, id interface{}) []interface{} {
	bits := newBitVec(2*m.n)
	for _, i := range idxs {
		bits.toggleBit(i)
	}
	if bits.empty() {
		// we've been passed the all-zero vector
		return []interface{}{id}
	}
	for _, r := range m.rows {
		if bits.getBit(r.pivot) {
			bits.xor(r.bits)
		}
	}
	p := bits.firstBit()
	if p < m.n {
		bits.setBit(m.n+uint(len(m.ids)))
		m.ids = append(m.ids, id)
		m.rows = append(m.rows, row{bits, p})
		return nil
	}

	// found a linear combination of vectors that generates the 0 vector.
	a := []interface{}{id}
	for i := uint(0); i < m.n; i++ {
		if bits.getBit(m.n + i) {
			a = append(a, m.ids[i])
		}
	}
	// Note: we don't add this vector to the list of rows, as it
	// is linearly dependent.
	return a
}
<|endoftext|>