<commit_before>package beanstalk

import "time"

type finalizeJob struct {
	job      *Job
	method   JobMethod
	ret      chan error
	priority uint32
	delay    time.Duration
}

// Consumer reserves jobs from a beanstalk server and keeps those jobs alive
// until an external consumer has either buried, deleted or released it.
type Consumer struct {
	Client
	tubes       []string
	jobC        chan<- *Job
	finalizeJob chan *finalizeJob
	reserve     chan struct{}
	reservedJob chan *Job
	pause       chan bool
	stop        chan struct{}
}

// NewConsumer creates a new Consumer object.
func NewConsumer(socket string, tubes []string, jobC chan<- *Job, options *Options) *Consumer {
	consumer := &Consumer{
		Client:      NewClient(socket, options),
		tubes:       tubes,
		jobC:        jobC,
		finalizeJob: make(chan *finalizeJob),
		reserve:     make(chan struct{}),
		reservedJob: make(chan *Job),
		pause:       make(chan bool, 1),
		stop:        make(chan struct{}, 1)}

	go consumer.jobReserver()
	go consumer.jobManager()

	return consumer
}

// Stop tells the jobManager() goroutine to stop running.
func (consumer *Consumer) Stop() {
	consumer.stop <- struct{}{}
}

// Play makes this consumer reserve jobs.
func (consumer *Consumer) Play() {
	consumer.pause <- false
}

// Pause stops this consumer from reserving jobs.
func (consumer *Consumer) Pause() {
	consumer.pause <- true
}

// FinalizeJob is an interface function for Job that gets called whenever it is
// decided to finalize the job by either burying, deleting or releasing it.
func (consumer *Consumer) FinalizeJob(job *Job, method JobMethod, priority uint32, delay time.Duration) error {
	fJob := &finalizeJob{job: job, method: method, ret: make(chan error), priority: priority, delay: delay}
	consumer.finalizeJob <- fJob
	return <-fJob.ret
}

// jobReserver simply reserves jobs.
func (consumer *Consumer) jobReserver() {
	for {
		select {
		case _, ok := <-consumer.reserve:
			if !ok {
				close(consumer.reservedJob)
				return
			}

			job, _ := consumer.Reserve()
			consumer.reservedJob <- job
		}
	}
}

// jobManager is responsible for maintaining a connection to the beanstalk
// server, reserving jobs, keeping them reserved and finalizing them.
func (consumer *Consumer) jobManager() {
	var job *Job
	var jobC chan<- *Job
	var paused, requested, offered, ok = true, false, false, true

	consumer.OpenConnection()
	defer consumer.CloseConnection()

	// This timer is used to keep a reserved job alive.
	touchTimer := time.NewTimer(time.Second)
	touchTimer.Stop()

	// reserveJob fetches a new job if the state allows for it.
	reserveJob := func() {
		if !requested && !paused && consumer.isConnected && job == nil {
			consumer.reserve <- struct{}{}
			requested = true
		}
	}

	// releaseJob releases a job back to beanstalk when it hasn't already been
	// offered up.
	releaseJob := func() {
		if job != nil && !offered {
			consumer.Release(job, job.Priority, 0)
			job, jobC = nil, nil
			touchTimer.Stop()
		}
	}

	for {
		select {
		// Wait for a new reserved job.
		case job, ok = <-consumer.reservedJob:
			// If this channel closes, exit this goroutine.
			if !ok {
				return
			}

			requested, offered = false, false

			// If no job could be reserved, try again.
			if job == nil {
				reserveJob()
				break
			}

			// If this consumer was paused in the meantime, release the job.
			if paused {
				releaseJob()
				break
			}

			jobC, job.Manager = consumer.jobC, consumer
			touchTimer.Reset(job.TTR)

		// Offer up the reserved job.
		case jobC <- job:
			jobC, offered = nil, true

		// Keep the job reserved by regularly touching it.
		case <-touchTimer.C:
			if err := consumer.Touch(job); err != nil {
				job, jobC = nil, nil
				break
			}

			touchTimer.Reset(job.TTR)

		// Finalize a job, which means either bury, delete or release it.
		case req := <-consumer.finalizeJob:
			// This can happen if a disconnect occured before a job was finalized.
			if req.job != job {
				req.ret <- ErrNotFound
				break
			}

			switch req.method {
			case BuryJob:
				req.ret <- consumer.Bury(req.job, req.priority)
			case DeleteJob:
				req.ret <- consumer.Delete(req.job)
			case ReleaseJob:
				req.ret <- consumer.Release(req.job, req.priority, req.delay)
			}

			job = nil
			reserveJob()
			touchTimer.Stop()

		// Set up a new connection.
		case conn := <-consumer.connCreatedC:
			consumer.SetConnection(conn)

			for _, tube := range consumer.tubes {
				consumer.Watch(tube)
			}

			// Ignore the 'default' tube if it wasn't in the list of tubes to watch.
			if !includesString(consumer.tubes, "default") {
				consumer.Ignore("default")
			}

			reserveJob()

		// The connection was closed, so any reserved jobs are now useless.
		case <-consumer.connClosedC:
			job, jobC = nil, nil
			touchTimer.Stop()

		// Play or pause this consumer.
		case paused = <-consumer.pause:
			if paused {
				releaseJob()
			} else {
				reserveJob()
			}

		// Closing the reserve channel tells jobReserver() to stop running.
		case <-consumer.stop:
			releaseJob()
			paused = true
			close(consumer.reserve)
		}
	}
}
<commit_msg>Rewrite Consumer{} a bit so the Play(), Pause() and Stop() functions won't ever block; Also, simplify the main goroutine to do less state tracking<commit_after>package beanstalk

import "time"

// Consumer reserves jobs from a beanstalk server and keeps those jobs alive
// until an external consumer has either buried, deleted or released it.
type Consumer struct {
	Client
	tubes           []string
	jobC            chan<- *Job
	finalizeJob     chan *finalizeJob
	pause           chan bool
	pauseJobManager chan bool
	stop            chan struct{}
	stopJobManager  chan struct{}
}

// NewConsumer returns a new Consumer object.
func NewConsumer(socket string, tubes []string, jobC chan<- *Job, options *Options) *Consumer {
	consumer := &Consumer{
		Client:          NewClient(socket, options),
		tubes:           tubes,
		jobC:            jobC,
		finalizeJob:     make(chan *finalizeJob),
		pause:           make(chan bool, 1),
		pauseJobManager: make(chan bool),
		stop:            make(chan struct{}, 1),
		stopJobManager:  make(chan struct{}),
	}

	go consumer.controlManager()
	go consumer.jobManager()

	return consumer
}

// Play makes this consumer reserve jobs.
func (consumer *Consumer) Play() {
	consumer.pause <- false
}

// Pause stops this consumer from reserving jobs.
func (consumer *Consumer) Pause() {
	consumer.pause <- true
}

// Stop this consumer from running.
func (consumer *Consumer) Stop() {
	consumer.stop <- struct{}{}
}

// controlManager deals with the state changes issued from the Play(), Pause()
// and Stop() functions.
func (consumer *Consumer) controlManager() {
	var paused bool
	var pauseJobManager chan bool
	var stopJobManager chan struct{}

	for {
		select {
		case paused = <-consumer.pause:
			pauseJobManager = consumer.pauseJobManager
		case <-consumer.stop:
			stopJobManager = consumer.stopJobManager

		case pauseJobManager <- paused:
			pauseJobManager = nil
		case stopJobManager <- struct{}{}:
			return
		}
	}
}

type finalizeJob struct {
	job      *Job
	method   JobMethod
	ret      chan error
	priority uint32
	delay    time.Duration
}

// FinalizeJob is an interface function for Job that gets called whenever it is
// decided to finalize the job by either burying, deleting or releasing it.
func (consumer *Consumer) FinalizeJob(job *Job, method JobMethod, priority uint32, delay time.Duration) error {
	fJob := &finalizeJob{job: job, method: method, ret: make(chan error), priority: priority, delay: delay}
	consumer.finalizeJob <- fJob
	return <-fJob.ret
}

