<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"

	"encoding/base64"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/secretsmanager"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSecretsManagerSecretVersion() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSecretsManagerSecretVersionCreate,
		Read:   resourceAwsSecretsManagerSecretVersionRead,
		Update: resourceAwsSecretsManagerSecretVersionUpdate,
		Delete: resourceAwsSecretsManagerSecretVersionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"secret_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"secret_string": {
				Type:          schema.TypeString,
				Optional:      true,
				ForceNew:      true,
				Sensitive:     true,
				ConflictsWith: []string{"secret_binary"},
			},
			"secret_binary": {
				Type:          schema.TypeString,
				Optional:      true,
				ForceNew:      true,
				Sensitive:     true,
				ConflictsWith: []string{"secret_string"},
			},
			"version_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"version_stages": {
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourceAwsSecretsManagerSecretVersionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn
	secretID := d.Get("secret_id").(string)

	input := &secretsmanager.PutSecretValueInput{
		SecretId: aws.String(secretID),
	}

	if v, ok := d.GetOk("secret_string"); ok {
		input.SecretString = aws.String(v.(string))
	}

	if v, ok := d.GetOk("secret_binary"); ok {
		vs := []byte(v.(string))

		if !isBase64Encoded(vs) {
			fmt.Errorf("expected base64 in secret_binary")
		}

		var err error
		input.SecretBinary, err = base64.StdEncoding.DecodeString(v.(string))

		if err != nil {
			return fmt.Errorf("error decoding secret binary value: %s", err)
		}
	}

	if v, ok := d.GetOk("version_stages"); ok {
		input.VersionStages = expandStringList(v.(*schema.Set).List())
	}

	log.Printf("[DEBUG] Putting Secrets Manager Secret %q value", secretID)
	output, err := conn.PutSecretValue(input)
	if err != nil {
		return fmt.Errorf("error putting Secrets Manager Secret value: %s", err)
	}

	d.SetId(fmt.Sprintf("%s|%s", secretID, aws.StringValue(output.VersionId)))

	return resourceAwsSecretsManagerSecretVersionRead(d, meta)
}

func resourceAwsSecretsManagerSecretVersionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn

	secretID, versionID, err := decodeSecretsManagerSecretVersionID(d.Id())
	if err != nil {
		return err
	}

	input := &secretsmanager.GetSecretValueInput{
		SecretId:  aws.String(secretID),
		VersionId: aws.String(versionID),
	}

	log.Printf("[DEBUG] Reading Secrets Manager Secret Version: %s", input)
	output, err := conn.GetSecretValue(input)
	if err != nil {
		if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
			log.Printf("[WARN] Secrets Manager Secret Version %q not found - removing from state", d.Id())
			d.SetId("")
			return nil
		}
		if isAWSErr(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
			log.Printf("[WARN] Secrets Manager Secret Version %q not found - removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("error reading Secrets Manager Secret Version: %s", err)
	}

	d.Set("secret_id", secretID)
	d.Set("secret_string", output.SecretString)
	d.Set("secret_binary", base64Encode(output.SecretBinary))
	d.Set("version_id", output.VersionId)
	d.Set("arn", output.ARN)

	if err := d.Set("version_stages", flattenStringList(output.VersionStages)); err != nil {
		return fmt.Errorf("error setting version_stages: %s", err)
	}

	return nil
}

func resourceAwsSecretsManagerSecretVersionUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn

	secretID, versionID, err := decodeSecretsManagerSecretVersionID(d.Id())
	if err != nil {
		return err
	}

	o, n := d.GetChange("version_stages")
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	stagesToAdd := ns.Difference(os).List()
	stagesToRemove := os.Difference(ns).List()

	for _, stage := range stagesToAdd {
		input := &secretsmanager.UpdateSecretVersionStageInput{
			MoveToVersionId: aws.String(versionID),
			SecretId:        aws.String(secretID),
			VersionStage:    aws.String(stage.(string)),
		}

		log.Printf("[DEBUG] Updating Secrets Manager Secret Version Stage: %s", input)
		_, err := conn.UpdateSecretVersionStage(input)
		if err != nil {
			return fmt.Errorf("error updating Secrets Manager Secret %q Version Stage %q: %s", secretID, stage.(string), err)
		}
	}

	for _, stage := range stagesToRemove {
		// InvalidParameterException: You can only move staging label AWSCURRENT to a different secret version. It can’t be completely removed.
		if stage.(string) == "AWSCURRENT" {
			log.Printf("[INFO] Skipping removal of AWSCURRENT staging label for secret %q version %q", secretID, versionID)
			continue
		}
		input := &secretsmanager.UpdateSecretVersionStageInput{
			RemoveFromVersionId: aws.String(versionID),
			SecretId:            aws.String(secretID),
			VersionStage:        aws.String(stage.(string)),
		}
		log.Printf("[DEBUG] Updating Secrets Manager Secret Version Stage: %s", input)
		_, err := conn.UpdateSecretVersionStage(input)
		if err != nil {
			return fmt.Errorf("error updating Secrets Manager Secret %q Version Stage %q: %s", secretID, stage.(string), err)
		}
	}

	return resourceAwsSecretsManagerSecretVersionRead(d, meta)
}

func resourceAwsSecretsManagerSecretVersionDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn

	secretID, versionID, err := decodeSecretsManagerSecretVersionID(d.Id())
	if err != nil {
		return err
	}

	if v, ok := d.GetOk("version_stages"); ok {
		for _, stage := range v.(*schema.Set).List() {
			// InvalidParameterException: You can only move staging label AWSCURRENT to a different secret version. It can’t be completely removed.
			if stage.(string) == "AWSCURRENT" {
				log.Printf("[WARN] Cannot remove AWSCURRENT staging label, which may leave the secret %q version %q active", secretID, versionID)
				continue
			}
			input := &secretsmanager.UpdateSecretVersionStageInput{
				RemoveFromVersionId: aws.String(versionID),
				SecretId:            aws.String(secretID),
				VersionStage:        aws.String(stage.(string)),
			}
			log.Printf("[DEBUG] Updating Secrets Manager Secret Version Stage: %s", input)
			_, err := conn.UpdateSecretVersionStage(input)
			if err != nil {
				if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
					return nil
				}
				if isAWSErr(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
					return nil
				}
				return fmt.Errorf("error updating Secrets Manager Secret %q Version Stage %q: %s", secretID, stage.(string), err)
			}
		}
	}

	return nil
}

func decodeSecretsManagerSecretVersionID(id string) (string, string, error) {
	idParts := strings.Split(id, "|")
	if len(idParts) != 2 {
		return "", "", fmt.Errorf("expected ID in format SecretID|VersionID, received: %s", id)
	}
	return idParts[0], idParts[1], nil
}
<commit_msg>fixing make lint<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"

	"encoding/base64"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/secretsmanager"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSecretsManagerSecretVersion() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSecretsManagerSecretVersionCreate,
		Read:   resourceAwsSecretsManagerSecretVersionRead,
		Update: resourceAwsSecretsManagerSecretVersionUpdate,
		Delete: resourceAwsSecretsManagerSecretVersionDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"secret_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"secret_string": {
				Type:          schema.TypeString,
				Optional:      true,
				ForceNew:      true,
				Sensitive:     true,
				ConflictsWith: []string{"secret_binary"},
			},
			"secret_binary": {
				Type:          schema.TypeString,
				Optional:      true,
				ForceNew:      true,
				Sensitive:     true,
				ConflictsWith: []string{"secret_string"},
			},
			"version_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"version_stages": {
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourceAwsSecretsManagerSecretVersionCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn
	secretID := d.Get("secret_id").(string)

	input := &secretsmanager.PutSecretValueInput{
		SecretId: aws.String(secretID),
	}

	if v, ok := d.GetOk("secret_string"); ok {
		input.SecretString = aws.String(v.(string))
	}

	if v, ok := d.GetOk("secret_binary"); ok {
		vs := []byte(v.(string))

		if !isBase64Encoded(vs) {
			return fmt.Errorf("expected base64 in secret_binary")
		}

		var err error
		input.SecretBinary, err = base64.StdEncoding.DecodeString(v.(string))

		if err != nil {
			return fmt.Errorf("error decoding secret binary value: %s", err)
		}
	}

	if v, ok := d.GetOk("version_stages"); ok {
		input.VersionStages = expandStringList(v.(*schema.Set).List())
	}

	log.Printf("[DEBUG] Putting Secrets Manager Secret %q value", secretID)
	output, err := conn.PutSecretValue(input)
	if err != nil {
		return fmt.Errorf("error putting Secrets Manager Secret value: %s", err)
	}

	d.SetId(fmt.Sprintf("%s|%s", secretID, aws.StringValue(output.VersionId)))

	return resourceAwsSecretsManagerSecretVersionRead(d, meta)
}

func resourceAwsSecretsManagerSecretVersionRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn

	secretID, versionID, err := decodeSecretsManagerSecretVersionID(d.Id())
	if err != nil {
		return err
	}

	input := &secretsmanager.GetSecretValueInput{
		SecretId:  aws.String(secretID),
		VersionId: aws.String(versionID),
	}

	log.Printf("[DEBUG] Reading Secrets Manager Secret Version: %s", input)
	output, err := conn.GetSecretValue(input)
	if err != nil {
		if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
			log.Printf("[WARN] Secrets Manager Secret Version %q not found - removing from state", d.Id())
			d.SetId("")
			return nil
		}
		if isAWSErr(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
			log.Printf("[WARN] Secrets Manager Secret Version %q not found - removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("error reading Secrets Manager Secret Version: %s", err)
	}

	d.Set("secret_id", secretID)
	d.Set("secret_string", output.SecretString)
	d.Set("secret_binary", base64Encode(output.SecretBinary))
	d.Set("version_id", output.VersionId)
	d.Set("arn", output.ARN)

	if err := d.Set("version_stages", flattenStringList(output.VersionStages)); err != nil {
		return fmt.Errorf("error setting version_stages: %s", err)
	}

	return nil
}

func resourceAwsSecretsManagerSecretVersionUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn

	secretID, versionID, err := decodeSecretsManagerSecretVersionID(d.Id())
	if err != nil {
		return err
	}

	o, n := d.GetChange("version_stages")
	os := o.(*schema.Set)
	ns := n.(*schema.Set)
	stagesToAdd := ns.Difference(os).List()
	stagesToRemove := os.Difference(ns).List()

	for _, stage := range stagesToAdd {
		input := &secretsmanager.UpdateSecretVersionStageInput{
			MoveToVersionId: aws.String(versionID),
			SecretId:        aws.String(secretID),
			VersionStage:    aws.String(stage.(string)),
		}

		log.Printf("[DEBUG] Updating Secrets Manager Secret Version Stage: %s", input)
		_, err := conn.UpdateSecretVersionStage(input)
		if err != nil {
			return fmt.Errorf("error updating Secrets Manager Secret %q Version Stage %q: %s", secretID, stage.(string), err)
		}
	}

	for _, stage := range stagesToRemove {
		// InvalidParameterException: You can only move staging label AWSCURRENT to a different secret version. It can’t be completely removed.
		if stage.(string) == "AWSCURRENT" {
			log.Printf("[INFO] Skipping removal of AWSCURRENT staging label for secret %q version %q", secretID, versionID)
			continue
		}
		input := &secretsmanager.UpdateSecretVersionStageInput{
			RemoveFromVersionId: aws.String(versionID),
			SecretId:            aws.String(secretID),
			VersionStage:        aws.String(stage.(string)),
		}
		log.Printf("[DEBUG] Updating Secrets Manager Secret Version Stage: %s", input)
		_, err := conn.UpdateSecretVersionStage(input)
		if err != nil {
			return fmt.Errorf("error updating Secrets Manager Secret %q Version Stage %q: %s", secretID, stage.(string), err)
		}
	}

	return resourceAwsSecretsManagerSecretVersionRead(d, meta)
}

func resourceAwsSecretsManagerSecretVersionDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).secretsmanagerconn

	secretID, versionID, err := decodeSecretsManagerSecretVersionID(d.Id())
	if err != nil {
		return err
	}

	if v, ok := d.GetOk("version_stages"); ok {
		for _, stage := range v.(*schema.Set).List() {
			// InvalidParameterException: You can only move staging label AWSCURRENT to a different secret version. It can’t be completely removed.
			if stage.(string) == "AWSCURRENT" {
				log.Printf("[WARN] Cannot remove AWSCURRENT staging label, which may leave the secret %q version %q active", secretID, versionID)
				continue
			}
			input := &secretsmanager.UpdateSecretVersionStageInput{
				RemoveFromVersionId: aws.String(versionID),
				SecretId:            aws.String(secretID),
				VersionStage:        aws.String(stage.(string)),
			}
			log.Printf("[DEBUG] Updating Secrets Manager Secret Version Stage: %s", input)
			_, err := conn.UpdateSecretVersionStage(input)
			if err != nil {
				if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
					return nil
				}
				if isAWSErr(err, secretsmanager.ErrCodeInvalidRequestException, "You can’t perform this operation on the secret because it was deleted") {
					return nil
				}
				return fmt.Errorf("error updating Secrets Manager Secret %q Version Stage %q: %s", secretID, stage.(string), err)
			}
		}
	}

	return nil
}

func decodeSecretsManagerSecretVersionID(id string) (string, string, error) {
	idParts := strings.Split(id, "|")
	if len(idParts) != 2 {
		return "", "", fmt.Errorf("expected ID in format SecretID|VersionID, received: %s", id)
	}
	return idParts[0], idParts[1], nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"sync"

	"golang.org/x/net/dns/dnsmessage"
)

var txts = []string{`Set this TXT record: curl -X POST http://localhost/update -d  '{"txt":"Certificate Authority validation token"}'`}

// Txt is for parsing the JSON POST to set the DNS TXT record
type Txt struct {
	Txt string `json:"txt"`
}

func main() {
	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})
	if err != nil {
		log.Fatal(err.Error())
	}

	var group sync.WaitGroup
	group.Add(1)
	go dnsServer(conn, &group)
	group.Add(1)
	go httpServer(&group)
	group.Wait()
}

func dnsServer(conn *net.UDPConn, group *sync.WaitGroup) {
	var query dnsmessage.Message

	defer group.Done()
	log.Println("I'm firing up the DNS server.")
	queryRaw := make([]byte, 512)
	for {
		_, addr, err := conn.ReadFromUDP(queryRaw)
		if err != nil {
			log.Println(err.Error())
			continue
		}
		err = query.Unpack(queryRaw)
		if err != nil {
			log.Println(err.Error())
			continue
		}
		// Technically, there can be multiple questions in a DNS message; practically, there's only one
		if len(query.Questions) != 1 {
			log.Printf("I expected one question but got %d.\n", len(query.Questions))
			continue
		}
		// We only return answers to TXT queries, nothing else
		if query.Questions[0].Type != dnsmessage.TypeTXT {
			log.Println("I expected a question for a TypeTXT record but got a question for a " + query.Questions[0].Type.String() + " record.")
			continue
		}
		var txtAnswers = []dnsmessage.Resource{}
		for _, txt := range txts {
			txtAnswers = append(txtAnswers, dnsmessage.Resource{
				Header: dnsmessage.ResourceHeader{
					Name:  query.Questions[0].Name,
					Type:  dnsmessage.TypeTXT,
					Class: dnsmessage.ClassINET,
					TTL:   60,
				},
				Body: &dnsmessage.TXTResource{TXT: []string{txt}},
			})
		}
		reply := dnsmessage.Message{
			Header: dnsmessage.Header{
				ID:               query.ID,
				Response:         true,
				Authoritative:    true,
				RecursionDesired: query.RecursionDesired,
			},
			Questions: query.Questions,
			Answers:   txtAnswers,
		}
		replyRaw, err := reply.Pack()
		if err != nil {
			log.Println(err.Error())
			continue
		}
		_, err = conn.WriteToUDP(replyRaw, addr)
		if err != nil {
			log.Println(err.Error())
			continue
		}
		log.Printf("%v.%d %s → \"%v\"\n", addr.IP, addr.Port, query.Questions[0].Type.String(), txts)
	}
}

func httpServer(group *sync.WaitGroup) {
	defer group.Done()
	log.Println("I'm firing up the HTTP server.")
	http.HandleFunc("/", usageHandler)
	http.HandleFunc("/update", updateTxtHandler)
	log.Fatal(http.ListenAndServe(":80", nil))
}

func usageHandler(w http.ResponseWriter, r *http.Request) {
	_, err := fmt.Fprintln(w, `Set the TXT record: curl -X POST http://localhost/update -d  '{"txt":"Certificate Authority's validation token"}'`)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err.Error())
	}
}

func updateTxtHandler(w http.ResponseWriter, r *http.Request) {
	var err error
	if r.Method != http.MethodPost {
		err = errors.New("/update requires POST method, not " + r.Method + " method")
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Println(err.Error())
		return
	}
	var body []byte
	if body, err = ioutil.ReadAll(r.Body); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}
	var updateTxt Txt
	if err := json.Unmarshal(body, &updateTxt); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}
	if body, err = json.Marshal(updateTxt); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}
	if _, err = fmt.Fprintf(w, string(body)); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}
	log.Println("Creating new TXT record \"" + updateTxt.Txt + "\".")
	// this is the money shot, where we create a new DNS TXT record to what was in the POST request
	txts = append(txts, updateTxt.Txt)
}
<commit_msg>Log better messages for DNS/HTTP server<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"sync"

	"golang.org/x/net/dns/dnsmessage"
)

var txts = []string{`Set this TXT record: curl -X POST http://localhost/update -d  '{"txt":"Certificate Authority validation token"}'`}

// Txt is for parsing the JSON POST to set the DNS TXT record
type Txt struct {
	Txt string `json:"txt"`
}

func main() {
	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 53})
	if err != nil {
		log.Fatal(err.Error())
	}

	var group sync.WaitGroup
	group.Add(1)
	go dnsServer(conn, &group)
	group.Add(1)
	go httpServer(&group)
	group.Wait()
}

func dnsServer(conn *net.UDPConn, group *sync.WaitGroup) {
	var query dnsmessage.Message

	defer group.Done()
	log.Println("DNS: starting up.")
	queryRaw := make([]byte, 512)
	for {
		_, addr, err := conn.ReadFromUDP(queryRaw)
		if err != nil {
			log.Println("DNS: " + err.Error())
			continue
		}
		err = query.Unpack(queryRaw)
		if err != nil {
			log.Println("DNS: " + err.Error())
			continue
		}
		// Technically, there can be multiple questions in a DNS message; practically, there's only one
		if len(query.Questions) != 1 {
			log.Printf("DNS: I expected one question but got %d.\n", len(query.Questions))
			continue
		}
		// We only return answers to TXT queries, nothing else
		if query.Questions[0].Type != dnsmessage.TypeTXT {
			log.Println("DNS: I expected a question for a TypeTXT record but got a question for a " + query.Questions[0].Type.String() + " record.")
			continue
		}
		var txtAnswers = []dnsmessage.Resource{}
		for _, txt := range txts {
			txtAnswers = append(txtAnswers, dnsmessage.Resource{
				Header: dnsmessage.ResourceHeader{
					Name:  query.Questions[0].Name,
					Type:  dnsmessage.TypeTXT,
					Class: dnsmessage.ClassINET,
					TTL:   60,
				},
				Body: &dnsmessage.TXTResource{TXT: []string{txt}},
			})
		}
		reply := dnsmessage.Message{
			Header: dnsmessage.Header{
				ID:               query.ID,
				Response:         true,
				Authoritative:    true,
				RecursionDesired: query.RecursionDesired,
			},
			Questions: query.Questions,
			Answers:   txtAnswers,
		}
		replyRaw, err := reply.Pack()
		if err != nil {
			log.Println("DNS: " + err.Error())
			continue
		}
		_, err = conn.WriteToUDP(replyRaw, addr)
		if err != nil {
			log.Println("DNS: " + err.Error())
			continue
		}
		log.Printf("DNS: %v.%d %s → \"%v\"\n", addr.IP, addr.Port, query.Questions[0].Type.String(), txts)
	}
}

func httpServer(group *sync.WaitGroup) {
	defer group.Done()
	log.Println("HTTP: starting up.")
	http.HandleFunc("/", usageHandler)
	http.HandleFunc("/update", updateTxtHandler)
	log.Fatal("HTTP: " + http.ListenAndServe(":80", nil).Error())
}

func usageHandler(w http.ResponseWriter, r *http.Request) {
	_, err := fmt.Fprintln(w, `Set the TXT record: curl -X POST http://localhost/update -d  '{"txt":"Certificate Authority's validation token"}'`)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println("HTTP: " + err.Error())
	}
}

func updateTxtHandler(w http.ResponseWriter, r *http.Request) {
	var err error
	if r.Method != http.MethodPost {
		err = errors.New("/update requires POST method, not " + r.Method + " method")
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Println("HTTP: " + err.Error())
		return
	}
	var body []byte
	if body, err = ioutil.ReadAll(r.Body); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println("HTTP: " + err.Error())
		return
	}
	var updateTxt Txt
	if err := json.Unmarshal(body, &updateTxt); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println("HTTP: " + err.Error())
		return
	}
	if body, err = json.Marshal(updateTxt); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println("HTTP: " + err.Error())
		return
	}
	if _, err = fmt.Fprintf(w, string(body)); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println("HTTP: " + err.Error())
		return
	}
	log.Println("Creating new TXT record \"" + updateTxt.Txt + "\".")
	// this is the money shot, where we create a new DNS TXT record to what was in the POST request
	txts = append(txts, updateTxt.Txt)
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	ct "github.com/daviddengcn/go-colortext"
	"github.com/gosuri/uilive"
	"github.com/op/go-logging"
	"time"
)

type coloredLogger struct {
	writer      *uilive.Writer
	currentText string
}

func newColoredConsoleWriter() *coloredLogger {
	return &coloredLogger{writer: uilive.New(), currentText: ""}
}

func (cLogger *coloredLogger) writeSysoutBuffer(text string) {
	cLogger.currentText += text
}

func (cLogger *coloredLogger) SpecStart(heading string) {
	msg := formatSpec(heading)
	Log.Info(msg)
	ct.Foreground(ct.None, true)
	fmt.Println(msg)
	fmt.Println()
	ct.ResetColor()
}

func (coloredLogger *coloredLogger) SpecEnd() {
	fmt.Println()
}

func (cLogger *coloredLogger) ScenarioStart(scenarioHeading string) {
	msg := formatScenario(scenarioHeading)
	Log.Info(msg)

	indentedText := indent(msg, scenarioIndentation)
	if level == logging.INFO {
		cLogger.writer.Start()
		ct.Foreground(ct.Yellow, false)

		fmt.Fprintln(cLogger.writer, indentedText)
		cLogger.currentText = indentedText
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
	} else {
		cLogger.writer.Start()
		ct.Foreground(ct.Cyan, true)
		ConsoleWrite(indentedText)
		ct.ResetColor()
	}
}