// jobManager is responsible for reserving beanstalk jobs and keeping those
// jobs reserved until they're either buried, deleted or released.
func (consumer *Consumer) jobManager() {
	var job *Job
	var jobC chan<- *Job
	var paused = true

	consumer.OpenConnection()
	defer consumer.CloseConnection()

	// This channel is used as a programmable way to make the select-statement
	// below non-blocking in similar way a default-case would.
	dontWait := make(chan struct{}, 1)

	// This timer is used to keep a reserved job alive.
	touchTimer := time.NewTimer(time.Second)
	touchTimer.Stop()

	// releaseJob releases a job back to beanstalk.
	releaseJob := func() {
		consumer.Release(job, job.Priority, 0)
		job, jobC = nil, nil
		touchTimer.Stop()
	}

	for {
		// Reserve a new job, if the state allows for it.
		if consumer.isConnected && !paused && job == nil {
			if job, _ = consumer.Reserve(); job != nil {
				jobC, job.Manager = consumer.jobC, consumer
				touchTimer.Reset(job.TTR)
			} else if len(dontWait) == 0 {
				dontWait <- struct{}{}
			}
		}

		select {
		// If a job was reserved, offer it up.
		case jobC <- job:
			jobC = nil

		// Touch the reserved job at a regular interval to keep it reserved.
		case <-touchTimer.C:
			if err := consumer.Touch(job); err != nil {
				job, jobC = nil, nil
				break
			}

			touchTimer.Reset(job.TTR)

		// Bury, delete or release the reserved job.
		case req := <-consumer.finalizeJob:
			// This can happen if a disconnect occured before a job was finalized.
			if req.job != job {
				req.ret <- ErrNotFound
				break
			}

			switch req.method {
			case BuryJob:
				req.ret <- consumer.Bury(req.job, req.priority)
			case DeleteJob:
				req.ret <- consumer.Delete(req.job)
			case ReleaseJob:
				req.ret <- consumer.Release(req.job, req.priority, req.delay)
			}

			job = nil
			touchTimer.Stop()

		// Set up a new connection.
		case conn := <-consumer.connCreatedC:
			consumer.SetConnection(conn)
			for _, tube := range consumer.tubes {
				consumer.Watch(tube)
			}

			// Ignore the 'default' tube if it wasn't in the list of tubes to watch.
			if !includesString(consumer.tubes, "default") {
				consumer.Ignore("default")
			}

		// The connection was closed, so any reserved jobs are now useless.
		case <-consumer.connClosedC:
			job, jobC = nil, nil
			touchTimer.Stop()

		// Pause or unpause reservering new jobs.
		case paused = <-consumer.pauseJobManager:
			// If this job wasn't offered yet, quickly release it.
			if paused && job != nil && jobC != nil {
				releaseJob()
			}

		// Stop this goroutine. Release the job, if one is pending.
		case <-consumer.stopJobManager:
			if job != nil {
				releaseJob()
			}
			return

		// Don't let this select statement block when a job can be reserved.
		case <-dontWait:
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	jseg "github.com/garyhouston/jpegsegs"
	"log"
	"os"
)

// Print the JPEG markers and segment lengths, up to SOS.
func main() {
	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s file\n", os.Args[0])
		return
	}
	in, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer in.Close()
	reader := bufio.NewReader(in)
	scanner, err := jseg.NewScanner(reader)
	if err != nil {
		log.Fatal(err)
	}
	for {
		marker, buf, err := scanner.Scan()
		if err != nil {
			log.Fatal(err)
		}
		if marker == jseg.SOS {
			fmt.Println(marker.Name())
			break
		}
		fmt.Printf("%s, %d bytes\n", marker.Name(), len(buf))
	}
	buf := make([]byte, 10000)
	reset := 0
	total := 0
	for {
		var marker jseg.Marker
		var err error
		buf, marker, err = jseg.ReadImageData(reader, buf)
		if err != nil {
			log.Fatal(err)
		}
		total += len(buf)
		if marker >= jseg.RST0 && marker <= jseg.RST0+7 {
			reset++
		} else if marker == jseg.EOI {
			fmt.Printf("%d bytes of scan data and %d reset markers\n", total, reset)
			fmt.Println(marker.Name())
			break
		} else {
			fmt.Printf("%s, unexpected marker\n", marker.Name())
		}
	}
}
<commit_msg>Indicate if APP2 segment is MPF header<commit_after>package main

import (
	"bufio"
	"fmt"
	jseg "github.com/garyhouston/jpegsegs"
	"log"
	"os"
)

// Print the JPEG markers and segment lengths, up to SOS.
func main() {
	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s file\n", os.Args[0])
		return
	}
	in, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer in.Close()
	reader := bufio.NewReader(in)
	scanner, err := jseg.NewScanner(reader)
	if err != nil {
		log.Fatal(err)
	}
	for {
		marker, buf, err := scanner.Scan()
		if err != nil {
			log.Fatal(err)
		}
		if marker == jseg.SOS {
			fmt.Println(marker.Name())
			break
		}
		if marker == jseg.APP0+2 {
			isMPF, _ := jseg.GetMPFHeader(buf)
			if isMPF {
				fmt.Printf("%s, %d bytes (MPF segment)\n", marker.Name(), len(buf))
				continue
			}
		}
		fmt.Printf("%s, %d bytes\n", marker.Name(), len(buf))
	}
	buf := make([]byte, 10000)
	reset := 0
	total := 0
	for {
		var marker jseg.Marker
		var err error
		buf, marker, err = jseg.ReadImageData(reader, buf)
		if err != nil {
			log.Fatal(err)
		}
		total += len(buf)
		if marker >= jseg.RST0 && marker <= jseg.RST0+7 {
			reset++
		} else if marker == jseg.EOI {
			fmt.Printf("%d bytes of scan data and %d reset markers\n", total, reset)
			fmt.Println(marker.Name())
			break
		} else {
			fmt.Printf("%s, unexpected marker\n", marker.Name())
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 John Kelley. All rights reserved.
// This source code is licensed under the Simplified BSD License

// Woozle is a simple DNS recursor with the ability to filter out
// certain queries. The author finds this useful for forcing Youtube
// over IPv4 instead of his Hurricane Electric IPv6 tunnel

package main

import (
	"fmt"
	"github.com/miekg/dns"
	"os"
	"os/signal"
	"syscall"
)

const upstreamDNS = "10.10.10.1:53"
var filterDomainAAAA = []string{ "youtube.com.", "googlevideo.com." }

func serve(net string) {
	server := &dns.Server{Addr: ":53", Net: net, TsigSecret: nil}
	err := server.ListenAndServe()
	if err != nil {
		fmt.Printf("Failed to setup the "+net+" server: %s\n", err.Error())
		os.Exit(1)
	}
}

func handleRecurse(w dns.ResponseWriter, m *dns.Msg) {
	fmt.Printf("Recursing for %s %s\n", m.Question[0].Name, dns.TypeToString[m.Question[0].Qtype])
	c := new(dns.Client)
	r, _, e := c.Exchange(m, upstreamDNS)
	if e != nil {
		fmt.Printf("Client query failed: %s\n", e.Error())
	} else {
		w.WriteMsg(r)
	}
}

func filterAAAA(w dns.ResponseWriter, r *dns.Msg) {
	if r.Question[0].Qtype == dns.TypeAAAA {
		// send a blank reply
		m := new(dns.Msg)
		m.SetReply(r)
		w.WriteMsg(m)
		fmt.Printf("Filtering AAAA query for %s\n", m.Question[0].Name)
	} else {
		handleRecurse(w, r)
	}
}

func main() {
	// handler for filtering ipv6 AAAA records
	for _, domain := range filterDomainAAAA {
		dns.HandleFunc(domain, filterAAAA)
	}

	// default handler
	dns.HandleFunc(".", handleRecurse)
//	go serve("tcp")
	go serve("udp")

	// handle signals
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
forever:
	for {
		select {
		case s := <-sig:
			fmt.Printf("\nSignal (%d) received, stopping\n", s)
			break forever
		}
	}
}
<commit_msg>Simplify signal handling<commit_after>// Copyright 2015 John Kelley. All rights reserved.
// This source code is licensed under the Simplified BSD License

// Woozle is a simple DNS recursor with the ability to filter out
// certain queries. The author finds this useful for forcing Youtube
// over IPv4 instead of his Hurricane Electric IPv6 tunnel

package main

import (
	"fmt"
	"github.com/miekg/dns"
	"os"
	"os/signal"
	"syscall"
)

const upstreamDNS = "10.10.10.1:53"
var filterDomainAAAA = []string{ "youtube.com.", "googlevideo.com." }

func serve(net string) {
	server := &dns.Server{Addr: ":53", Net: net, TsigSecret: nil}
	err := server.ListenAndServe()
	if err != nil {
		fmt.Printf("Failed to setup the "+net+" server: %s\n", err.Error())
		os.Exit(1)
	}
}

func handleRecurse(w dns.ResponseWriter, m *dns.Msg) {
	fmt.Printf("Recursing for %s %s\n", m.Question[0].Name, dns.TypeToString[m.Question[0].Qtype])
	c := new(dns.Client)
	r, _, e := c.Exchange(m, upstreamDNS)
	if e != nil {
		fmt.Printf("Client query failed: %s\n", e.Error())
	} else {
		w.WriteMsg(r)
	}
}

func filterAAAA(w dns.ResponseWriter, r *dns.Msg) {
	if r.Question[0].Qtype == dns.TypeAAAA {
		// send a blank reply
		m := new(dns.Msg)
		m.SetReply(r)
		w.WriteMsg(m)
		fmt.Printf("Filtering AAAA query for %s\n", m.Question[0].Name)
	} else {
		handleRecurse(w, r)
	}
}

func main() {
	// handler for filtering ipv6 AAAA records
	for _, domain := range filterDomainAAAA {
		dns.HandleFunc(domain, filterAAAA)
	}

	// default handler
	dns.HandleFunc(".", handleRecurse)
//	go serve("tcp")
	go serve("udp")

	// handle signals
	sig := make(chan os.Signal)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1, syscall.SIGUSR2)
forever:
	for s := range sig {
		if s == syscall.SIGUSR1 {
		} else {
			fmt.Printf("\nSignal (%d) received, stopping\n", s)
			break forever
		}
	}
}
<|endoftext|>


<commit_before>
package packer2terraform

import (
    "bytes"
    "encoding/csv"
    "errors"
    "fmt"
    "io"
    "strconv"
    "strings"
    "text/template"
)

type LogLine struct {
    time         string
    builderType  string
    lineType     string
    messageType  string
    messageTypeI int
    messageA     string
    messageB     string
}

type Artifact struct {
    BuilderType string
    BuilderId   string
    Id          string
    IdSplit     []string
    Message     string
    FilesCount  string
}

type TemplatePage struct {
    Artifacts []Artifact
}

var TemplateAmazonEBS = `variable "images" {
    default = {
{{range .Artifacts}}
        {{index .IdSplit 0}} = "{{index .IdSplit 1}}"{{end}}
    }
}`;

func ReadCSV(csvReader io.Reader) (ret [][]string, err error) {
    reader := csv.NewReader(csvReader)
    reader.FieldsPerRecord = -1
    reader.LazyQuotes = true
    return reader.ReadAll()
}

func Filter(parsed [][]string) (artifacts []Artifact, err error) {
    var errorCount int
    var errorMsg []string
    var artifactCount int

    for _, v := range parsed {
        // Build a LogLine
        line := LogLine{"", "", "", "", 0, "", ""}
        if len(v) > 0 {
            line.time = v[0]
        }
        if len(v) > 1 {
            line.builderType = v[1]
        }
        if len(v) > 2 {
            line.lineType = v[2]
        }
        if len(v) > 3 {
            line.messageType = v[3]
        }
        if len(v) > 4 {
            line.messageA = v[4]
        }
        if len(v) > 5 {
            line.messageB = v[5]
        }
        if len(line.messageType) > 0 {
            line.messageTypeI, _ = strconv.Atoi(line.messageType)
        }

        // Artifacts:
        if line.lineType == "artifact-count" {
            artifactCount = line.messageTypeI
        }
        if line.lineType == "artifact" {

            if len(artifacts) < line.messageTypeI+1 {
                a := Artifact{}
                a.BuilderType = line.builderType
                artifacts = append(artifacts, a)
            }

            a := &artifacts[line.messageTypeI]
            if line.messageA == "id" {
                a.Id = line.messageB
                a.IdSplit = strings.Split(line.messageB, ":")
            }
            if line.messageA == "files-count" {
                a.FilesCount = line.messageB
            }
            if line.messageA == "builder-id" {
                a.BuilderId = line.messageB
            }
            if line.messageA == "string" {
                a.Message = line.messageB
            }
        }

        // Errors:
        if line.lineType == "error-count" && line.messageTypeI > 0 {
            errorCount = line.messageTypeI
        }
        if line.lineType == "error" {
            errorMsg = append(errorMsg, line.messageType)
        }
    }

    if artifactCount < len(artifacts) {
        artifactsMissing := artifactCount - len(artifacts)
        return nil, errors.New(fmt.Sprintf("Missing %s artifacts.", artifactsMissing))
    }

    if errorCount > 0 && len(errorMsg) > 0 {
        return nil, errors.New(strings.Join(errorMsg, "\n"))
    }

    // Clean up empty artifacts
    for i, artifact := range artifacts {
        if artifact.Id == "" {
            artifacts = append(artifacts[:i], artifacts[i+1:]...)
        }
    }
    if len(artifacts) == 0 {
        return nil, errors.New("No Artifacts found.")
    }

    return artifacts, nil
}

func ToTemplate(artifacts []Artifact, tmpl string) (ret string, err error) {
    // Setup the page vars
    var thePage = TemplatePage{}
    thePage.Artifacts = artifacts

    t := template.Must(template.New("tmpl").Parse(tmpl))

    var doc bytes.Buffer
    t.Execute(&doc, thePage)
    ret = doc.String()

    return ret, nil
}
<commit_msg>Better match Packer's field definitions<commit_after>
package packer2terraform

import (
    "bytes"
    "encoding/csv"
    "errors"
    "fmt"
    "io"
    "strconv"
    "strings"
    "text/template"
)

type LogLine struct {
    timestamp     string
    builderTarget string
    lineType      string
    messageType   string
    messageTypeI  int
    messageA      string
    messageB      string
}

type Artifact struct {
    BuilderTarget string
    BuilderId     string
    Id            string
    IdSplit       []string
    Message       string
    FilesCount    string
}

type TemplatePage struct {
    Artifacts []Artifact
}

var TemplateAmazonEBS = `variable "images" {
    default = {
{{range .Artifacts}}
        {{index .IdSplit 0}} = "{{index .IdSplit 1}}"{{end}}
    }
}`;

func ReadCSV(csvReader io.Reader) (ret [][]string, err error) {
    reader := csv.NewReader(csvReader)
    reader.FieldsPerRecord = -1
    reader.LazyQuotes = true
    return reader.ReadAll()
}

func Filter(parsed [][]string) (artifacts []Artifact, err error) {
    var errorCount int
    var errorMsg []string
    var artifactCount int

    for _, v := range parsed {
        // Build a LogLine
        line := LogLine{"", "", "", "", 0, "", ""}
        if len(v) > 0 {
            line.timestamp = v[0]
        }
        if len(v) > 1 {
            line.builderTarget = v[1]
        }
        if len(v) > 2 {
            line.lineType = v[2]
        }
        if len(v) > 3 {
            line.messageType = v[3]
        }
        if len(v) > 4 {
            line.messageA = v[4]
        }
        if len(v) > 5 {
            line.messageB = v[5]
        }
        if len(line.messageType) > 0 {
            line.messageTypeI, _ = strconv.Atoi(line.messageType)
        }

        // Artifacts:
        if line.lineType == "artifact-count" {
            artifactCount = line.messageTypeI
        }
        if line.lineType == "artifact" {

            if len(artifacts) < line.messageTypeI+1 {
                a := Artifact{}
                a.BuilderTarget = line.builderTarget
                artifacts = append(artifacts, a)
            }

            a := &artifacts[line.messageTypeI]
            if line.messageA == "id" {
                a.Id = line.messageB
                a.IdSplit = strings.Split(line.messageB, ":")
            }
            if line.messageA == "files-count" {
                a.FilesCount = line.messageB
            }
            if line.messageA == "builder-id" {
                a.BuilderId = line.messageB
            }
            if line.messageA == "string" {
                a.Message = line.messageB
            }
        }

        // Errors:
        if line.lineType == "error-count" && line.messageTypeI > 0 {
            errorCount = line.messageTypeI
        }
        if line.lineType == "error" {
            errorMsg = append(errorMsg, line.messageType)
        }
    }

    if artifactCount < len(artifacts) {
        artifactsMissing := artifactCount - len(artifacts)
        return nil, errors.New(fmt.Sprintf("Missing %s artifacts.", artifactsMissing))
    }

    if errorCount > 0 && len(errorMsg) > 0 {
        return nil, errors.New(strings.Join(errorMsg, "\n"))
    }

    // Clean up empty artifacts
    for i, artifact := range artifacts {
        if artifact.Id == "" {
            artifacts = append(artifacts[:i], artifacts[i+1:]...)
        }
    }
    if len(artifacts) == 0 {
        return nil, errors.New("No Artifacts found.")
    }

    return artifacts, nil
}

func ToTemplate(artifacts []Artifact, tmpl string) (ret string, err error) {
    // Setup the page vars
    var thePage = TemplatePage{}
    thePage.Artifacts = artifacts

    t := template.Must(template.New("tmpl").Parse(tmpl))

    var doc bytes.Buffer
    t.Execute(&doc, thePage)
    ret = doc.String()

    return ret, nil
}
<|endoftext|>


<commit_before>package hwaflib

func (ctx *Context) Version() string {
	version := "20131015"
	return version
}

func (ctx *Context) Revision() string {
	revision := "5edbc66"
	return revision
}

// EOF

<commit_msg>version: 20131017<commit_after>package hwaflib

func (ctx *Context) Version() string {
	version := "20131017"
	return version
}

func (ctx *Context) Revision() string {
	revision := "e26f0da"
	return revision
}

// EOF

<|endoftext|>


<commit_before>package hive

import (
	"bufio"
	"errors"
	// "fmt"
	"github.com/eaciit/errorlib"
	"io"
	"log"
	"os/exec"
	"strings"
)

const (
	BEE_CLI_STR  = "0: jdbc:hive2:"
	CLOSE_SCRIPT = "!quit"
)

type DuplexTerm struct {
	Writer     *bufio.Writer
	Reader     *bufio.Reader
	Cmd        *exec.Cmd
	CmdStr     string
	Stdin      io.WriteCloser
	Stdout     io.ReadCloser
	FnReceive  FnHiveReceive
	OutputType string
	DateFormat string
}

/*func (d *DuplexTerm) Open() (e error) {
	if d.Stdin, e = d.Cmd.StdinPipe(); e != nil {
		return
	}

	if d.Stdout, e = d.Cmd.StdoutPipe(); e != nil {
		return
	}

	d.Writer = bufio.NewWriter(d.Stdin)
	d.Reader = bufio.NewReader(d.Stdout)

	e = d.Cmd.Start()
	return
}*/

var hr *HiveResult

func (d *DuplexTerm) Open() (e error) {
	if d.CmdStr != "" {
		arg := append([]string{"-c"}, d.CmdStr)
		d.Cmd = exec.Command("sh", arg...)

		if d.Stdin, e = d.Cmd.StdinPipe(); e != nil {
			return
		}

		if d.Stdout, e = d.Cmd.StdoutPipe(); e != nil {
			return
		}

		d.Writer = bufio.NewWriter(d.Stdin)
		d.Reader = bufio.NewReader(d.Stdout)

		if d.FnReceive != nil {
			go func() {
				_, e = d.Wait()
			}()
		}
		e = d.Cmd.Start()
	} else {
		errorlib.Error("", "", "Open", "The Connection Config not Set")
	}

	return
}

func (d *DuplexTerm) Close() {
	result, e := d.SendInput(CLOSE_SCRIPT)

	_ = result
	_ = e

	d.Cmd.Wait()
	d.Stdin.Close()
	d.Stdout.Close()
}

func (d *DuplexTerm) SendInput(input string) (result []string, e error) {
	iwrite, e := d.Writer.WriteString(input + "\n")
	if iwrite == 0 {
		e = errors.New("Writing only 0 byte")
	} else {
		e = d.Writer.Flush()
	}

	if e != nil {
		return
	}

	if d.FnReceive == nil {
		result, e = d.Wait()
	}

	return
}

func (d *DuplexTerm) Wait() (result []string, e error) {
	for {
		peekBefore, _ := d.Reader.Peek(14)
		peekBeforeStr := string(peekBefore)
		// log.Printf("peekBefore: %v\n", peekBefore)
		// log.Printf("peekBeforeStr: %v\n", peekBeforeStr)

		bread, e := d.Reader.ReadString('\n')
		bread = strings.TrimRight(bread, "\n")

		peek, _ := d.Reader.Peek(14)
		peekStr := string(peek)
		// log.Printf("peek: %v\n", peek)
		// log.Printf("peekStr: %v\n", peekStr)

		delimiter := "\t"

		if d.OutputType == CSV {
			delimiter = ","
		}

		if BEE_CLI_STR == peekBeforeStr {
			hr := HiveResult{}
			hr.constructHeader(bread, delimiter)
			log.Printf("model: %v\n", hr)
			log.Printf("headerStr: %v\n", bread)
			for _, val := range hr.Header {
				log.Printf("header: %v\n", val)
			}
		}

		if !strings.Contains(bread, BEE_CLI_STR) {
			//result = append(result, bread)
			if d.FnReceive != nil {
				/*Parse(hr.Header, bread, hr.ResultObj, d.OutputType, d.DateFormat)
				log.Printf("model: %v\n", hr.ResultObj)*/
				d.FnReceive(bread)
			} else {
				result = append(result, bread)
			}
		}

		if d.FnReceive != nil {
			if (e != nil && e.Error() == "EOF") || (strings.Contains(peekStr, CLOSE_SCRIPT)) {
				break
			}
		} else {
			if (e != nil && e.Error() == "EOF") || (BEE_CLI_STR == peekStr) {
				break
			}
		}

	}

	return
}
<commit_msg>bug fixing for exec<commit_after>package hive

import (
	"bufio"
	"errors"
	// "fmt"
	"github.com/eaciit/errorlib"
	"io"
	"log"
	"os/exec"
	"strings"
)

const (
	BEE_CLI_STR  = "0: jdbc:hive2:"
	CLOSE_SCRIPT = "!quit"
)

type DuplexTerm struct {
	Writer     *bufio.Writer
	Reader     *bufio.Reader
	Cmd        *exec.Cmd
	CmdStr     string
	Stdin      io.WriteCloser
	Stdout     io.ReadCloser
	FnReceive  FnHiveReceive
	OutputType string
	DateFormat string
}

/*func (d *DuplexTerm) Open() (e error) {
	if d.Stdin, e = d.Cmd.StdinPipe(); e != nil {
		return
	}

	if d.Stdout, e = d.Cmd.StdoutPipe(); e != nil {
		return
	}

	d.Writer = bufio.NewWriter(d.Stdin)
	d.Reader = bufio.NewReader(d.Stdout)

	e = d.Cmd.Start()
	return
}*/

var hr *HiveResult

func (d *DuplexTerm) Open() (e error) {
	if d.CmdStr != "" {
		arg := append([]string{"-c"}, d.CmdStr)
		d.Cmd = exec.Command("sh", arg...)

		if d.Stdin, e = d.Cmd.StdinPipe(); e != nil {
			return
		}

		if d.Stdout, e = d.Cmd.StdoutPipe(); e != nil {
			return
		}

		d.Writer = bufio.NewWriter(d.Stdin)
		d.Reader = bufio.NewReader(d.Stdout)

		if d.FnReceive != nil {
			go func() {
				_, e = d.Wait()
			}()
		}
		e = d.Cmd.Start()
	} else {
		errorlib.Error("", "", "Open", "The Connection Config not Set")
	}

	return
}

func (d *DuplexTerm) Close() {
	result, e := d.SendInput(CLOSE_SCRIPT)

	_ = result
	_ = e

	d.Cmd.Wait()
	d.Stdin.Close()
	d.Stdout.Close()
}

func (d *DuplexTerm) SendInput(input string) (result []string, e error) {
	iwrite, e := d.Writer.WriteString(input + "\n")
	if iwrite == 0 {
		e = errors.New("Writing only 0 byte")
	} else {
		e = d.Writer.Flush()
	}

	if e != nil {
		return
	}

	if d.FnReceive == nil {
		result, e = d.Wait()
	}

	return
}

func (d *DuplexTerm) Wait() (result []string, e error) {
	isHeader := false
	for {
		peekBefore, _ := d.Reader.Peek(14)
		peekBeforeStr := string(peekBefore)
		// log.Printf("peekBefore: %v\n", peekBefore)
		// log.Printf("peekBeforeStr: %v\n", peekBeforeStr)

		bread, e := d.Reader.ReadString('\n')
		bread = strings.TrimRight(bread, "\n")

		peek, _ := d.Reader.Peek(14)
		peekStr := string(peek)
		// log.Printf("peek: %v\n", peek)
		// log.Printf("peekStr: %v\n", peekStr)

		delimiter := "\t"

		if d.OutputType == CSV {
			delimiter = ","
		}

		if BEE_CLI_STR == peekBeforeStr {
			isHeader = true
		}

		if isHeader {
			hr := HiveResult{}
			hr.constructHeader(bread, delimiter)
			log.Printf("model: %v\n", hr)
			log.Printf("headerStr: %v\n", bread)
			for _, val := range hr.Header {
				log.Printf("header: %v\n", val)
			}

			isHeader = false
		}

		if !strings.Contains(bread, BEE_CLI_STR) {
			//result = append(result, bread)
			if d.FnReceive != nil {
				/*Parse(hr.Header, bread, hr.ResultObj, d.OutputType, d.DateFormat)
				log.Printf("model: %v\n", hr.ResultObj)*/
				d.FnReceive(bread)
			} else {
				result = append(result, bread)
			}
		}

		if d.FnReceive != nil {
			if (e != nil && e.Error() == "EOF") || (strings.Contains(peekStr, CLOSE_SCRIPT)) {
				break
			}
		} else {
			if (e != nil && e.Error() == "EOF") || (BEE_CLI_STR == peekStr) {
				break
			}
		}

	}

	return
}
<|endoftext|>


<commit_before>package v1

import (
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/managedfields"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
	"k8s.io/client-go/discovery"
	"k8s.io/kube-openapi/pkg/util/proto"
	"sigs.k8s.io/structured-merge-diff/v4/typed"
)

// openAPISchemaTTL is how frequently we need to check
// whether the open API schema has changed or not.
const openAPISchemaTTL = time.Minute

// UnstructuredExtractor enables extracting the applied configuration state from object for fieldManager into an
// unstructured object type.
type UnstructuredExtractor interface {
	Extract(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error)
	ExtractStatus(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error)
}

// gvkParserCache caches the GVKParser in order to prevent from having to repeatedly
// parse the models from the open API schema when the schema itself changes infrequently.
type gvkParserCache struct {
	// discoveryClient is the client for retrieving the openAPI document and checking
	// whether the document has changed recently
	discoveryClient discovery.DiscoveryInterface
	// ttl is how long the openAPI schema should be considered valid
	ttl time.Duration
	// mu protects the gvkParser
	mu sync.Mutex
	// gvkParser retrieves the objectType for a given gvk
	gvkParser *fieldmanager.GvkParser
	// lastChecked is the last time we checked if the openAPI doc has changed.
	lastChecked time.Time
}

// regenerateGVKParser builds the parser from the raw OpenAPI schema.
func regenerateGVKParser(dc discovery.DiscoveryInterface) (*fieldmanager.GvkParser, error) {
	doc, err := dc.OpenAPISchema()
	if err != nil {
		return nil, err
	}
	//c.lastChecked = time.Now()
	models, err := proto.NewOpenAPIData(doc)
	if err != nil {
		return nil, err
	}

	return fieldmanager.NewGVKParser(models, false)
	//gvkParser, err := fieldmanager.NewGVKParser(models, false)
	//if err != nil {
	//	return nil, err
	//}

	//return gvkParser, nil
	//return nil
}

// objectTypeForGVK retrieves the typed.ParseableType for a given gvk from the cache
func (c *gvkParserCache) objectTypeForGVK(gvk schema.GroupVersionKind) (*typed.ParseableType, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	// if the ttl on the openAPISchema has expired,
	// recheck the discovery client to see if the Open API schema has changed
	if time.Now().After(c.lastChecked.Add(openAPISchemaTTL)) {
		c.lastChecked = time.Now()
		if c.discoveryClient.HasOpenAPISchemaChanged() {
			// the schema has changed, regenerate the parser
			parser, err := regenerateGVKParser(c.discoveryClient)
			if err != nil {
				return nil, err
			}
			c.gvkParser = parser
		}
	}
	return c.gvkParser.Type(gvk), nil
}

type extractor struct {
	cache *gvkParserCache
}

// NewUnstructuredExtractor creates the extractor with which you can extract the applied configuration
// for a given manager from an unstructured object.
func NewUnstructuredExtractor(dc discovery.DiscoveryInterface) (UnstructuredExtractor, error) {
	// TODO: expose ttl as an argument if we want to.

	parser, err := regenerateGVKParser(dc)
	if err != nil {
		return nil, err
	}
	return &extractor{
		cache: &gvkParserCache{
			gvkParser:       parser,
			discoveryClient: dc,
		},
	}, nil
}

// Extract extracts the applied configuration owned by fiieldManager from an unstructured object.
// Note that the apply configuration itself is also an unstructured object.
func (e *extractor) Extract(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error) {
	return e.extractUnstructured(object, fieldManager, "")
}

// ExtractStatus is the same as ExtractUnstructured except
// that it extracts the status subresource applied configuration.
// Experimental!
func (e *extractor) ExtractStatus(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error) {
	return e.extractUnstructured(object, fieldManager, "status")
}

func (e *extractor) extractUnstructured(object *unstructured.Unstructured, fieldManager string, subresource string) (*unstructured.Unstructured, error) {
	gvk := object.GetObjectKind().GroupVersionKind()
	objectType, err := e.cache.objectTypeForGVK(gvk)
	if err != nil {
		return nil, err
	}
	result := &unstructured.Unstructured{}
	err = managedfields.ExtractInto(object, *objectType, fieldManager, result, subresource)
	if err != nil {
		return nil, err
	}
	result.SetName(object.GetName())
	result.SetNamespace(object.GetNamespace())
	result.SetKind(object.GetKind())
	result.SetAPIVersion(object.GetAPIVersion())
	return result, nil
}
<commit_msg>remove commented out code<commit_after>package v1

import (
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/managedfields"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
	"k8s.io/client-go/discovery"
	"k8s.io/kube-openapi/pkg/util/proto"
	"sigs.k8s.io/structured-merge-diff/v4/typed"
)

// openAPISchemaTTL is how frequently we need to check
// whether the open API schema has changed or not.
const openAPISchemaTTL = time.Minute

// UnstructuredExtractor enables extracting the applied configuration state from object for fieldManager into an
// unstructured object type.
type UnstructuredExtractor interface {
	Extract(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error)
	ExtractStatus(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error)
}

// gvkParserCache caches the GVKParser in order to prevent from having to repeatedly
// parse the models from the open API schema when the schema itself changes infrequently.
type gvkParserCache struct {
	// discoveryClient is the client for retrieving the openAPI document and checking
	// whether the document has changed recently
	discoveryClient discovery.DiscoveryInterface
	// mu protects the gvkParser
	mu sync.Mutex
	// gvkParser retrieves the objectType for a given gvk
	gvkParser *fieldmanager.GvkParser
	// lastChecked is the last time we checked if the openAPI doc has changed.
	lastChecked time.Time
}

// regenerateGVKParser builds the parser from the raw OpenAPI schema.
func regenerateGVKParser(dc discovery.DiscoveryInterface) (*fieldmanager.GvkParser, error) {
	doc, err := dc.OpenAPISchema()
	if err != nil {
		return nil, err
	}

	models, err := proto.NewOpenAPIData(doc)
	if err != nil {
		return nil, err
	}

	return fieldmanager.NewGVKParser(models, false)
}

// objectTypeForGVK retrieves the typed.ParseableType for a given gvk from the cache
func (c *gvkParserCache) objectTypeForGVK(gvk schema.GroupVersionKind) (*typed.ParseableType, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	// if the ttl on the openAPISchema has expired,
	// recheck the discovery client to see if the Open API schema has changed
	if time.Now().After(c.lastChecked.Add(openAPISchemaTTL)) {
		c.lastChecked = time.Now()
		if c.discoveryClient.HasOpenAPISchemaChanged() {
			// the schema has changed, regenerate the parser
			parser, err := regenerateGVKParser(c.discoveryClient)
			if err != nil {
				return nil, err
			}
			c.gvkParser = parser
		}
	}
	return c.gvkParser.Type(gvk), nil
}

type extractor struct {
	cache *gvkParserCache
}

// NewUnstructuredExtractor creates the extractor with which you can extract the applied configuration
// for a given manager from an unstructured object.
func NewUnstructuredExtractor(dc discovery.DiscoveryInterface) (UnstructuredExtractor, error) {
	// TODO: expose ttl as an argument if we want to.

	parser, err := regenerateGVKParser(dc)
	if err != nil {
		return nil, err
	}
	return &extractor{
		cache: &gvkParserCache{
			gvkParser:       parser,
			discoveryClient: dc,
		},
	}, nil
}

// Extract extracts the applied configuration owned by fiieldManager from an unstructured object.
// Note that the apply configuration itself is also an unstructured object.
func (e *extractor) Extract(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error) {
	return e.extractUnstructured(object, fieldManager, "")
}

// ExtractStatus is the same as ExtractUnstructured except
// that it extracts the status subresource applied configuration.
// Experimental!
func (e *extractor) ExtractStatus(object *unstructured.Unstructured, fieldManager string) (*unstructured.Unstructured, error) {
	return e.extractUnstructured(object, fieldManager, "status")
}

func (e *extractor) extractUnstructured(object *unstructured.Unstructured, fieldManager string, subresource string) (*unstructured.Unstructured, error) {
	gvk := object.GetObjectKind().GroupVersionKind()
	objectType, err := e.cache.objectTypeForGVK(gvk)
	if err != nil {
		return nil, err
	}
	result := &unstructured.Unstructured{}
	err = managedfields.ExtractInto(object, *objectType, fieldManager, result, subresource)
	if err != nil {
		return nil, err
	}
	result.SetName(object.GetName())
	result.SetNamespace(object.GetNamespace())
	result.SetKind(object.GetKind())
	result.SetAPIVersion(object.GetAPIVersion())
	return result, nil
}
<|endoftext|>


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

//Package gobgp contains Ligato GoBGP Plugin implementation
package gobgp

import (
	"fmt"
	"github.com/ligato/bgp-agent/bgp"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/osrg/gobgp/config"
	"github.com/osrg/gobgp/server"
	"strconv"
	"sync"
)

// Plugin is GoBGP Ligato BGP Plugin implementation
type Plugin struct {
	Deps
	server                *server.BgpServer
	serverWatcher         *server.Watcher
	watchersWithCallbacks map[watcherName]func(*bgp.ReachableIPRoute)
	stopWatch             chan bool
	watchWG               sync.WaitGroup // wait group that allows to wait until Watch loop is ended
}

// Deps combines all needed dependencies for Plugin struct. These dependencies should be injected into Plugin by using constructor's Deps parameter.
type Deps struct {
	local.PluginInfraDeps             // inject
	SessionConfig         *config.Bgp // optional inject (if not injected, it must be set using external config file)
}

// watcherName is by-name identification of registered watcher
type watcherName string

//New creates a GoBGP Ligato BGP Plugin implementation. Needed dependencies are injected into plugin implementation.
func New(dependencies Deps) *Plugin {
	return &Plugin{Deps: dependencies, watchersWithCallbacks: map[watcherName]func(*bgp.ReachableIPRoute){}}
}

//Init creates the gobgp server and checks if needed SessionConfig was injected and fails if it is not.
func (plugin *Plugin) Init() error {
	plugin.Log.Debug("Init goBgp plugin")
	//TODO if not config load from filesystem, use config injection, if config injection is missing then error
	if plugin.SessionConfig == nil {
		return fmt.Errorf("Can't init GoBGP plugin without configuration")
	}
	plugin.server = server.NewBgpServer()

	return nil
}

// AfterInit starts gobgp with dedicated goroutine for watching gobgp and forwarding best path reachable ip routes to registered watchers.
// After start of gobgp session, known neighbors from configuration are added to gobgp server.
// Due to fact that AfterInit is called once Init() of all plugins have returned without error, other plugins can be registered watchers
// from the start of gobgp server if they call this plugin's WatchIPRoutes() in their Init(). In this way they won't miss any information
// forwarded to registered watchers just because they registered too late.
func (plugin *Plugin) AfterInit() error {
	go plugin.server.Serve()
	if err := plugin.startSession(); err != nil {
		return err
	}
	if err := plugin.addKnownNeighbors(); err != nil {
		return err
	}
	plugin.stopWatch = make(chan bool, 1)
	plugin.serverWatcher = plugin.server.Watch(server.WatchBestPath(true))
	plugin.watchWG.Add(1)
	go plugin.watchChanges(plugin.serverWatcher)

	return nil
}

// watchChanges watches for events from goBGP server, translates them to bgp.ReachableIPRoute and sends them to registered watchers.
func (plugin *Plugin) watchChanges(watcher *server.Watcher) {
	defer plugin.watchWG.Done()

	for {
		select {
		case <-plugin.stopWatch:
			plugin.Log.Debug("Stop Watching ", plugin.PluginName)
			return
		case ev := <-watcher.Event():
			switch msg := ev.(type) {
			case *server.WatchEventBestPath:
				for _, path := range msg.PathList {
					asPath := path.GetAsPath().String()
					as, err := strconv.ParseUint(asPath, 10, 32)
					if err != nil {
						plugin.Log.Warnf("Ignoring Path '%s' due to parse error: %v", asPath, err)
						continue
					}
					pathInfo := bgp.ReachableIPRoute{
						As:      uint32(as),
						Prefix:  path.GetNlri().String(),
						Nexthop: path.GetNexthop(),
					}
					plugin.Log.Debug("Fill channel with new path", pathInfo)
					for _, callback := range plugin.watchersWithCallbacks {
						callback(&pathInfo)
					}
				}
			}
		}
	}
}

//Close stops dedicated goroutine for watching gobgp. Then stops watcher provider by gobgp server and finally stops that gobgp server itself.
func (plugin *Plugin) Close() error {
	plugin.Log.Info("Closing goBgp plugin ", plugin.PluginName)
	close(plugin.stopWatch) //command to stop watching
	plugin.watchWG.Wait()   //wait for actual stop of watching
	plugin.serverWatcher.Stop()
	return plugin.server.Stop()
}

//WatchIPRoutes register watcher to notifications for any new learned IP-based routes.
//WatchRegistration is not retroactive, that means that any IP-based routes learned in the past are not send to new watchers.
//This also means that if you want be notified of all learned IP-based routes, you must register before calling of
//AfterInit(). In case of external(=not other plugin started with this plugin) watchers this means before plugin start.
//However, late-registered watchers are permitted (no error will be returned), but they can miss some learned IP-based routes.
func (plugin *Plugin) WatchIPRoutes(watcher string, callback func(*bgp.ReachableIPRoute)) (bgp.WatchRegistration, error) {
	plugin.Log.Infof("Watcher %s registering for watching of IPRoutes in %s.", watcher, plugin.PluginName)
	plugin.watchersWithCallbacks[watcherName(watcher)] = callback
	return &watchRegistration{watcher: watcherName(watcher), plugin: plugin}, nil
}

//startSession starts session on already running goBGP server
func (plugin *Plugin) startSession() error {
	if err := plugin.server.Start(&plugin.SessionConfig.Global); err != nil {
		plugin.Log.Error("Failed to initialize go server", plugin.PluginName, err)
		return err
	}
	return nil
}

// addKnownNeighbors configures goBGP server for known neighbors from config
func (plugin *Plugin) addKnownNeighbors() error {
	for _, neighbor := range plugin.SessionConfig.Neighbors {
		if err := plugin.server.AddNeighbor(&neighbor); err != nil {
			plugin.Log.Error("Failed to add go neighbour", plugin.PluginName, err)
			return err
		}
	}

	return nil
}

// watchRegistration is Plugin's simple WatchRegistration implementation that is sent to watchers.
// This implementation is not thread-safe.
type watchRegistration struct {
	watcher watcherName
	plugin  *Plugin
}

//Close ends the agreement between Plugin and watcher. Plugin stops sending watcher any further notifications.
func (wr *watchRegistration) Close() error {
	delete(wr.plugin.watchersWithCallbacks, wr.watcher)
	return nil
}
<commit_msg>added possibility of external file for GoBGP configuration<commit_after>// Copyright (c) 2017 Pantheon technologies s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//Package gobgp contains Ligato GoBGP Plugin implementation
package gobgp

import (
	"fmt"
	"github.com/ligato/bgp-agent/bgp"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/osrg/gobgp/config"
	"github.com/osrg/gobgp/server"
	"strconv"
	"sync"
)

// Plugin is GoBGP Ligato BGP Plugin implementation
type Plugin struct {
	Deps
	server                *server.BgpServer
	serverWatcher         *server.Watcher
	watchersWithCallbacks map[watcherName]func(*bgp.ReachableIPRoute)
	stopWatch             chan bool
	watchWG               sync.WaitGroup // wait group that allows to wait until Watch loop is ended
}

// Deps combines all needed dependencies for Plugin struct. These dependencies should be injected into Plugin by using constructor's Deps parameter.
type Deps struct {
	local.PluginInfraDeps             // inject
	SessionConfig         *config.Bgp // optional inject (if not injected, it must be set using external config file)
}

// watcherName is by-name identification of registered watcher
type watcherName string

//New creates a GoBGP Ligato BGP Plugin implementation. Needed dependencies are injected into plugin implementation.
func New(dependencies Deps) *Plugin {
	return &Plugin{Deps: dependencies, watchersWithCallbacks: map[watcherName]func(*bgp.ReachableIPRoute){}}
}

//Init creates the gobgp server and checks if needed SessionConfig was injected and fails if it is not.
func (plugin *Plugin) Init() error {
	plugin.Log.Debug("Init goBgp plugin")
	plugin.applyExternalConfig()
	if plugin.SessionConfig == nil {
		return fmt.Errorf("Can't init GoBGP plugin without configuration")
	}
	plugin.server = server.NewBgpServer()

	return nil
}

// applyExternalConfig tries to find and load configuration from external filesystem and change it for injected configuration, because external configuration has higher priority.
// If external configuration is not found or can't be loaded, plugin.SessionConfig is not changed. This means that previous injection of plugin.SessionConfig variable can be still used.
func (plugin *Plugin) applyExternalConfig() {
	var externalCfg *config.Bgp
	found, err := plugin.PluginConfig.GetValue(externalCfg)	// It tries to lookup `PluginName + "-config"` in go run command flags.
	if !found {
		plugin.Log.Debug("External GoBGP plugin configuration was not found")
		return
	}
	if err != nil {
		plugin.Log.Debug("External GoBGP plugin configuration could not load", err)
		return
	}
	plugin.SessionConfig = externalCfg
}

// AfterInit starts gobgp with dedicated goroutine for watching gobgp and forwarding best path reachable ip routes to registered watchers.
// After start of gobgp session, known neighbors from configuration are added to gobgp server.
// Due to fact that AfterInit is called once Init() of all plugins have returned without error, other plugins can be registered watchers
// from the start of gobgp server if they call this plugin's WatchIPRoutes() in their Init(). In this way they won't miss any information
// forwarded to registered watchers just because they registered too late.
func (plugin *Plugin) AfterInit() error {
	go plugin.server.Serve()
	if err := plugin.startSession(); err != nil {
		return err
	}
	if err := plugin.addKnownNeighbors(); err != nil {
		return err
	}
	plugin.stopWatch = make(chan bool, 1)
	plugin.serverWatcher = plugin.server.Watch(server.WatchBestPath(true))
	plugin.watchWG.Add(1)
	go plugin.watchChanges(plugin.serverWatcher)

	return nil
}

// watchChanges watches for events from goBGP server, translates them to bgp.ReachableIPRoute and sends them to registered watchers.
func (plugin *Plugin) watchChanges(watcher *server.Watcher) {
	defer plugin.watchWG.Done()

	for {
		select {
		case <-plugin.stopWatch:
			plugin.Log.Debug("Stop Watching ", plugin.PluginName)
			return
		case ev := <-watcher.Event():
			switch msg := ev.(type) {
			case *server.WatchEventBestPath:
				for _, path := range msg.PathList {
					asPath := path.GetAsPath().String()
					as, err := strconv.ParseUint(asPath, 10, 32)
					if err != nil {
						plugin.Log.Warnf("Ignoring Path '%s' due to parse error: %v", asPath, err)
						continue
					}
					pathInfo := bgp.ReachableIPRoute{
						As:      uint32(as),
						Prefix:  path.GetNlri().String(),
						Nexthop: path.GetNexthop(),
					}
					plugin.Log.Debug("Fill channel with new path", pathInfo)
					for _, callback := range plugin.watchersWithCallbacks {
						callback(&pathInfo)
					}
				}
			}
		}
	}
}

//Close stops dedicated goroutine for watching gobgp. Then stops watcher provider by gobgp server and finally stops that gobgp server itself.
func (plugin *Plugin) Close() error {
	plugin.Log.Info("Closing goBgp plugin ", plugin.PluginName)
	close(plugin.stopWatch) //command to stop watching
	plugin.watchWG.Wait()   //wait for actual stop of watching
	plugin.serverWatcher.Stop()
	return plugin.server.Stop()
}

//WatchIPRoutes register watcher to notifications for any new learned IP-based routes.
//WatchRegistration is not retroactive, that means that any IP-based routes learned in the past are not send to new watchers.
//This also means that if you want be notified of all learned IP-based routes, you must register before calling of
//AfterInit(). In case of external(=not other plugin started with this plugin) watchers this means before plugin start.
//However, late-registered watchers are permitted (no error will be returned), but they can miss some learned IP-based routes.
func (plugin *Plugin) WatchIPRoutes(watcher string, callback func(*bgp.ReachableIPRoute)) (bgp.WatchRegistration, error) {
	plugin.Log.Infof("Watcher %s registering for watching of IPRoutes in %s.", watcher, plugin.PluginName)
	plugin.watchersWithCallbacks[watcherName(watcher)] = callback
	return &watchRegistration{watcher: watcherName(watcher), plugin: plugin}, nil
}

//startSession starts session on already running goBGP server
func (plugin *Plugin) startSession() error {
	if err := plugin.server.Start(&plugin.SessionConfig.Global); err != nil {
		plugin.Log.Error("Failed to initialize go server", plugin.PluginName, err)
		return err
	}
	return nil
}

// addKnownNeighbors configures goBGP server for known neighbors from config
func (plugin *Plugin) addKnownNeighbors() error {
	for _, neighbor := range plugin.SessionConfig.Neighbors {
		if err := plugin.server.AddNeighbor(&neighbor); err != nil {
			plugin.Log.Error("Failed to add go neighbour", plugin.PluginName, err)
			return err
		}
	}

	return nil
}

// watchRegistration is Plugin's simple WatchRegistration implementation that is sent to watchers.
// This implementation is not thread-safe.
type watchRegistration struct {
	watcher watcherName
	plugin  *Plugin
}

//Close ends the agreement between Plugin and watcher. Plugin stops sending watcher any further notifications.
func (wr *watchRegistration) Close() error {
	delete(wr.plugin.watchersWithCallbacks, wr.watcher)
	return nil
}
<|endoftext|>


<commit_before>package certificates

import (
	"context"
	"crypto/x509"
	"fmt"
	"time"

	api "k8s.io/api/core/v1"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/runtime"

	"github.com/golang/glog"
	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/issuer"
	"github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/pkg/util/errors"
	"github.com/jetstack/cert-manager/pkg/util/kube"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

const renewBefore = time.Hour * 24 * 30

const (
	errorIssuerNotFound       = "ErrorIssuerNotFound"
	errorIssuerNotReady       = "ErrorIssuerNotReady"
	errorIssuerInit           = "ErrorIssuerInitialization"
	errorCheckCertificate     = "ErrorCheckCertificate"
	errorGetCertificate       = "ErrorGetCertificate"
	errorPreparingCertificate = "ErrorPrepareCertificate"
	errorIssuingCertificate   = "ErrorIssueCertificate"
	errorRenewingCertificate  = "ErrorRenewCertificate"
	errorSavingCertificate    = "ErrorSaveCertificate"

	reasonPreparingCertificate = "PrepareCertificate"
	reasonIssuingCertificate   = "IssueCertificate"
	reasonRenewingCertificate  = "RenewCertificate"

	successCeritificateIssued  = "CeritifcateIssued"
	successCeritificateRenewed = "CeritifcateRenewed"
	successRenewalScheduled    = "RenewalScheduled"

	messageIssuerNotFound            = "Issuer %s does not exist"
	messageIssuerNotReady            = "Issuer %s not ready"
	messageIssuerErrorInit           = "Error initializing issuer: "
	messageErrorCheckCertificate     = "Error checking existing TLS certificate: "
	messageErrorGetCertificate       = "Error getting TLS certificate: "
	messageErrorPreparingCertificate = "Error preparing issuer for certificate: "
	messageErrorIssuingCertificate   = "Error issuing certificate: "
	messageErrorRenewingCertificate  = "Error renewing certificate: "
	messageErrorSavingCertificate    = "Error saving TLS certificate: "

	messagePreparingCertificate = "Preparing certificate with issuer"
	messageIssuingCertificate   = "Issuing certificate..."
	messageRenewingCertificate  = "Renewing certificate..."

	messageCertificateIssued  = "Certificated issued successfully"
	messageCertificateRenewed = "Certificated renewed successfully"
	messageRenewalScheduled   = "Certificate scheduled for renewal in %d hours"
)

func (c *Controller) Sync(ctx context.Context, crt *v1alpha1.Certificate) (err error) {
	// step zero: check if the referenced issuer exists and is ready
	issuerObj, err := c.getGenericIssuer(crt)

	if err != nil {
		s := fmt.Sprintf(messageIssuerNotFound, err.Error())
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuerNotFound, s)
		return err
	}

	issuerReady := issuerObj.HasCondition(v1alpha1.IssuerCondition{
		Type:   v1alpha1.IssuerConditionReady,
		Status: v1alpha1.ConditionTrue,
	})
	if !issuerReady {
		s := fmt.Sprintf(messageIssuerNotReady, issuerObj.GetObjectMeta().Name)
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuerNotReady, s)
		return fmt.Errorf(s)
	}

	i, err := c.issuerFactory.IssuerFor(issuerObj)
	if err != nil {
		s := messageIssuerErrorInit + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuerInit, s)
		return err
	}

	expectedCN, err := pki.CommonNameForCertificate(crt)
	if err != nil {
		return err
	}
	expectedDNSNames, err := pki.DNSNamesForCertificate(crt)
	if err != nil {
		return err
	}

	// grab existing certificate and validate private key
	cert, err := kube.SecretTLSCert(c.secretLister, crt.Namespace, crt.Spec.SecretName)
	if err != nil {
		s := messageErrorCheckCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorCheckCertificate, s)
	}

	// if an error is returned, and that error is something other than
	// IsNotFound or invalid data, then we should return the error.
	if err != nil && !k8sErrors.IsNotFound(err) && !errors.IsInvalidData(err) {
		return err
	}

	// as there is an existing certificate, or we may create one below, we will
	// run scheduleRenewal to schedule a renewal if required at the end of
	// execution.
	defer c.scheduleRenewal(crt)

	crtCopy := crt.DeepCopy()

	// if the certificate was not found, or the certificate data is invalid, we
	// should issue a new certificate.
	// if the certificate is valid for a list of domains other than those
	// listed in the certificate spec, we should re-issue the certificate.
	if k8sErrors.IsNotFound(err) || errors.IsInvalidData(err) ||
		expectedCN != cert.Subject.CommonName || !util.EqualUnsorted(cert.DNSNames, expectedDNSNames) {
		err := c.issue(ctx, i, crtCopy)
		updateErr := c.updateCertificateStatus(crtCopy)
		if err != nil || updateErr != nil {
			return utilerrors.NewAggregate([]error{err, updateErr})
		}
		return nil
	}

	// calculate the amount of time until expiry
	durationUntilExpiry := cert.NotAfter.Sub(time.Now())
	// calculate how long until we should start attempting to renew the
	// certificate
	renewIn := durationUntilExpiry - renewBefore
	// if we should being attempting to renew now, then trigger a renewal
	if renewIn <= 0 {
		err := c.renew(ctx, i, crtCopy)
		updateErr := c.updateCertificateStatus(crtCopy)
		if err != nil || updateErr != nil {
			return utilerrors.NewAggregate([]error{err, updateErr})
		}
	}

	return nil
}