func (cLogger *coloredLogger) ScenarioEnd(failed bool) {
	if level == logging.INFO {
		if failed {
			ct.Foreground(ct.Red, true)
		} else {
			ct.Foreground(ct.Green, true)
		}

		fmt.Fprintln(cLogger.writer, cLogger.currentText)
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
	} else {
		cLogger.writer.Stop()
		cLogger.currentText = ""
	}
	if level == logging.INFO {
		cLogger.writer.Flush()
		cLogger.writer.Stop()
	}
	cLogger.writer = uilive.New()
	cLogger.currentText = ""
}

func (cLogger *coloredLogger) StepStart(stepText string) {
	Log.Debug(stepText)
	if level == logging.DEBUG {
		ct.Foreground(ct.Yellow, true)

		cLogger.currentText = indent(stepText, stepIndentation)
		fmt.Fprintln(cLogger.writer, cLogger.currentText)
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
	}
}

func (cLogger *coloredLogger) StepEnd(failed bool) {
	if level == logging.DEBUG {
		if failed {
			ct.Foreground(ct.Red, true)
		} else {
			ct.Foreground(ct.Green, true)
		}

		fmt.Fprintln(cLogger.writer, cLogger.currentText)
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
		cLogger.writer.Flush()
		fmt.Println()
		cLogger.currentText = ""
	}
}
<commit_msg>minor refactoring in colored logger<commit_after>package logger

import (
	"fmt"
	ct "github.com/daviddengcn/go-colortext"
	"github.com/gosuri/uilive"
	"github.com/op/go-logging"
	"time"
)

type coloredLogger struct {
	writer      *uilive.Writer
	currentText string
}

func newColoredConsoleWriter() *coloredLogger {
	return &coloredLogger{writer: uilive.New(), currentText: ""}
}

func (cLogger *coloredLogger) writeSysoutBuffer(text string) {
	cLogger.currentText += text
}

func (cLogger *coloredLogger) SpecStart(heading string) {
	msg := formatSpec(heading)
	Log.Info(msg)
	ct.Foreground(ct.None, true)
	fmt.Println(msg)
	fmt.Println()
	ct.ResetColor()
}

func (coloredLogger *coloredLogger) SpecEnd() {
	fmt.Println()
}

func (cLogger *coloredLogger) ScenarioStart(scenarioHeading string) {
	msg := formatScenario(scenarioHeading)
	Log.Info(msg)
	cLogger.writer.Start()

	indentedText := indent(msg, scenarioIndentation)
	if level == logging.INFO {
		ct.Foreground(ct.Yellow, false)

		fmt.Fprintln(cLogger.writer, indentedText)
		cLogger.currentText = indentedText
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
	} else {
		ct.Foreground(ct.Cyan, true)
		ConsoleWrite(indentedText)
		ct.ResetColor()
	}
}

func (cLogger *coloredLogger) ScenarioEnd(failed bool) {
	if level == logging.INFO {
		if failed {
			ct.Foreground(ct.Red, true)
		} else {
			ct.Foreground(ct.Green, true)
		}

		fmt.Fprintln(cLogger.writer, cLogger.currentText)
		time.Sleep(time.Millisecond * 10)
		ct.ResetColor()
		cLogger.writer.Flush()
	}
	cLogger.writer.Stop()
	cLogger.writer = uilive.New()
	cLogger.currentText = ""
}

func (cLogger *coloredLogger) StepStart(stepText string) {
	Log.Debug(stepText)
	if level == logging.DEBUG {
		ct.Foreground(ct.Yellow, true)

		cLogger.currentText = indent(stepText, stepIndentation)
		fmt.Fprintln(cLogger.writer, cLogger.currentText)
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
	}
}

func (cLogger *coloredLogger) StepEnd(failed bool) {
	if level == logging.DEBUG {
		if failed {
			ct.Foreground(ct.Red, true)
		} else {
			ct.Foreground(ct.Green, true)
		}

		fmt.Fprintln(cLogger.writer, cLogger.currentText)
		time.Sleep(time.Millisecond * 10)

		ct.ResetColor()
		cLogger.writer.Flush()
		fmt.Println()
		cLogger.currentText = ""
	}
}
<|endoftext|>


<commit_before>package orm

import (
	"bytes"
	"errors"
	"reflect"
)

func Insert(db DB, v ...interface{}) error {
	_, err := NewQuery(db, v...).Insert()
	return err
}

type insertQuery struct {
	*Query
	returningFields []*Field
}

var _ QueryAppender = (*insertQuery)(nil)

func (q insertQuery) AppendQuery(b []byte, params ...interface{}) ([]byte, error) {
	if q.model == nil {
		return nil, errors.New("pg: Model(nil)")
	}

	table := q.model.Table()
	value := q.model.Value()

	b = append(b, "INSERT INTO "...)
	if q.onConflict != nil {
		b = q.appendTableNameWithAlias(b)
	} else {
		b = q.appendTableName(b)
	}
	b = append(b, " ("...)

	start := len(b)
	for _, f := range table.Fields {
		b = append(b, f.ColName...)
		b = append(b, ", "...)
	}
	if len(b) > start {
		b = b[:len(b)-2]
	}

	b = append(b, ") VALUES ("...)
	if value.Kind() == reflect.Struct {
		b = q.appendValues(b, table.Fields, value)
	} else {
		for i := 0; i < value.Len(); i++ {
			el := value.Index(i)
			if el.Kind() == reflect.Interface {
				el = el.Elem()
			}
			b = q.appendValues(b, table.Fields, reflect.Indirect(el))
			if i != value.Len()-1 {
				b = append(b, "), ("...)
			}
		}
	}
	b = append(b, ')')

	if q.onConflict != nil {
		b = append(b, " ON CONFLICT "...)
		b = q.onConflict.AppendFormat(b, q)

		if onConflictDoUpdate(b) {
			if len(q.set) > 0 {
				b = q.appendSet(b)
			}

			if len(q.where) > 0 {
				b = q.appendWhere(b)
			}
		}
	}

	if len(q.returning) > 0 {
		b = q.appendReturning(b)
	} else if len(q.returningFields) > 0 {
		b = q.appendReturningFields(b, q.returningFields)
	}

	return b, nil
}

func onConflictDoUpdate(b []byte) bool {
	return bytes.LastIndex(b, []byte(" DO UPDATE")) >= 0
}

func (q *insertQuery) appendValues(b []byte, fields []*Field, v reflect.Value) []byte {
	for i, f := range fields {
		if i > 0 {
			b = append(b, ", "...)
		}
		if q.omitEmpty(f, v) {
			b = append(b, "DEFAULT"...)
			q.addReturningField(f)
		} else {
			b = f.AppendValue(b, v, 1)
		}
	}
	return b
}

func (insertQuery) omitEmpty(f *Field, v reflect.Value) bool {
	omit := f.Has(PrimaryKeyFlag)
	if !omit && v.Kind() == reflect.Struct {
		omit = f.OmitEmpty(v)
	}
	if !omit {
		return false
	}
	return f.IsEmpty(v)
}

func (ins *insertQuery) addReturningField(field *Field) {
	for _, f := range ins.returningFields {
		if f == field {
			return
		}
	}
	ins.returningFields = append(ins.returningFields, field)
}

func (insertQuery) appendReturningFields(b []byte, fields []*Field) []byte {
	b = append(b, " RETURNING "...)
	for i, f := range fields {
		if i > 0 {
			b = append(b, ", "...)
		}
		b = append(b, f.ColName...)
	}
	return b
}
<commit_msg>orm: stricter check.<commit_after>package orm

import (
	"bytes"
	"errors"
	"reflect"
)

func Insert(db DB, v ...interface{}) error {
	_, err := NewQuery(db, v...).Insert()
	return err
}

type insertQuery struct {
	*Query
	returningFields []*Field
}

var _ QueryAppender = (*insertQuery)(nil)

func (q insertQuery) AppendQuery(b []byte, params ...interface{}) ([]byte, error) {
	if q.model == nil {
		return nil, errors.New("pg: Model(nil)")
	}

	table := q.model.Table()
	value := q.model.Value()

	b = append(b, "INSERT INTO "...)
	if q.onConflict != nil {
		b = q.appendTableNameWithAlias(b)
	} else {
		b = q.appendTableName(b)
	}
	b = append(b, " ("...)

	start := len(b)
	for _, f := range table.Fields {
		b = append(b, f.ColName...)
		b = append(b, ", "...)
	}
	if len(b) > start {
		b = b[:len(b)-2]
	}

	b = append(b, ") VALUES ("...)
	if value.Kind() == reflect.Struct {
		b = q.appendValues(b, table.Fields, value)
	} else {
		for i := 0; i < value.Len(); i++ {
			el := value.Index(i)
			if el.Kind() == reflect.Interface {
				el = el.Elem()
			}
			b = q.appendValues(b, table.Fields, reflect.Indirect(el))
			if i != value.Len()-1 {
				b = append(b, "), ("...)
			}
		}
	}
	b = append(b, ')')

	if q.onConflict != nil {
		b = append(b, " ON CONFLICT "...)
		b = q.onConflict.AppendFormat(b, q)

		if onConflictDoUpdate(b) {
			if len(q.set) > 0 {
				b = q.appendSet(b)
			}

			if len(q.where) > 0 {
				b = q.appendWhere(b)
			}
		}
	}

	if len(q.returning) > 0 {
		b = q.appendReturning(b)
	} else if len(q.returningFields) > 0 {
		b = q.appendReturningFields(b, q.returningFields)
	}

	return b, nil
}

func onConflictDoUpdate(b []byte) bool {
	return bytes.HasSuffix(b, []byte(" DO UPDATE"))
}

func (q *insertQuery) appendValues(b []byte, fields []*Field, v reflect.Value) []byte {
	for i, f := range fields {
		if i > 0 {
			b = append(b, ", "...)
		}
		if q.omitEmpty(f, v) {
			b = append(b, "DEFAULT"...)
			q.addReturningField(f)
		} else {
			b = f.AppendValue(b, v, 1)
		}
	}
	return b
}

func (insertQuery) omitEmpty(f *Field, v reflect.Value) bool {
	omit := f.Has(PrimaryKeyFlag)
	if !omit && v.Kind() == reflect.Struct {
		omit = f.OmitEmpty(v)
	}
	if !omit {
		return false
	}
	return f.IsEmpty(v)
}

func (ins *insertQuery) addReturningField(field *Field) {
	for _, f := range ins.returningFields {
		if f == field {
			return
		}
	}
	ins.returningFields = append(ins.returningFields, field)
}

func (insertQuery) appendReturningFields(b []byte, fields []*Field) []byte {
	b = append(b, " RETURNING "...)
	for i, f := range fields {
		if i > 0 {
			b = append(b, ", "...)
		}
		b = append(b, f.ColName...)
	}
	return b
}
<|endoftext|>


<commit_before>package termite

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var _ = fmt.Println

type FsServer struct {
	contentServer  *ContentServer
	contentCache   *ContentCache
	Root           string
	excluded       map[string]bool
	excludePrivate bool
	attr           *AttributeCache
}

func NewFsServer(root string, cache *ContentCache, excluded []string) *FsServer {
	me := &FsServer{
		contentCache:   cache,
		contentServer:  &ContentServer{Cache: cache},
		Root:           root,
		excludePrivate: true,
	}
	me.attr = NewAttributeCache(func(n string) *FileAttr {
		return me.uncachedGetAttr(n)
	},
		func(n string) *os.FileInfo {
			fi, _ := os.Lstat(me.path(n))
			return fi
		})
	me.excluded = make(map[string]bool)
	for _, e := range excluded {
		if e[0] == '/' {
			panic("leading slash")
		}
		me.excluded[e] = true
	}
	return me
}

func (me *FsServer) path(n string) string {
	if me.Root == "" {
		return n
	}
	return filepath.Join(me.Root, strings.TrimLeft(n, "/"))
}

func (me *FsServer) FileContent(req *ContentRequest, rep *ContentResponse) os.Error {
	return me.contentServer.FileContent(req, rep)
}

func (me *FsServer) GetAttr(req *AttrRequest, rep *AttrResponse) os.Error {
	log.Printf("GetAttr req %q", req.Name)
	if req.Name != "" && req.Name[0] == '/' {
		panic("leading /")
	}

	a := me.attr.GetDir(req.Name)
	if a.Hash != "" {
		log.Printf("GetAttr %v %x", a, a.Hash)
	}
	rep.Attrs = append(rep.Attrs, a)
	return nil
}

func (me *FsServer) uncachedGetAttr(name string) (rep *FileAttr) {
	rep = &FileAttr{Path: name}
	p := me.path(name)
	fi, _ := os.Lstat(p)
	// We don't want to expose the master's private files to the
	// world.
	if me.excludePrivate && fi != nil && fi.Mode&0077 == 0 {
		log.Printf("Denied access to private file %q", name)
		return rep
	}

	if me.excluded[name] {
		log.Printf("Denied access to excluded file %q", name)
		return rep
	}
	rep.FileInfo = fi
	if fi != nil {
		me.fillContent(rep)
	}
	return rep
}

func (me *FsServer) fillContent(rep *FileAttr) {
	if rep.IsSymlink() || rep.IsDirectory() {
		rep.ReadFromFs(me.path(rep.Path))
	}
	if rep.IsRegular() {
		// TODO - /usr should be configurable.
		fullPath := me.path(rep.Path)
		if HasDirPrefix(fullPath, "/usr") && !HasDirPrefix(fullPath, "/usr/local") {
			rep.Hash = me.contentCache.SaveImmutablePath(fullPath)
		} else {
			rep.Hash = me.contentCache.SavePath(fullPath)
		}
		if rep.Hash == "" {
			// Typically happens if we want to open /etc/shadow as normal user.
			log.Println("fillContent returning EPERM for", rep.Path)
			rep.FileInfo = nil
		}
	}
}

func (me *FsServer) updateFiles(infos []*FileAttr) {
	me.attr.Update(infos)
}

func (me *FsServer) refreshAttributeCache(prefix string) FileSet {
	return me.attr.Refresh(prefix)
}

func (me *FsServer) copyCache() FileSet {
	return me.attr.Copy()
}

func (me *FsServer) FetchDirs(root string) {
	a := me.attr.GetDir(root)
	for n, m := range a.NameModeMap {
		if m.IsDirectory() {
			me.FetchDirs(filepath.Join(root, n))
		}
	}
}
<commit_msg>Don't print hash separately.<commit_after>package termite

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var _ = fmt.Println

type FsServer struct {
	contentServer  *ContentServer
	contentCache   *ContentCache
	Root           string
	excluded       map[string]bool
	excludePrivate bool
	attr           *AttributeCache
}

func NewFsServer(root string, cache *ContentCache, excluded []string) *FsServer {
	me := &FsServer{
		contentCache:   cache,
		contentServer:  &ContentServer{Cache: cache},
		Root:           root,
		excludePrivate: true,
	}
	me.attr = NewAttributeCache(func(n string) *FileAttr {
		return me.uncachedGetAttr(n)
	},
		func(n string) *os.FileInfo {
			fi, _ := os.Lstat(me.path(n))
			return fi
		})
	me.excluded = make(map[string]bool)
	for _, e := range excluded {
		if e[0] == '/' {
			panic("leading slash")
		}
		me.excluded[e] = true
	}
	return me
}

func (me *FsServer) path(n string) string {
	if me.Root == "" {
		return n
	}
	return filepath.Join(me.Root, strings.TrimLeft(n, "/"))
}

func (me *FsServer) FileContent(req *ContentRequest, rep *ContentResponse) os.Error {
	return me.contentServer.FileContent(req, rep)
}

func (me *FsServer) GetAttr(req *AttrRequest, rep *AttrResponse) os.Error {
	log.Printf("GetAttr req %q", req.Name)
	if req.Name != "" && req.Name[0] == '/' {
		panic("leading /")
	}

	a := me.attr.GetDir(req.Name)
	if a.Hash != "" {
		log.Printf("GetAttr %v", a)
	}
	rep.Attrs = append(rep.Attrs, a)
	return nil
}

func (me *FsServer) uncachedGetAttr(name string) (rep *FileAttr) {
	rep = &FileAttr{Path: name}
	p := me.path(name)
	fi, _ := os.Lstat(p)
	// We don't want to expose the master's private files to the
	// world.
	if me.excludePrivate && fi != nil && fi.Mode&0077 == 0 {
		log.Printf("Denied access to private file %q", name)
		return rep
	}

	if me.excluded[name] {
		log.Printf("Denied access to excluded file %q", name)
		return rep
	}
	rep.FileInfo = fi
	if fi != nil {
		me.fillContent(rep)
	}
	return rep
}

func (me *FsServer) fillContent(rep *FileAttr) {
	if rep.IsSymlink() || rep.IsDirectory() {
		rep.ReadFromFs(me.path(rep.Path))
	}
	if rep.IsRegular() {
		// TODO - /usr should be configurable.
		fullPath := me.path(rep.Path)
		if HasDirPrefix(fullPath, "/usr") && !HasDirPrefix(fullPath, "/usr/local") {
			rep.Hash = me.contentCache.SaveImmutablePath(fullPath)
		} else {
			rep.Hash = me.contentCache.SavePath(fullPath)
		}
		if rep.Hash == "" {
			// Typically happens if we want to open /etc/shadow as normal user.
			log.Println("fillContent returning EPERM for", rep.Path)
			rep.FileInfo = nil
		}
	}
}

func (me *FsServer) updateFiles(infos []*FileAttr) {
	me.attr.Update(infos)
}

func (me *FsServer) refreshAttributeCache(prefix string) FileSet {
	return me.attr.Refresh(prefix)
}

func (me *FsServer) copyCache() FileSet {
	return me.attr.Copy()
}

func (me *FsServer) FetchDirs(root string) {
	a := me.attr.GetDir(root)
	for n, m := range a.NameModeMap {
		if m.IsDirectory() {
			me.FetchDirs(filepath.Join(root, n))
		}
	}
}
<|endoftext|>


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

package metric

import (
	"errors"
	"testing"

	"github.com/fsouza/go-dockerclient"
	"github.com/tsuru/bs/container"
	"gopkg.in/check.v1"
)

var _ = check.Suite(&S{})

func Test(t *testing.T) {
	check.TestingT(t)
}

type S struct{}

func (s *S) SetUpTest(c *check.C) {
	fakeStatter.reset()
}

func (s *S) TestSendMetrics(c *check.C) {
	cont := container.Container{
		Container:   docker.Container{Config: &docker.Config{Hostname: "afdb3737ff"}},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	r := Reporter{backend: &fakeStatter}
	metrics := map[string]float{"cpu": float(900), "mem": float(512)}
	err := r.sendMetrics(&cont, metrics)
	c.Assert(err, check.IsNil)
	expected := []fakeStat{
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "cpu", value: float(900)},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "mem", value: float(512)},
	}
	if fakeStatter.stats[0].key != "cpu" {
		expected[0], expected[1] = expected[1], expected[0]
	}
	c.Assert(fakeStatter.stats, check.DeepEquals, expected)
}