func (c *Controller) getGenericIssuer(crt *v1alpha1.Certificate) (v1alpha1.GenericIssuer, error) {
	switch crt.Spec.IssuerRef.Kind {
	case "", v1alpha1.IssuerKind:
		return c.issuerLister.Issuers(crt.Namespace).Get(crt.Spec.IssuerRef.Name)
	case v1alpha1.ClusterIssuerKind:
		if c.clusterIssuerLister == nil {
			return nil, fmt.Errorf("cannot get ClusterIssuer for %q as cert-manager is scoped to a single namespace", crt.Name)
		}
		return c.clusterIssuerLister.Get(crt.Spec.IssuerRef.Name)
	default:
		return nil, fmt.Errorf(`invalid value %q for certificate issuer kind. Must be empty, %q or %q`, crt.Spec.IssuerRef.Kind, v1alpha1.IssuerKind, v1alpha1.ClusterIssuerKind)
	}
}

func needsRenew(cert *x509.Certificate) bool {
	durationUntilExpiry := cert.NotAfter.Sub(time.Now())
	renewIn := durationUntilExpiry - renewBefore
	// step three: check if referenced secret is valid (after start & before expiry)
	if renewIn <= 0 {
		return true
	}
	return false
}

func (c *Controller) scheduleRenewal(crt *v1alpha1.Certificate) {
	key, err := keyFunc(crt)

	if err != nil {
		runtime.HandleError(fmt.Errorf("error getting key for certificate resource: %s", err.Error()))
		return
	}

	cert, err := kube.SecretTLSCert(c.secretLister, crt.Namespace, crt.Spec.SecretName)

	if err != nil {
		runtime.HandleError(fmt.Errorf("[%s/%s] Error getting certificate '%s': %s", crt.Namespace, crt.Name, crt.Spec.SecretName, err.Error()))
		return
	}

	durationUntilExpiry := cert.NotAfter.Sub(time.Now())
	renewIn := durationUntilExpiry - renewBefore

	c.scheduledWorkQueue.Add(key, renewIn)

	s := fmt.Sprintf(messageRenewalScheduled, renewIn/time.Hour)
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, successRenewalScheduled, s)
}

func (c *Controller) updateSecret(name, namespace string, cert, key []byte) (*api.Secret, error) {
	secret, err := c.client.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
	if err != nil && !k8sErrors.IsNotFound(err) {
		return nil, err
	}
	if k8sErrors.IsNotFound(err) {
		secret = &api.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:      name,
				Namespace: namespace,
			},
			Type: api.SecretTypeTLS,
			Data: map[string][]byte{},
		}
	}
	secret.Data[api.TLSCertKey] = cert
	secret.Data[api.TLSPrivateKeyKey] = key
	// if it is a new resource
	if secret.SelfLink == "" {
		secret, err = c.client.CoreV1().Secrets(namespace).Create(secret)
	} else {
		secret, err = c.client.CoreV1().Secrets(namespace).Update(secret)
	}
	if err != nil {
		return nil, err
	}
	return secret, nil
}

// return an error on failure. If retrieval is succesful, the certificate data
// and private key will be stored in the named secret
func (c *Controller) issue(ctx context.Context, issuer issuer.Interface, crt *v1alpha1.Certificate) error {
	var err error
	s := messagePreparingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonPreparingCertificate, s)
	if err = issuer.Prepare(ctx, crt); err != nil {
		s := messageErrorPreparingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorPreparingCertificate, s)
		return err
	}

	s = messageIssuingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonIssuingCertificate, s)

	var key, cert []byte
	key, cert, err = issuer.Issue(ctx, crt)

	if err != nil {
		s := messageErrorIssuingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuingCertificate, s)
		return err
	}

	if _, err := c.updateSecret(crt.Spec.SecretName, crt.Namespace, cert, key); err != nil {
		s := messageErrorSavingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorSavingCertificate, s)
		return err
	}

	s = messageCertificateIssued
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, successCeritificateIssued, s)

	return nil
}