func (s *S) TestSendMetricsFailure(c *check.C) {
	cont := container.Container{
		Container:   docker.Container{Config: &docker.Config{Hostname: "afdb3737ff"}},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	r := Reporter{backend: &fakeStatter}
	prepErr := errors.New("something went wrong")
	fakeStatter.prepareFailure(prepErr)
	err := r.sendMetrics(&cont, map[string]float{"cpu": float(256)})
	c.Assert(err, check.Equals, prepErr)
}

func (s *S) TestSendConnMetrics(c *check.C) {
	cont := container.Container{
		Container: docker.Container{
			Config:          &docker.Config{Hostname: "afdb3737ff"},
			NetworkSettings: &docker.NetworkSettings{IPAddress: "172.17.0.27"},
		},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	conns := []conn{
		{SourceIP: "192.168.50.4", SourcePort: "33404", DestinationIP: "192.168.50.4", DestinationPort: "2375"},
		{SourceIP: "172.17.42.1", SourcePort: "42418", DestinationIP: "172.17.0.27", DestinationPort: "4001"},
		{SourceIP: "172.17.42.1", SourcePort: "42428", DestinationIP: "172.17.0.27", DestinationPort: "4001"},
		{SourceIP: "192.168.50.4", SourcePort: "53922", DestinationIP: "192.168.50.4", DestinationPort: "5000"},
		{SourceIP: "192.168.50.4", SourcePort: "43227", DestinationIP: "192.168.50.4", DestinationPort: "8080"},
		{SourceIP: "172.17.0.27", SourcePort: "39502", DestinationIP: "172.17.42.1", DestinationPort: "4001"},
		{SourceIP: "192.168.50.4", SourcePort: "33496", DestinationIP: "192.168.50.4", DestinationPort: "2375"},
		{SourceIP: "192.168.50.4", SourcePort: "33495", DestinationIP: "192.168.50.4", DestinationPort: "2375"},
		{SourceIP: "10.211.55.2", SourcePort: "51388", DestinationIP: "10.211.55.184", DestinationPort: "22"},
		{SourceIP: "172.17.0.27", SourcePort: "39492", DestinationIP: "172.17.42.1", DestinationPort: "4001"},
		{SourceIP: "172.17.0.27", SourcePort: "39492", DestinationIP: "192.168.50.4", DestinationPort: "8080"},
		{SourceIP: "10.211.55.2", SourcePort: "51370", DestinationIP: "10.211.55.184", DestinationPort: "22"},
	}
	r := Reporter{backend: &fakeStatter}
	err := r.sendConnMetrics(&cont, conns)
	c.Assert(err, check.IsNil)
	expected := []fakeStat{
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:42418"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:42428"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:4001"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:4001"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "192.168.50.4:8080"},
	}
	c.Assert(fakeStatter.stats, check.DeepEquals, expected)
}

func (s *S) TestSendConnMetricsFailure(c *check.C) {
	cont := container.Container{
		Container: docker.Container{
			Config:          &docker.Config{Hostname: "afdb3737ff"},
			NetworkSettings: &docker.NetworkSettings{IPAddress: "172.17.0.3"},
		},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	r := Reporter{backend: &fakeStatter}
	prepErr := errors.New("something went wrong")
	fakeStatter.prepareFailure(prepErr)
	conns := []conn{{SourceIP: "172.17.0.3"}}
	err := r.sendConnMetrics(&cont, conns)
	c.Assert(err, check.Equals, prepErr)
}

func (s *S) TestGetMetrics(c *check.C) {
	var containers []docker.APIContainers
	r := &Reporter{}
	r.getMetrics(containers)
}

func (s *S) TestSendSysMetrics(c *check.C) {
	r := Reporter{backend: &fakeStatter}
	metrics := map[string]float{"cpu": float(900), "mem": float(512)}
	err := r.sendHostMetrics("hostname", metrics)
	c.Assert(err, check.IsNil)
	expected := []fakeStat{
		{app: "sysapp", hostname: "hostname", process: "-", key: "cpu", value: float(900)},
		{app: "sysapp", hostname: "hostname", process: "-", key: "mem", value: float(512)},
	}
	if fakeStatter.stats[0].key != "cpu" {
		expected[0], expected[1] = expected[1], expected[0]
	}
	c.Assert(fakeStatter.stats, check.DeepEquals, expected)
}

func (s *S) TestSendSysMetricsFailure(c *check.C) {
	r := Reporter{backend: &fakeStatter}
	prepErr := errors.New("something wen wrong")
	fakeStatter.prepareFailure(prepErr)
	metrics := map[string]float{"cpu": float(900)}
	err := r.sendHostMetrics("hostname", metrics)
	c.Assert(err, check.Equals, prepErr)
}
<commit_msg>metric/reporter: fix test name<commit_after>// Copyright 2015 bs authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package metric

import (
	"errors"
	"testing"

	"github.com/fsouza/go-dockerclient"
	"github.com/tsuru/bs/container"
	"gopkg.in/check.v1"
)

var _ = check.Suite(&S{})

func Test(t *testing.T) {
	check.TestingT(t)
}

type S struct{}

func (s *S) SetUpTest(c *check.C) {
	fakeStatter.reset()
}

func (s *S) TestSendMetrics(c *check.C) {
	cont := container.Container{
		Container:   docker.Container{Config: &docker.Config{Hostname: "afdb3737ff"}},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	r := Reporter{backend: &fakeStatter}
	metrics := map[string]float{"cpu": float(900), "mem": float(512)}
	err := r.sendMetrics(&cont, metrics)
	c.Assert(err, check.IsNil)
	expected := []fakeStat{
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "cpu", value: float(900)},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "mem", value: float(512)},
	}
	if fakeStatter.stats[0].key != "cpu" {
		expected[0], expected[1] = expected[1], expected[0]
	}
	c.Assert(fakeStatter.stats, check.DeepEquals, expected)
}

func (s *S) TestSendMetricsFailure(c *check.C) {
	cont := container.Container{
		Container:   docker.Container{Config: &docker.Config{Hostname: "afdb3737ff"}},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	r := Reporter{backend: &fakeStatter}
	prepErr := errors.New("something went wrong")
	fakeStatter.prepareFailure(prepErr)
	err := r.sendMetrics(&cont, map[string]float{"cpu": float(256)})
	c.Assert(err, check.Equals, prepErr)
}

func (s *S) TestSendConnMetrics(c *check.C) {
	cont := container.Container{
		Container: docker.Container{
			Config:          &docker.Config{Hostname: "afdb3737ff"},
			NetworkSettings: &docker.NetworkSettings{IPAddress: "172.17.0.27"},
		},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	conns := []conn{
		{SourceIP: "192.168.50.4", SourcePort: "33404", DestinationIP: "192.168.50.4", DestinationPort: "2375"},
		{SourceIP: "172.17.42.1", SourcePort: "42418", DestinationIP: "172.17.0.27", DestinationPort: "4001"},
		{SourceIP: "172.17.42.1", SourcePort: "42428", DestinationIP: "172.17.0.27", DestinationPort: "4001"},
		{SourceIP: "192.168.50.4", SourcePort: "53922", DestinationIP: "192.168.50.4", DestinationPort: "5000"},
		{SourceIP: "192.168.50.4", SourcePort: "43227", DestinationIP: "192.168.50.4", DestinationPort: "8080"},
		{SourceIP: "172.17.0.27", SourcePort: "39502", DestinationIP: "172.17.42.1", DestinationPort: "4001"},
		{SourceIP: "192.168.50.4", SourcePort: "33496", DestinationIP: "192.168.50.4", DestinationPort: "2375"},
		{SourceIP: "192.168.50.4", SourcePort: "33495", DestinationIP: "192.168.50.4", DestinationPort: "2375"},
		{SourceIP: "10.211.55.2", SourcePort: "51388", DestinationIP: "10.211.55.184", DestinationPort: "22"},
		{SourceIP: "172.17.0.27", SourcePort: "39492", DestinationIP: "172.17.42.1", DestinationPort: "4001"},
		{SourceIP: "172.17.0.27", SourcePort: "39492", DestinationIP: "192.168.50.4", DestinationPort: "8080"},
		{SourceIP: "10.211.55.2", SourcePort: "51370", DestinationIP: "10.211.55.184", DestinationPort: "22"},
	}
	r := Reporter{backend: &fakeStatter}
	err := r.sendConnMetrics(&cont, conns)
	c.Assert(err, check.IsNil)
	expected := []fakeStat{
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:42418"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:42428"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:4001"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "172.17.42.1:4001"},
		{app: "myapp", hostname: "afdb3737ff", process: "myprocess", key: "connection", value: "192.168.50.4:8080"},
	}
	c.Assert(fakeStatter.stats, check.DeepEquals, expected)
}

func (s *S) TestSendConnMetricsFailure(c *check.C) {
	cont := container.Container{
		Container: docker.Container{
			Config:          &docker.Config{Hostname: "afdb3737ff"},
			NetworkSettings: &docker.NetworkSettings{IPAddress: "172.17.0.3"},
		},
		AppName:     "myapp",
		ProcessName: "myprocess",
	}
	r := Reporter{backend: &fakeStatter}
	prepErr := errors.New("something went wrong")
	fakeStatter.prepareFailure(prepErr)
	conns := []conn{{SourceIP: "172.17.0.3"}}
	err := r.sendConnMetrics(&cont, conns)
	c.Assert(err, check.Equals, prepErr)
}

func (s *S) TestGetMetrics(c *check.C) {
	var containers []docker.APIContainers
	r := &Reporter{}
	r.getMetrics(containers)
}

func (s *S) TestSendHostMetrics(c *check.C) {
	r := Reporter{backend: &fakeStatter}
	metrics := map[string]float{"cpu": float(900), "mem": float(512)}
	err := r.sendHostMetrics("hostname", metrics)
	c.Assert(err, check.IsNil)
	expected := []fakeStat{
		{app: "sysapp", hostname: "hostname", process: "-", key: "cpu", value: float(900)},
		{app: "sysapp", hostname: "hostname", process: "-", key: "mem", value: float(512)},
	}
	if fakeStatter.stats[0].key != "cpu" {
		expected[0], expected[1] = expected[1], expected[0]
	}
	c.Assert(fakeStatter.stats, check.DeepEquals, expected)
}

func (s *S) TestSendHostMetricsFailure(c *check.C) {
	r := Reporter{backend: &fakeStatter}
	prepErr := errors.New("something wen wrong")
	fakeStatter.prepareFailure(prepErr)
	metrics := map[string]float{"cpu": float(900)}
	err := r.sendHostMetrics("hostname", metrics)
	c.Assert(err, check.Equals, prepErr)
}
<|endoftext|>


<commit_before>package middleware

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	"time"

	u "github.com/techjanitor/pram-get/utils"
)

// requesttype holds the data we want to capture
type RequestType struct {
	Ib        string
	Ip        string
	Path      string
	Status    int
	Latency   time.Duration
	Useragent string
	Referer   string
	Country   string
}

// Analytics will log requests in the database
func Analytics() gin.HandlerFunc {
	return func(c *gin.Context) {
		req := c.Request
		// Start timer
		start := time.Now()
		// get request path
		path := req.URL.Path

		// Process request
		c.Next()

		// get the ib
		ib := c.Param("ib")

		// abort if theres no ib
		if ib == "" {
			c.Abort()
			return
		}

		// Stop timer
		end := time.Now()
		// get request latency
		latency := end.Sub(start)

		// set our data
		request := RequestType{
			Ib:        ib,
			Ip:        c.ClientIP(),
			Path:      path,
			Status:    c.Writer.Status(),
			Latency:   latency,
			Useragent: req.UserAgent(),
			Referer:   req.Referer(),
			Country:   req.Header.Get("CF-IPCountry"),
		}

		// Get Database handle
		db, err := u.GetDb()
		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}

		// prepare query for analytics table
		ps1, err = db.Prepare("INSERT INTO analytics (ib_id, request_time, request_ip, request_path, request_status) VALUES (?,NOW(),?,?,?)")
		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}

		// input data
		_, err := ps1.Exec(request.Ib, request.Ip, request.Path, request.Status)
		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}

	}
}
<commit_msg>add analytics middleware<commit_after>package middleware

import (
	"github.com/gin-gonic/gin"
	"time"

	u "github.com/techjanitor/pram-get/utils"
)

// requesttype holds the data we want to capture
type RequestType struct {
	Ib        string
	Ip        string
	Path      string
	Status    int
	Latency   time.Duration
	Useragent string
	Referer   string
	Country   string
}

// Analytics will log requests in the database
func Analytics() gin.HandlerFunc {
	return func(c *gin.Context) {
		req := c.Request
		// Start timer
		start := time.Now()
		// get request path
		path := req.URL.Path

		// Process request
		c.Next()

		// get the ib
		ib := c.Param("ib")

		// abort if theres no ib
		if ib == "" {
			c.Abort()
			return
		}

		// Stop timer
		end := time.Now()
		// get request latency
		latency := end.Sub(start)

		// set our data
		request := RequestType{
			Ib:        ib,
			Ip:        c.ClientIP(),
			Path:      path,
			Status:    c.Writer.Status(),
			Latency:   latency,
			Useragent: req.UserAgent(),
			Referer:   req.Referer(),
			Country:   req.Header.Get("CF-IPCountry"),
		}

		// Get Database handle
		db, err := u.GetDb()
		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}

		// prepare query for analytics table
		ps1, err := db.Prepare("INSERT INTO analytics (ib_id, request_time, request_ip, request_path, request_status) VALUES (?,NOW(),?,?,?)")
		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}

		// input data
		_, err := ps1.Exec(request.Ib, request.Ip, request.Path, request.Status)
		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}

	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package log

import (
	"encoding/json"
	"errors"
	"fmt"
	"log/syslog"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
)

// singleton defines the object of a Singleton pattern
type singleton struct {
	once sync.Once
	log  *AuditLogger
}

// _Singleton is the single AuditLogger entity in memory
var _Singleton singleton

// The constant used to identify audit-specific messages
const auditTag = "[AUDIT]"

// Constant used to indicate an emergency exit to the executor
const emergencyReturnValue = 13

// exitFunction closes the running system
type exitFunction func()

// Default to calling os.Exit()
func defaultEmergencyExit() {
	os.Exit(emergencyReturnValue)
}

// AuditLogger is a System Logger with additional audit-specific methods.
// In addition to all the standard syslog.Writer methods from
// http://golang.org/pkg/log/syslog/#Writer, you can also call
//   auditLogger.Audit(msg string)
// to send a message as an audit event.
type AuditLogger struct {
	*syslog.Writer
	Stats        statsd.Statter
	exitFunction exitFunction
}

// Dial establishes a connection to the log daemon by passing through
// the parameters to the syslog.Dial method.
// See http://golang.org/pkg/log/syslog/#Dial
func Dial(network, raddr string, tag string, stats statsd.Statter) (*AuditLogger, error) {
	syslogger, err := syslog.Dial(network, raddr, syslog.LOG_INFO|syslog.LOG_LOCAL0, tag)
	if err != nil {
		return nil, err
	}
	return NewAuditLogger(syslogger, stats)
}

// NewAuditLogger constructs an Audit Logger that decorates a normal
// System Logger. All methods in log/syslog continue to work.
func NewAuditLogger(log *syslog.Writer, stats statsd.Statter) (*AuditLogger, error) {
	if log == nil {
		return nil, errors.New("Attempted to use a nil System Logger.")
	}
	audit := &AuditLogger{
		log,
		stats,
		defaultEmergencyExit,
	}
	return audit, nil
}

// initializeAuditLogger should only be used in unit tests. Failures in this
// method are unlikely as the defaults are safe, and they are also
// of minimal consequence during unit testing -- logs get printed to stdout
// even if syslog is missing.
func initializeAuditLogger() {
	stats, _ := statsd.NewNoopClient(nil)
	audit, _ := Dial("", "", "default", stats)
	audit.Notice("Using default logging configuration.")

	SetAuditLogger(audit)
}

// SetAuditLogger configures the singleton audit logger. This method
// must only be called once, and before calling GetAuditLogger the
// first time.
func SetAuditLogger(logger *AuditLogger) (err error) {
	if _Singleton.log != nil {
		err = errors.New("You may not call SetAuditLogger after it has already been implicitly or explicitly set.")
		_Singleton.log.WarningErr(err)
	} else {
		_Singleton.log = logger
	}
	return
}

// GetAuditLogger obtains the singleton audit logger. If SetAuditLogger
// has not been called first, this method initializes with basic defaults.
// The basic defaults cannot error, and subequent access to an already-set
// AuditLogger also cannot error, so this method is error-safe.
func GetAuditLogger() *AuditLogger {
	_Singleton.once.Do(func() {
		if _Singleton.log == nil {
			initializeAuditLogger()
		}
	})

	return _Singleton.log
}

// Log the provided message at the appropriate level, writing to
// both stdout and the Logger, as well as informing statsd.
func (log *AuditLogger) logAtLevel(level, msg string) (err error) {
	fmt.Printf("%s %s\n", time.Now().Format("2006/01/02 15:04:05"), msg)
	log.Stats.Inc(level, 1, 1.0)

	switch level {
	case "Logging.Alert":
		err = log.Writer.Alert(msg)
	case "Logging.Crit":
		err = log.Writer.Crit(msg)
	case "Logging.Debug":
		err = log.Writer.Debug(msg)
	case "Logging.Emerg":
		err = log.Writer.Emerg(msg)
	case "Logging.Err":
		err = log.Writer.Err(msg)
	case "Logging.Info":
		err = log.Writer.Info(msg)
	case "Logging.Warning":
		err = log.Writer.Warning(msg)
	case "Logging.Notice":
		err = log.Writer.Notice(msg)
	default:
		err = fmt.Errorf("Unknown logging level: %s", level)
	}
	return
}

// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) auditAtLevel(level, msg string) (err error) {
	// Submit a separate counter that marks an Audit event
	log.Stats.Inc("Logging.Audit", 1, 1.0)

	text := fmt.Sprintf("%s %s", auditTag, msg)
	return log.logAtLevel(level, text)
}

// Return short format caller info for panic events, skipping to before the
// panic handler.
func caller(level int) string {
	_, file, line, _ := runtime.Caller(level)
	splits := strings.Split(file, "/")
	filename := splits[len(splits)-1]
	return fmt.Sprintf("%s:%d:", filename, line)
}

// AuditPanic catches panicking executables. This method should be added
// in a defer statement as early as possible
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) AuditPanic() {
	if err := recover(); err != nil {
		log.Audit(fmt.Sprintf("Panic: %s %v", caller(4), err))
	}
}

// WarningErr formats an error for the Warn level.
func (log *AuditLogger) WarningErr(msg error) (err error) {
	return log.logAtLevel("Logging.Warning", msg.Error())
}

// Alert level messages pass through normally.
func (log *AuditLogger) Alert(msg string) (err error) {
	return log.logAtLevel("Logging.Alert", msg)
}

// Crit level messages are automatically marked for audit
func (log *AuditLogger) Crit(msg string) (err error) {
	return log.auditAtLevel("Logging.Crit", msg)
}

// Debug level messages pass through normally.
func (log *AuditLogger) Debug(msg string) (err error) {
	return log.logAtLevel("Logging.Debug", msg)
}

// Emerg level messages are automatically marked for audit
func (log *AuditLogger) Emerg(msg string) (err error) {
	return log.auditAtLevel("Logging.Emerg", msg)
}

// Err level messages are automatically marked for audit
func (log *AuditLogger) Err(msg string) (err error) {
	return log.auditAtLevel("Logging.Err", msg)
}

// Info level messages pass through normally.
func (log *AuditLogger) Info(msg string) (err error) {
	return log.logAtLevel("Logging.Info", msg)
}

// Warning level messages pass through normally.
func (log *AuditLogger) Warning(msg string) (err error) {
	return log.logAtLevel("Logging.Warning", msg)
}

// Notice level messages pass through normally.
func (log *AuditLogger) Notice(msg string) (err error) {
	return log.logAtLevel("Logging.Notice", msg)
}

// Audit sends a NOTICE-severity message that is prefixed with the
// audit tag, for special handling at the upstream system logger.
func (log *AuditLogger) Audit(msg string) (err error) {
	return log.auditAtLevel("Logging.Notice", msg)
}

// AuditObject sends a NOTICE-severity JSON-serialized object message that is prefixed
// with the audit tag, for special handling at the upstream system logger.
func (log *AuditLogger) AuditObject(msg string, obj interface{}) (err error) {
	jsonLogEvent, logErr := json.Marshal(obj)
	if logErr != nil {
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		log.auditAtLevel("Logging.Err", fmt.Sprintf("%s - logEvent could not be serialized. Raw: %+v", msg, obj))
		return logErr
	}

	return log.auditAtLevel("Logging.Notice", fmt.Sprintf("%s - %s", msg, jsonLogEvent))
}

// AuditErr can format an error for auditing; it does so at ERR level.
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) AuditErr(msg error) (err error) {
	return log.auditAtLevel("Logging.Err", msg.Error())
}

// SetEmergencyExitFunc changes the systems' behavior on an emergency exit.
func (log *AuditLogger) SetEmergencyExitFunc(exit exitFunction) {
	log.exitFunction = exit
}

// EmergencyExit triggers an immediate Boulder shutdown in the event of serious
// errors. This function will provide the necessary housekeeping.
// Currently, make an emergency log entry and exit; the Activity Monitor
// should notice the Emerg level event and shut down all components.
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) EmergencyExit(msg string) {
	log.auditAtLevel("Logging.Emerg", msg)
	log.exitFunction()
}
<commit_msg>Improve audit logging on panic conditions: - Audit log the error - Audit log the current frame - Warning log the other frames (up to 8k)<commit_after>// Copyright 2015 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package log

import (
	"encoding/json"
	"errors"
	"fmt"
	"log/syslog"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
)

// singleton defines the object of a Singleton pattern
type singleton struct {
	once sync.Once
	log  *AuditLogger
}

// _Singleton is the single AuditLogger entity in memory
var _Singleton singleton

// The constant used to identify audit-specific messages
const auditTag = "[AUDIT]"

// Constant used to indicate an emergency exit to the executor
const emergencyReturnValue = 13

// exitFunction closes the running system
type exitFunction func()

// Default to calling os.Exit()
func defaultEmergencyExit() {
	os.Exit(emergencyReturnValue)
}

// AuditLogger is a System Logger with additional audit-specific methods.
// In addition to all the standard syslog.Writer methods from
// http://golang.org/pkg/log/syslog/#Writer, you can also call
//   auditLogger.Audit(msg string)
// to send a message as an audit event.
type AuditLogger struct {
	*syslog.Writer
	Stats        statsd.Statter
	exitFunction exitFunction
}

// Dial establishes a connection to the log daemon by passing through
// the parameters to the syslog.Dial method.
// See http://golang.org/pkg/log/syslog/#Dial
func Dial(network, raddr string, tag string, stats statsd.Statter) (*AuditLogger, error) {
	syslogger, err := syslog.Dial(network, raddr, syslog.LOG_INFO|syslog.LOG_LOCAL0, tag)
	if err != nil {
		return nil, err
	}
	return NewAuditLogger(syslogger, stats)
}

// NewAuditLogger constructs an Audit Logger that decorates a normal
// System Logger. All methods in log/syslog continue to work.
func NewAuditLogger(log *syslog.Writer, stats statsd.Statter) (*AuditLogger, error) {
	if log == nil {
		return nil, errors.New("Attempted to use a nil System Logger.")
	}
	audit := &AuditLogger{
		log,
		stats,
		defaultEmergencyExit,
	}
	return audit, nil
}

// initializeAuditLogger should only be used in unit tests. Failures in this
// method are unlikely as the defaults are safe, and they are also
// of minimal consequence during unit testing -- logs get printed to stdout
// even if syslog is missing.
func initializeAuditLogger() {
	stats, _ := statsd.NewNoopClient(nil)
	audit, _ := Dial("", "", "default", stats)
	audit.Notice("Using default logging configuration.")

	SetAuditLogger(audit)
}

// SetAuditLogger configures the singleton audit logger. This method
// must only be called once, and before calling GetAuditLogger the
// first time.
func SetAuditLogger(logger *AuditLogger) (err error) {
	if _Singleton.log != nil {
		err = errors.New("You may not call SetAuditLogger after it has already been implicitly or explicitly set.")
		_Singleton.log.WarningErr(err)
	} else {
		_Singleton.log = logger
	}
	return
}

// GetAuditLogger obtains the singleton audit logger. If SetAuditLogger
// has not been called first, this method initializes with basic defaults.
// The basic defaults cannot error, and subequent access to an already-set
// AuditLogger also cannot error, so this method is error-safe.
func GetAuditLogger() *AuditLogger {
	_Singleton.once.Do(func() {
		if _Singleton.log == nil {
			initializeAuditLogger()
		}
	})

	return _Singleton.log
}

// Log the provided message at the appropriate level, writing to
// both stdout and the Logger, as well as informing statsd.
func (log *AuditLogger) logAtLevel(level, msg string) (err error) {
	fmt.Printf("%s %s\n", time.Now().Format("2006/01/02 15:04:05"), msg)
	log.Stats.Inc(level, 1, 1.0)

	switch level {
	case "Logging.Alert":
		err = log.Writer.Alert(msg)
	case "Logging.Crit":
		err = log.Writer.Crit(msg)
	case "Logging.Debug":
		err = log.Writer.Debug(msg)
	case "Logging.Emerg":
		err = log.Writer.Emerg(msg)
	case "Logging.Err":
		err = log.Writer.Err(msg)
	case "Logging.Info":
		err = log.Writer.Info(msg)
	case "Logging.Warning":
		err = log.Writer.Warning(msg)
	case "Logging.Notice":
		err = log.Writer.Notice(msg)
	default:
		err = fmt.Errorf("Unknown logging level: %s", level)
	}
	return
}

// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) auditAtLevel(level, msg string) (err error) {
	// Submit a separate counter that marks an Audit event
	log.Stats.Inc("Logging.Audit", 1, 1.0)

	text := fmt.Sprintf("%s %s", auditTag, msg)
	return log.logAtLevel(level, text)
}