// renew will attempt to renew a certificate from the specified issuer, or
// return an error on failure. If renewal is succesful, the certificate data
// and private key will be stored in the named secret
func (c *Controller) renew(ctx context.Context, issuer issuer.Interface, crt *v1alpha1.Certificate) error {
	var err error
	s := messagePreparingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonPreparingCertificate, s)

	if err = issuer.Prepare(ctx, crt); err != nil {
		s := messageErrorPreparingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorPreparingCertificate, s)
		return err
	}

	s = messageRenewingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonRenewingCertificate, s)

	var key, cert []byte
	key, cert, err = issuer.Renew(ctx, crt)

	if err != nil {
		s := messageErrorRenewingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorRenewingCertificate, s)
		return err
	}

	if _, err := c.updateSecret(crt.Spec.SecretName, crt.Namespace, cert, key); err != nil {
		s := messageErrorSavingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorSavingCertificate, s)
		return err
	}

	s = messageCertificateRenewed
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, successCeritificateRenewed, s)

	return nil
}

func (c *Controller) updateCertificateStatus(crt *v1alpha1.Certificate) error {
	// TODO: replace Update call with UpdateStatus. This requires a custom API
	// server with the /status subresource enabled and/or subresource support
	// for CRDs (https://github.com/kubernetes/kubernetes/issues/38113)
	_, err := c.cmClient.CertmanagerV1alpha1().Certificates(crt.Namespace).Update(crt)
	return err
}
<commit_msg>Make existing TLS certificate check emit a Normal event instead of Warning when the existing certificate is invalid<commit_after>package certificates