// Return short format caller info for panic events, skipping to before the
// panic handler.
func caller(level int) string {
	_, file, line, _ := runtime.Caller(level)
	splits := strings.Split(file, "/")
	filename := splits[len(splits)-1]
	return fmt.Sprintf("%s:%d:", filename, line)
}

// AuditPanic catches panicking executables. This method should be added
// in a defer statement as early as possible
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) AuditPanic() {
	if err := recover(); err != nil {
		buf := make([]byte, 8192)
		log.Audit(fmt.Sprintf("Panic caused by err: %v", err))

		runtime.Stack(buf, false)
		log.Audit(fmt.Sprintf("Stack Trace (Current frame) %s", buf))

		runtime.Stack(buf, true)
		log.Warning(fmt.Sprintf("Stack Trace (All frames): %s", buf))
	}
}

// WarningErr formats an error for the Warn level.
func (log *AuditLogger) WarningErr(msg error) (err error) {
	return log.logAtLevel("Logging.Warning", msg.Error())
}

// Alert level messages pass through normally.
func (log *AuditLogger) Alert(msg string) (err error) {
	return log.logAtLevel("Logging.Alert", msg)
}

// Crit level messages are automatically marked for audit
func (log *AuditLogger) Crit(msg string) (err error) {
	return log.auditAtLevel("Logging.Crit", msg)
}

// Debug level messages pass through normally.
func (log *AuditLogger) Debug(msg string) (err error) {
	return log.logAtLevel("Logging.Debug", msg)
}

// Emerg level messages are automatically marked for audit
func (log *AuditLogger) Emerg(msg string) (err error) {
	return log.auditAtLevel("Logging.Emerg", msg)
}

// Err level messages are automatically marked for audit
func (log *AuditLogger) Err(msg string) (err error) {
	return log.auditAtLevel("Logging.Err", msg)
}

// Info level messages pass through normally.
func (log *AuditLogger) Info(msg string) (err error) {
	return log.logAtLevel("Logging.Info", msg)
}

// Warning level messages pass through normally.
func (log *AuditLogger) Warning(msg string) (err error) {
	return log.logAtLevel("Logging.Warning", msg)
}

// Notice level messages pass through normally.
func (log *AuditLogger) Notice(msg string) (err error) {
	return log.logAtLevel("Logging.Notice", msg)
}

// Audit sends a NOTICE-severity message that is prefixed with the
// audit tag, for special handling at the upstream system logger.
func (log *AuditLogger) Audit(msg string) (err error) {
	return log.auditAtLevel("Logging.Notice", msg)
}

// AuditObject sends a NOTICE-severity JSON-serialized object message that is prefixed
// with the audit tag, for special handling at the upstream system logger.
func (log *AuditLogger) AuditObject(msg string, obj interface{}) (err error) {
	jsonLogEvent, logErr := json.Marshal(obj)
	if logErr != nil {
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		log.auditAtLevel("Logging.Err", fmt.Sprintf("%s - logEvent could not be serialized. Raw: %+v", msg, obj))
		return logErr
	}

	return log.auditAtLevel("Logging.Notice", fmt.Sprintf("%s - %s", msg, jsonLogEvent))
}

// AuditErr can format an error for auditing; it does so at ERR level.
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) AuditErr(msg error) (err error) {
	return log.auditAtLevel("Logging.Err", msg.Error())
}

// SetEmergencyExitFunc changes the systems' behavior on an emergency exit.
func (log *AuditLogger) SetEmergencyExitFunc(exit exitFunction) {
	log.exitFunction = exit
}

// EmergencyExit triggers an immediate Boulder shutdown in the event of serious
// errors. This function will provide the necessary housekeeping.
// Currently, make an emergency log entry and exit; the Activity Monitor
// should notice the Emerg level event and shut down all components.
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
func (log *AuditLogger) EmergencyExit(msg string) {
	log.auditAtLevel("Logging.Emerg", msg)
	log.exitFunction()
}
<|endoftext|>


<commit_before>package modular

import (
	"testing"

	"errors"

	"bytes"
	"encoding/gob"

	"io"

	"github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/require"
)

func TestLoggerBase_WithFields(t *testing.T) {
	// Create a new logger
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
		fields: logrus.Fields{
			"test": "test",
		},
	}

	// Create a new logger by calling WithFields
	logger := lb.WithFields(logrus.Fields{
		"test2": "test2",
	})
	require.NotNil(t, logger)

	lb2, ok := logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 2)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.Contains(t, lb2.fields, "test2")
	require.EqualValues(t, "test", lb2.fields["test"])
	require.EqualValues(t, "test2", lb2.fields["test2"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])

	// Test override of field value
	logger = lb.WithFields(logrus.Fields{
		"test": "test2",
	})
	lb2, ok = logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 1)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.EqualValues(t, "test2", lb2.fields["test"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])
}

func TestLoggerBase_WithField(t *testing.T) {
	// Create a new logger
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
		fields: logrus.Fields{
			"test": "test",
		},
	}

	logger := lb.WithField("test2", "test2")
	require.NotNil(t, logger)

	lb2, ok := logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 2)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.Contains(t, lb2.fields, "test2")
	require.EqualValues(t, "test", lb2.fields["test"])
	require.EqualValues(t, "test2", lb2.fields["test2"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])
}

func TestLoggerBase_WithError(t *testing.T) {
	// Create a new logger
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
		fields: logrus.Fields{
			"test": "test",
		},
	}

	err := errors.New("test2")

	logger := lb.WithError(err)
	require.NotNil(t, logger)

	lb2, ok := logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 2)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.Contains(t, lb2.fields, "error")
	require.EqualValues(t, "test", lb2.fields["test"])
	require.EqualValues(t, err, lb2.fields["error"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])
}

func TestLoggerBase_GetModuleLogger(t *testing.T) {
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
	}

	require.EqualValues(t, lb.moduleLogger, lb.GetModuleLogger())
}

func TestLoggerBase_NewEntry(t *testing.T) {
	lb := &loggerBase{
		moduleLogger: &loggerModule{
			level: logrus.DebugLevel,
			name:  "test_module",
			root: &loggerRoot{
				logger:      &logrus.Logger{},
				moduleField: "module",
			},
		},
	}

	levels := []logrus.Level{
		logrus.DebugLevel,
		logrus.InfoLevel,
		logrus.WarnLevel,
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}

	// Test our "base" case: debug logging enabled at module level,
	// newEntry called with debug level.
	// This must return a new entry.
	require.NotNil(t, lb.newEntry(logrus.DebugLevel))

	// Test if newEntry behaves correctly...
	for i, level := range levels {
		if i == 0 {
			// Ignore debug level above
			continue
		}
		lb.moduleLogger.SetLevel(level)
		for j := 0; j < i; j++ {
			require.Nil(t, lb.newEntry(levels[j]), "Level is %s, newEntry(%s) should return nil", level.String(), levels[j].String())
		}
		for j := i; j < len(levels); j++ {
			require.NotNil(t, lb.newEntry(levels[j]), "Level is %s, newEntry(%s) should not return nil", levels[j].String(), levels[j].String())
		}
	}

	// Test if newEntry creates a copy of fields...
	lb.moduleLogger.SetLevel(logrus.DebugLevel)
	lb.fields = logrus.Fields{
		"test":  "test",
		"test2": "test2",
	}

	entry := lb.newEntry(logrus.DebugLevel)
	require.NotNil(t, entry)
	require.Len(t, entry.Data, len(lb.fields)+1)
	require.Contains(t, entry.Data, "test")
	require.EqualValues(t, "test", entry.Data["test"])
	require.Contains(t, entry.Data, "test2")
	require.EqualValues(t, "test2", entry.Data["test2"])
	require.Contains(t, entry.Data, "module")
	require.EqualValues(t, "test_module", entry.Data["module"])
}

var _ logrus.Formatter = (*gobFormatter)(nil)

type gobFormatter struct {
}

func (gf *gobFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	buffer := bytes.Buffer{}
	encoder := gob.NewEncoder(&buffer)
	logger := entry.Logger
	entry.Logger = nil
	defer func() {
		entry.Logger = logger
	}()
	if err := encoder.Encode(entry); err != nil {
		return nil, err
	}

	return buffer.Bytes(), nil
}

func (gf gobFormatter) Decode(reader io.Reader) (*logrus.Entry, error) {
	decoder := gob.NewDecoder(reader)
	entry := new(logrus.Entry)
	if err := decoder.Decode(entry); err != nil {
		return nil, err
	}
	return entry, nil
}

func testLogFunction(t *testing.T, level logrus.Level, expectedMessage string, logFn func(*loggerBase)) {
	buffer := bytes.NewBufferString("")

	levels := []logrus.Level{
		logrus.DebugLevel,
		logrus.InfoLevel,
		logrus.WarnLevel,
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}

	lb := &loggerBase{
		moduleLogger: &loggerModule{
			level: level,
			name:  "test_module",
			root: &loggerRoot{
				logger: &logrus.Logger{
					Out:       buffer,
					Formatter: &gobFormatter{},
					Level:     logrus.DebugLevel,
				},
				moduleField: "module",
			},
		},
	}

	// Check if logging works
	logFn(lb)
	require.NotEmpty(t, buffer.Bytes())
	entry, err := gobFormatter{}.Decode(buffer)

	require.NoError(t, err)
	require.NotNil(t, entry)

	require.EqualValues(t, expectedMessage, entry.Message)
	require.NotNil(t, entry.Data)
	require.Len(t, entry.Data, 1)
	require.Contains(t, entry.Data, "module")
	require.EqualValues(t, "test_module", entry.Data["module"])
	require.EqualValues(t, level, entry.Level)

	if level != logrus.PanicLevel {
		ignoredLevel := logrus.PanicLevel
		foundLevel := false
		for _, lvl := range levels {
			if lvl == level {
				foundLevel = true
			} else if foundLevel {
				ignoredLevel = lvl
				break
			}
		}
		// Check if we are not logging if the level is excluded
		buffer.Reset()
		lb.moduleLogger.SetLevel(ignoredLevel)
		logFn(lb)
		require.Empty(t, buffer.Bytes())
	}
}

func TestLoggerBase_Debug(t *testing.T) {
	testLogFunction(t, logrus.DebugLevel, "test", func(lb *loggerBase) {
		lb.Debug("test")
	})
}

func TestLoggerBase_Debugf(t *testing.T) {
	testLogFunction(t, logrus.DebugLevel, "test: 1", func(lb *loggerBase) {
		lb.Debugf("test: %d", 1)
	})
}

func TestLoggerBase_Debugln(t *testing.T) {
	testLogFunction(t, logrus.DebugLevel, "test", func(lb *loggerBase) {
		lb.Debugln("test")
	})
}

func TestLoggerBase_Error(t *testing.T) {
	testLogFunction(t, logrus.ErrorLevel, "test", func(lb *loggerBase) {
		lb.Error("test")
	})
}

func TestLoggerBase_Errorf(t *testing.T) {
	testLogFunction(t, logrus.ErrorLevel, "test: 1", func(lb *loggerBase) {
		lb.Errorf("test: %d", 1)
	})
}

func TestLoggerBase_Errorln(t *testing.T) {
	testLogFunction(t, logrus.ErrorLevel, "test", func(lb *loggerBase) {
		lb.Errorln("test")
	})
}

func TestLoggerBase_Info(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Info("test")
	})
}

func TestLoggerBase_Infof(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test: 1", func(lb *loggerBase) {
		lb.Infof("test: %d", 1)
	})
}