import (
	"context"
	"crypto/x509"
	"fmt"
	"time"

	api "k8s.io/api/core/v1"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/runtime"

	"github.com/golang/glog"
	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/issuer"
	"github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/pkg/util/errors"
	"github.com/jetstack/cert-manager/pkg/util/kube"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

const renewBefore = time.Hour * 24 * 30

const (
	errorIssuerNotFound       = "ErrorIssuerNotFound"
	errorIssuerNotReady       = "ErrorIssuerNotReady"
	errorIssuerInit           = "ErrorIssuerInitialization"
	errorCheckCertificate     = "ErrorCheckCertificate"
	errorGetCertificate       = "ErrorGetCertificate"
	errorPreparingCertificate = "ErrorPrepareCertificate"
	errorIssuingCertificate   = "ErrorIssueCertificate"
	errorRenewingCertificate  = "ErrorRenewCertificate"
	errorSavingCertificate    = "ErrorSaveCertificate"

	reasonPreparingCertificate = "PrepareCertificate"
	reasonIssuingCertificate   = "IssueCertificate"
	reasonRenewingCertificate  = "RenewCertificate"

	successCeritificateIssued  = "CeritifcateIssued"
	successCeritificateRenewed = "CeritifcateRenewed"
	successRenewalScheduled    = "RenewalScheduled"

	messageIssuerNotFound            = "Issuer %s does not exist"
	messageIssuerNotReady            = "Issuer %s not ready"
	messageIssuerErrorInit           = "Error initializing issuer: "
	messageErrorCheckCertificate     = "Error checking existing TLS certificate, will re-issue: "
	messageErrorGetCertificate       = "Error getting TLS certificate: "
	messageErrorPreparingCertificate = "Error preparing issuer for certificate: "
	messageErrorIssuingCertificate   = "Error issuing certificate: "
	messageErrorRenewingCertificate  = "Error renewing certificate: "
	messageErrorSavingCertificate    = "Error saving TLS certificate: "

	messagePreparingCertificate = "Preparing certificate with issuer"
	messageIssuingCertificate   = "Issuing certificate..."
	messageRenewingCertificate  = "Renewing certificate..."

	messageCertificateIssued  = "Certificated issued successfully"
	messageCertificateRenewed = "Certificated renewed successfully"
	messageRenewalScheduled   = "Certificate scheduled for renewal in %d hours"
)

func (c *Controller) Sync(ctx context.Context, crt *v1alpha1.Certificate) (err error) {
	// step zero: check if the referenced issuer exists and is ready
	issuerObj, err := c.getGenericIssuer(crt)

	if err != nil {
		s := fmt.Sprintf(messageIssuerNotFound, err.Error())
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuerNotFound, s)
		return err
	}

	issuerReady := issuerObj.HasCondition(v1alpha1.IssuerCondition{
		Type:   v1alpha1.IssuerConditionReady,
		Status: v1alpha1.ConditionTrue,
	})
	if !issuerReady {
		s := fmt.Sprintf(messageIssuerNotReady, issuerObj.GetObjectMeta().Name)
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuerNotReady, s)
		return fmt.Errorf(s)
	}

	i, err := c.issuerFactory.IssuerFor(issuerObj)
	if err != nil {
		s := messageIssuerErrorInit + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuerInit, s)
		return err
	}

	expectedCN, err := pki.CommonNameForCertificate(crt)
	if err != nil {
		return err
	}
	expectedDNSNames, err := pki.DNSNamesForCertificate(crt)
	if err != nil {
		return err
	}

	// grab existing certificate and validate private key
	cert, err := kube.SecretTLSCert(c.secretLister, crt.Namespace, crt.Spec.SecretName)
	if err != nil {
		s := messageErrorCheckCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeNormal, errorCheckCertificate, s)
	}

	// if an error is returned, and that error is something other than
	// IsNotFound or invalid data, then we should return the error.
	if err != nil && !k8sErrors.IsNotFound(err) && !errors.IsInvalidData(err) {
		return err
	}

	// as there is an existing certificate, or we may create one below, we will
	// run scheduleRenewal to schedule a renewal if required at the end of
	// execution.
	defer c.scheduleRenewal(crt)

	crtCopy := crt.DeepCopy()

	// if the certificate was not found, or the certificate data is invalid, we
	// should issue a new certificate.
	// if the certificate is valid for a list of domains other than those
	// listed in the certificate spec, we should re-issue the certificate.
	if k8sErrors.IsNotFound(err) || errors.IsInvalidData(err) ||
		expectedCN != cert.Subject.CommonName || !util.EqualUnsorted(cert.DNSNames, expectedDNSNames) {
		err := c.issue(ctx, i, crtCopy)
		updateErr := c.updateCertificateStatus(crtCopy)
		if err != nil || updateErr != nil {
			return utilerrors.NewAggregate([]error{err, updateErr})
		}
		return nil
	}

	// calculate the amount of time until expiry
	durationUntilExpiry := cert.NotAfter.Sub(time.Now())
	// calculate how long until we should start attempting to renew the
	// certificate
	renewIn := durationUntilExpiry - renewBefore
	// if we should being attempting to renew now, then trigger a renewal
	if renewIn <= 0 {
		err := c.renew(ctx, i, crtCopy)
		updateErr := c.updateCertificateStatus(crtCopy)
		if err != nil || updateErr != nil {
			return utilerrors.NewAggregate([]error{err, updateErr})
		}
	}

	return nil
}