func TestLoggerBase_Infoln(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Infoln("test")
	})
}

func TestLoggerBase_Print(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Print("test")
	})
}

func TestLoggerBase_Printf(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test: 1", func(lb *loggerBase) {
		lb.Printf("test: %d", 1)
	})
}

func TestLoggerBase_Println(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Println("test")
	})
}

func TestLoggerBase_Warn(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warn("test")
	})
}

func TestLoggerBase_Warnf(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test: 1", func(lb *loggerBase) {
		lb.Warnf("test: %d", 1)
	})
}

func TestLoggerBase_Warnln(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warnln("test")
	})
}

func TestLoggerBase_Warning(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warning("test")
	})
}

func TestLoggerBase_Warningf(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test: 1", func(lb *loggerBase) {
		lb.Warningf("test: %d", 1)
	})
}

func TestLoggerBase_Warningln(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warningln("test")
	})
}

func TestLoggerBase_Panic(t *testing.T) {
	testLogFunction(t, logrus.PanicLevel, "test", func(lb *loggerBase) {
		defer func() {
			if r := recover(); r == nil {
				t.Fatal("Expected panic, but did not panic")
			}
		}()
		lb.Panic("test")
	})
}

func TestLoggerBase_Panicf(t *testing.T) {
	testLogFunction(t, logrus.PanicLevel, "test: 1", func(lb *loggerBase) {
		defer func() {
			if r := recover(); r == nil {
				t.Fatal("Expected panic, but did not panic")
			}
		}()
		lb.Panicf("test: %d", 1)
	})
}

func TestLoggerBase_Panicln(t *testing.T) {
	testLogFunction(t, logrus.PanicLevel, "test", func(lb *loggerBase) {
		defer func() {
			if r := recover(); r == nil {
				t.Fatal("Expected panic, but did not panic")
			}
		}()
		lb.Panicln("test")
	})
}
<commit_msg>Replace custom encoding/gob formatter in tests with JSON formatter<commit_after>package modular

import (
	"testing"

	"errors"

	"bytes"
	"encoding/json"

	"github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/require"
)

func TestLoggerBase_WithFields(t *testing.T) {
	// Create a new logger
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
		fields: logrus.Fields{
			"test": "test",
		},
	}

	// Create a new logger by calling WithFields
	logger := lb.WithFields(logrus.Fields{
		"test2": "test2",
	})
	require.NotNil(t, logger)

	lb2, ok := logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 2)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.Contains(t, lb2.fields, "test2")
	require.EqualValues(t, "test", lb2.fields["test"])
	require.EqualValues(t, "test2", lb2.fields["test2"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])

	// Test override of field value
	logger = lb.WithFields(logrus.Fields{
		"test": "test2",
	})
	lb2, ok = logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 1)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.EqualValues(t, "test2", lb2.fields["test"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])
}

func TestLoggerBase_WithField(t *testing.T) {
	// Create a new logger
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
		fields: logrus.Fields{
			"test": "test",
		},
	}

	logger := lb.WithField("test2", "test2")
	require.NotNil(t, logger)

	lb2, ok := logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 2)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.Contains(t, lb2.fields, "test2")
	require.EqualValues(t, "test", lb2.fields["test"])
	require.EqualValues(t, "test2", lb2.fields["test2"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])
}

func TestLoggerBase_WithError(t *testing.T) {
	// Create a new logger
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
		fields: logrus.Fields{
			"test": "test",
		},
	}

	err := errors.New("test2")

	logger := lb.WithError(err)
	require.NotNil(t, logger)

	lb2, ok := logger.(*loggerBase)
	require.EqualValues(t, true, ok)
	// Check if the two loggers are not equal
	require.NotEqual(t, lb, lb2)

	// Check if the old logger's fields have not been modified
	require.Len(t, lb.fields, 1)
	// Check if the new logger's fields are an extension of the old logger's fields
	require.Len(t, lb2.fields, 2)

	// Check field contents of new logger
	require.Contains(t, lb2.fields, "test")
	require.Contains(t, lb2.fields, "error")
	require.EqualValues(t, "test", lb2.fields["test"])
	require.EqualValues(t, err, lb2.fields["error"])

	// Check field contents of old logger
	require.Contains(t, lb.fields, "test")
	require.EqualValues(t, "test", lb.fields["test"])
}

func TestLoggerBase_GetModuleLogger(t *testing.T) {
	lb := &loggerBase{
		moduleLogger: &loggerModule{},
	}

	require.EqualValues(t, lb.moduleLogger, lb.GetModuleLogger())
}

func TestLoggerBase_NewEntry(t *testing.T) {
	lb := &loggerBase{
		moduleLogger: &loggerModule{
			level: logrus.DebugLevel,
			name:  "test_module",
			root: &loggerRoot{
				logger:      &logrus.Logger{},
				moduleField: "module",
			},
		},
	}

	levels := []logrus.Level{
		logrus.DebugLevel,
		logrus.InfoLevel,
		logrus.WarnLevel,
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}

	// Test our "base" case: debug logging enabled at module level,
	// newEntry called with debug level.
	// This must return a new entry.
	require.NotNil(t, lb.newEntry(logrus.DebugLevel))

	// Test if newEntry behaves correctly...
	for i, level := range levels {
		if i == 0 {
			// Ignore debug level above
			continue
		}
		lb.moduleLogger.SetLevel(level)
		for j := 0; j < i; j++ {
			require.Nil(t, lb.newEntry(levels[j]), "Level is %s, newEntry(%s) should return nil", level.String(), levels[j].String())
		}
		for j := i; j < len(levels); j++ {
			require.NotNil(t, lb.newEntry(levels[j]), "Level is %s, newEntry(%s) should not return nil", levels[j].String(), levels[j].String())
		}
	}

	// Test if newEntry creates a copy of fields...
	lb.moduleLogger.SetLevel(logrus.DebugLevel)
	lb.fields = logrus.Fields{
		"test":  "test",
		"test2": "test2",
	}

	entry := lb.newEntry(logrus.DebugLevel)
	require.NotNil(t, entry)
	require.Len(t, entry.Data, len(lb.fields)+1)
	require.Contains(t, entry.Data, "test")
	require.EqualValues(t, "test", entry.Data["test"])
	require.Contains(t, entry.Data, "test2")
	require.EqualValues(t, "test2", entry.Data["test2"])
	require.Contains(t, entry.Data, "module")
	require.EqualValues(t, "test_module", entry.Data["module"])
}

func testLogFunction(t *testing.T, level logrus.Level, expectedMessage string, logFn func(*loggerBase)) {
	buffer := bytes.NewBufferString("")

	levels := []logrus.Level{
		logrus.DebugLevel,
		logrus.InfoLevel,
		logrus.WarnLevel,
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}

	lb := &loggerBase{
		moduleLogger: &loggerModule{
			level: level,
			name:  "test_module",
			root: &loggerRoot{
				logger: &logrus.Logger{
					Out:       buffer,
					Formatter: &logrus.JSONFormatter{},
					Level:     logrus.DebugLevel,
				},
				moduleField: "module",
			},
		},
	}

	// Check if logging works
	logFn(lb)
	require.NotEmpty(t, buffer.Bytes())

	var data map[string]interface{}
	require.NoError(t, json.Unmarshal(buffer.Bytes(), &data))

	require.EqualValues(t, expectedMessage, data["msg"])
	require.EqualValues(t, "test_module", data["module"])
	lvl, err := logrus.ParseLevel(data["level"].(string))
	require.NoError(t, err)
	require.EqualValues(t, level, lvl)

	if level != logrus.PanicLevel {
		ignoredLevel := logrus.PanicLevel
		foundLevel := false
		for _, lvl := range levels {
			if lvl == level {
				foundLevel = true
			} else if foundLevel {
				ignoredLevel = lvl
				break
			}
		}
		// Check if we are not logging if the level is excluded
		buffer.Reset()
		lb.moduleLogger.SetLevel(ignoredLevel)
		logFn(lb)
		require.Empty(t, buffer.Bytes())
	}
}

func TestLoggerBase_Debug(t *testing.T) {
	testLogFunction(t, logrus.DebugLevel, "test", func(lb *loggerBase) {
		lb.Debug("test")
	})
}

func TestLoggerBase_Debugf(t *testing.T) {
	testLogFunction(t, logrus.DebugLevel, "test: 1", func(lb *loggerBase) {
		lb.Debugf("test: %d", 1)
	})
}

func TestLoggerBase_Debugln(t *testing.T) {
	testLogFunction(t, logrus.DebugLevel, "test", func(lb *loggerBase) {
		lb.Debugln("test")
	})
}

func TestLoggerBase_Error(t *testing.T) {
	testLogFunction(t, logrus.ErrorLevel, "test", func(lb *loggerBase) {
		lb.Error("test")
	})
}

func TestLoggerBase_Errorf(t *testing.T) {
	testLogFunction(t, logrus.ErrorLevel, "test: 1", func(lb *loggerBase) {
		lb.Errorf("test: %d", 1)
	})
}

func TestLoggerBase_Errorln(t *testing.T) {
	testLogFunction(t, logrus.ErrorLevel, "test", func(lb *loggerBase) {
		lb.Errorln("test")
	})
}

func TestLoggerBase_Info(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Info("test")
	})
}

func TestLoggerBase_Infof(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test: 1", func(lb *loggerBase) {
		lb.Infof("test: %d", 1)
	})
}

func TestLoggerBase_Infoln(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Infoln("test")
	})
}

func TestLoggerBase_Print(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Print("test")
	})
}

func TestLoggerBase_Printf(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test: 1", func(lb *loggerBase) {
		lb.Printf("test: %d", 1)
	})
}

func TestLoggerBase_Println(t *testing.T) {
	testLogFunction(t, logrus.InfoLevel, "test", func(lb *loggerBase) {
		lb.Println("test")
	})
}

func TestLoggerBase_Warn(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warn("test")
	})
}

func TestLoggerBase_Warnf(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test: 1", func(lb *loggerBase) {
		lb.Warnf("test: %d", 1)
	})
}

func TestLoggerBase_Warnln(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warnln("test")
	})
}

func TestLoggerBase_Warning(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warning("test")
	})
}

func TestLoggerBase_Warningf(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test: 1", func(lb *loggerBase) {
		lb.Warningf("test: %d", 1)
	})
}

func TestLoggerBase_Warningln(t *testing.T) {
	testLogFunction(t, logrus.WarnLevel, "test", func(lb *loggerBase) {
		lb.Warningln("test")
	})
}

func TestLoggerBase_Panic(t *testing.T) {
	testLogFunction(t, logrus.PanicLevel, "test", func(lb *loggerBase) {
		defer func() {
			if r := recover(); r == nil {
				t.Fatal("Expected panic, but did not panic")
			}
		}()
		lb.Panic("test")
	})
}

func TestLoggerBase_Panicf(t *testing.T) {
	testLogFunction(t, logrus.PanicLevel, "test: 1", func(lb *loggerBase) {
		defer func() {
			if r := recover(); r == nil {
				t.Fatal("Expected panic, but did not panic")
			}
		}()
		lb.Panicf("test: %d", 1)
	})
}

func TestLoggerBase_Panicln(t *testing.T) {
	testLogFunction(t, logrus.PanicLevel, "test", func(lb *loggerBase) {
		defer func() {
			if r := recover(); r == nil {
				t.Fatal("Expected panic, but did not panic")
			}
		}()
		lb.Panicln("test")
	})
}
<|endoftext|>