func (c *Controller) getGenericIssuer(crt *v1alpha1.Certificate) (v1alpha1.GenericIssuer, error) {
	switch crt.Spec.IssuerRef.Kind {
	case "", v1alpha1.IssuerKind:
		return c.issuerLister.Issuers(crt.Namespace).Get(crt.Spec.IssuerRef.Name)
	case v1alpha1.ClusterIssuerKind:
		if c.clusterIssuerLister == nil {
			return nil, fmt.Errorf("cannot get ClusterIssuer for %q as cert-manager is scoped to a single namespace", crt.Name)
		}
		return c.clusterIssuerLister.Get(crt.Spec.IssuerRef.Name)
	default:
		return nil, fmt.Errorf(`invalid value %q for certificate issuer kind. Must be empty, %q or %q`, crt.Spec.IssuerRef.Kind, v1alpha1.IssuerKind, v1alpha1.ClusterIssuerKind)
	}
}

func needsRenew(cert *x509.Certificate) bool {
	durationUntilExpiry := cert.NotAfter.Sub(time.Now())
	renewIn := durationUntilExpiry - renewBefore
	// step three: check if referenced secret is valid (after start & before expiry)
	if renewIn <= 0 {
		return true
	}
	return false
}

func (c *Controller) scheduleRenewal(crt *v1alpha1.Certificate) {
	key, err := keyFunc(crt)

	if err != nil {
		runtime.HandleError(fmt.Errorf("error getting key for certificate resource: %s", err.Error()))
		return
	}

	cert, err := kube.SecretTLSCert(c.secretLister, crt.Namespace, crt.Spec.SecretName)

	if err != nil {
		runtime.HandleError(fmt.Errorf("[%s/%s] Error getting certificate '%s': %s", crt.Namespace, crt.Name, crt.Spec.SecretName, err.Error()))
		return
	}

	durationUntilExpiry := cert.NotAfter.Sub(time.Now())
	renewIn := durationUntilExpiry - renewBefore

	c.scheduledWorkQueue.Add(key, renewIn)

	s := fmt.Sprintf(messageRenewalScheduled, renewIn/time.Hour)
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, successRenewalScheduled, s)
}

func (c *Controller) updateSecret(name, namespace string, cert, key []byte) (*api.Secret, error) {
	secret, err := c.client.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
	if err != nil && !k8sErrors.IsNotFound(err) {
		return nil, err
	}
	if k8sErrors.IsNotFound(err) {
		secret = &api.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:      name,
				Namespace: namespace,
			},
			Type: api.SecretTypeTLS,
			Data: map[string][]byte{},
		}
	}
	secret.Data[api.TLSCertKey] = cert
	secret.Data[api.TLSPrivateKeyKey] = key
	// if it is a new resource
	if secret.SelfLink == "" {
		secret, err = c.client.CoreV1().Secrets(namespace).Create(secret)
	} else {
		secret, err = c.client.CoreV1().Secrets(namespace).Update(secret)
	}
	if err != nil {
		return nil, err
	}
	return secret, nil
}

// return an error on failure. If retrieval is succesful, the certificate data
// and private key will be stored in the named secret
func (c *Controller) issue(ctx context.Context, issuer issuer.Interface, crt *v1alpha1.Certificate) error {
	var err error
	s := messagePreparingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonPreparingCertificate, s)
	if err = issuer.Prepare(ctx, crt); err != nil {
		s := messageErrorPreparingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorPreparingCertificate, s)
		return err
	}

	s = messageIssuingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonIssuingCertificate, s)

	var key, cert []byte
	key, cert, err = issuer.Issue(ctx, crt)

	if err != nil {
		s := messageErrorIssuingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorIssuingCertificate, s)
		return err
	}

	if _, err := c.updateSecret(crt.Spec.SecretName, crt.Namespace, cert, key); err != nil {
		s := messageErrorSavingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorSavingCertificate, s)
		return err
	}

	s = messageCertificateIssued
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, successCeritificateIssued, s)

	return nil
}

// renew will attempt to renew a certificate from the specified issuer, or
// return an error on failure. If renewal is succesful, the certificate data
// and private key will be stored in the named secret
func (c *Controller) renew(ctx context.Context, issuer issuer.Interface, crt *v1alpha1.Certificate) error {
	var err error
	s := messagePreparingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonPreparingCertificate, s)

	if err = issuer.Prepare(ctx, crt); err != nil {
		s := messageErrorPreparingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorPreparingCertificate, s)
		return err
	}

	s = messageRenewingCertificate
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, reasonRenewingCertificate, s)

	var key, cert []byte
	key, cert, err = issuer.Renew(ctx, crt)

	if err != nil {
		s := messageErrorRenewingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorRenewingCertificate, s)
		return err
	}

	if _, err := c.updateSecret(crt.Spec.SecretName, crt.Namespace, cert, key); err != nil {
		s := messageErrorSavingCertificate + err.Error()
		glog.Info(s)
		c.recorder.Event(crt, api.EventTypeWarning, errorSavingCertificate, s)
		return err
	}

	s = messageCertificateRenewed
	glog.Info(s)
	c.recorder.Event(crt, api.EventTypeNormal, successCeritificateRenewed, s)

	return nil
}

func (c *Controller) updateCertificateStatus(crt *v1alpha1.Certificate) error {
	// TODO: replace Update call with UpdateStatus. This requires a custom API
	// server with the /status subresource enabled and/or subresource support
	// for CRDs (https://github.com/kubernetes/kubernetes/issues/38113)
	_, err := c.cmClient.CertmanagerV1alpha1().Certificates(crt.Namespace).Update(crt)
	return err
}
<|endoftext|>