<commit_before>package command

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/koding/kite"
	"github.com/koding/kite/config"
	"github.com/koding/kite/protocol"
)

type Actioner interface {
	Action([]string, *kite.Client) error
}

type ActionFunc func(args []string, k *kite.Client) error

func (a ActionFunc) Action(args []string, k *kite.Client) error {
	return a(args, k)
}

func kloudWrapper(args []string, actioner Actioner) error {
	k, err := kloudClient()
	if err != nil {
		DefaultUi.Error(err.Error())
		return err
	}

	err = actioner.Action(args, k)
	if err != nil {
		DefaultUi.Error(err.Error())
		return err
	}

	return nil

}

func kloudClient() (*kite.Client, error) {
	k := kite.New("kloudctl", "0.0.1")
	config, err := config.Get()
	if err != nil {
		return nil, err
	}

	k.Config = config

	kloudQuery := protocol.KontrolQuery{
		Username:    "koding",
		Environment: "vagrant",
		Name:        "kloud",
	}

	kites, err := k.GetKites(kloudQuery)
	if err != nil {
		return nil, err
	}

	remoteKite := func(index int) (*kite.Client, error) {
		remoteKloud := kites[index-1]
		if err := remoteKloud.Dial(); err != nil {
			return nil, err
		}

		return remoteKloud, nil
	}

	if len(kites) == 1 {
		return remoteKite(1)
	}

	// we have more than one kloud instance
	DefaultUi.Output("Which kloud instance do you want to use?\n")
	for i, kite := range kites {
		fmt.Printf("[%d\t %+v\n", i+1, kite)
	}

	response, err := DefaultUi.Ask("\n==> ")
	if err != nil {
		return nil, err
	}

	index, err := strconv.Atoi(response)
	if err != nil {
		return nil, err
	}

	if index > len(kites) || index == 0 {
		return nil, errors.New("Invalid input")
	}

	return remoteKite(index)
}
<commit_msg>kloud/cli: do not show kite logging<commit_after>package command

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/koding/kite"
	"github.com/koding/kite/config"
	"github.com/koding/kite/protocol"
)

type Actioner interface {
	Action([]string, *kite.Client) error
}

type ActionFunc func(args []string, k *kite.Client) error

func (a ActionFunc) Action(args []string, k *kite.Client) error {
	return a(args, k)
}

func kloudWrapper(args []string, actioner Actioner) error {
	k, err := kloudClient()
	if err != nil {
		DefaultUi.Error(err.Error())
		return err
	}

	err = actioner.Action(args, k)
	if err != nil {
		DefaultUi.Error(err.Error())
		return err
	}

	return nil

}

func kloudClient() (*kite.Client, error) {
	k := kite.New("kloudctl", "0.0.1")
	config, err := config.Get()
	if err != nil {
		return nil, err
	}

	k.Config = config
	k.SetLogLevel(kite.WARNING)

	kloudQuery := protocol.KontrolQuery{
		Username:    "koding",
		Environment: "vagrant",
		Name:        "kloud",
	}

	kites, err := k.GetKites(kloudQuery)
	if err != nil {
		return nil, err
	}

	remoteKite := func(index int) (*kite.Client, error) {
		remoteKloud := kites[index-1]
		if err := remoteKloud.Dial(); err != nil {
			return nil, err
		}

		return remoteKloud, nil
	}

	if len(kites) == 1 {
		return remoteKite(1)
	}

	// we have more than one kloud instance
	DefaultUi.Output("Which kloud instance do you want to use?\n")
	for i, kite := range kites {
		fmt.Printf("[%d\t %+v\n", i+1, kite)
	}

	response, err := DefaultUi.Ask("\n==> ")
	if err != nil {
		return nil, err
	}

	index, err := strconv.Atoi(response)
	if err != nil {
		return nil, err
	}

	if index > len(kites) || index == 0 {
		return nil, errors.New("Invalid input")
	}

	return remoteKite(index)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/scipipe/scipipe"
)

// AuditReport is a container for data to be parsed into an audit report, in
// HTML, TeX or other format
type auditReport struct {
	FileName    string
	ScipipeVer  string
	RunTime     time.Duration
	AuditInfos  []*scipipe.AuditInfo
	ColorDef    string
	ChartHeight string
}

var (
	tplFuncs = template.FuncMap{
		"strrepl":        func(subj string, find string, repl string) string { return strings.Replace(subj, find, repl, -1) },
		"sub":            func(val1 int, val2 int) int { return val1 - val2 },
		"timesub":        func(t1 time.Time, t2 time.Time) time.Duration { return t1.Sub(t2) },
		"durtomillis":    func(exact time.Duration) (rounded time.Duration) { return exact.Truncate(1e6 * time.Nanosecond) },
		"timetomillis":   func(exact time.Time) (rounded time.Time) { return exact.Truncate(1e6 * time.Nanosecond) },
		"durtomillisint": func(exact time.Duration) (millis int) { return int(exact.Nanoseconds() / 1000000) },
	}
)

func auditInfoToHTML(inFilePath string, outFilePath string, flatten bool) error {
	ip := scipipe.NewFileIP(strings.Replace(inFilePath, ".audit.json", "", 1))
	auditInfo := ip.AuditInfo()

	outHTML := fmt.Sprintf(headHTMLPattern, ip.Path())
	if flatten {
		auditInfosByID := extractAuditInfosByID(auditInfo)
		auditInfosByStartTime := sortAuditInfosByStartTime(auditInfosByID)
		for _, ai := range auditInfosByStartTime {
			ai.Upstream = nil
			outHTML += formatTaskHTML(ai.ProcessName, ai)
		}
	} else {
		outHTML += formatTaskHTML(ip.Path(), auditInfo)
	}
	outHTML += bottomHTML

	if _, err := os.Stat(outFilePath); os.IsExist(err) {
		return errWrap(err, "File already exists:"+outFilePath)
	}
	outFile, err := os.Create(outFilePath)
	if err != nil {
		return errWrap(err, "Could not create file:"+outFilePath)
	}
	outFile.WriteString(outHTML)
	outFile.Close()
	fmt.Println("Wrote audit HTML file to: " + outFilePath)
	return nil
}

func formatTaskHTML(fileName string, auditInfo *scipipe.AuditInfo) (outHTML string) {
	outHTML = "
\n"
	outHTML += fmt.Sprintf("
%s / %s
\n", auditInfo.ProcessName, auditInfo.ID, auditInfo.ID, auditInfo.ID)
	outHTML += fmt.Sprintf("

%s

\n", auditInfo.Command)

	params := []string{}
	for pname, p := range auditInfo.Params {
		params = append(params, fmt.Sprintf("%s: %s", pname, p))
	}
	outHTML += fmt.Sprintf("
Parameters:	%s
\n", strings.Join(params, ", "))
	tags := []string{}
	for pname, p := range auditInfo.Tags {
		tags = append(tags, fmt.Sprintf("%s: %s", pname, p))
	}
	outHTML += fmt.Sprintf("
Tags:
%v

\n", strings.Join(tags, ", "))

	outHTML += fmt.Sprintf("
Start time:	%s
\n", auditInfo.StartTime.Format(`2006-01-02 15:04:05<span class="greyout">.000 -0700 MST</span>`))
	outHTML += fmt.Sprintf("
Finish time:	%s
\n", auditInfo.FinishTime.Format(`2006-01-02 15:04:05<span class="greyout">.000 -0700 MST</span>`))
	et := auditInfo.ExecTimeNS
	outHTML += fmt.Sprintf("
Execution time:	%s
\n", et.Truncate(time.Millisecond).String())
	//upStreamHTML := ""
	//for filePath, uai := range auditInfo.Upstream {
	//	upStreamHTML += formatTaskHTML(filePath, uai)
	//}
	//if outHTML != "" {
	//	outHTML += "
Upstreams:	" + upStreamHTML + "
\n"
	//}
	outHTML += "
\n"
	return
}

func auditInfoToTeX(inFilePath string, outFilePath string, flatten bool) error {
	outFile, err := os.Create(outFilePath)
	scipipe.CheckWithMsg(err, "Could not create TeX file")

	auditInfo := scipipe.UnmarshalAuditInfoJSONFile(inFilePath)
	auditInfosByID := extractAuditInfosByID(auditInfo)
	auditInfosByStartTime := sortAuditInfosByStartTime(auditInfosByID)

	texTpl := template.New("TeX").Funcs(tplFuncs)
	texTpl, err = texTpl.Parse(texTemplate)
	scipipe.CheckWithMsg(err, "Could not parse TeX template")

	runTime := time.Duration(0)
	for _, auInfo := range auditInfosByStartTime {
		runTime += auInfo.ExecTimeNS
	}

	report := auditReport{
		FileName:    inFilePath,
		ScipipeVer:  scipipe.Version,
		RunTime:     runTime,
		AuditInfos:  auditInfosByStartTime,
		ChartHeight: fmt.Sprintf("%.03f", 1.0+float64(len(auditInfosByStartTime))*0.5),
	}

	palette := palettes[1]
	if len(report.AuditInfos) <= 50 {
		palette = palettes[len(report.AuditInfos)]
	} else {
		palette = palettes[len(report.AuditInfos)%50]
	}
	for i, p := range palette {
		report.ColorDef += fmt.Sprintf("\\definecolor{color%d}{RGB}{%d,%d,%d}\n", i, p.r, p.g, p.b)
	}

	texTpl.Execute(outFile, report)
	fmt.Println("Wrote audit TeX file to: " + outFilePath)
	return nil
}

func extractAuditInfosByID(auditInfo *scipipe.AuditInfo) (auditInfosByID map[string]*scipipe.AuditInfo) {
	auditInfosByID = make(map[string]*scipipe.AuditInfo)
	auditInfosByID[auditInfo.ID] = auditInfo
	for _, ai := range auditInfo.Upstream {
		auditInfosByID = mergeStringAuditInfoMaps(auditInfosByID, extractAuditInfosByID(ai))
	}
	return auditInfosByID
}

func mergeStringAuditInfoMaps(ms ...map[string]*scipipe.AuditInfo) (merged map[string]*scipipe.AuditInfo) {
	merged = make(map[string]*scipipe.AuditInfo)
	for _, m := range ms {
		for k, v := range m {
			merged[k] = v
		}
	}
	return merged
}

func sortAuditInfosByStartTime(auditInfosByID map[string]*scipipe.AuditInfo) []*scipipe.AuditInfo {
	sorted := []*scipipe.AuditInfo{}

	auditInfosByStartTime := map[time.Time]*scipipe.AuditInfo{}
	startTimes := []time.Time{}
	for _, ai := range auditInfosByID {
		auditInfosByStartTime[ai.StartTime] = ai
		startTimes = append(startTimes, ai.StartTime)
	}
	sort.Slice(startTimes, func(i, j int) bool { return startTimes[i].Before(startTimes[j]) })
	for _, t := range startTimes {
		sorted = append(sorted, auditInfosByStartTime[t])
	}
	return sorted
}

const headHTMLPattern = `

<title>Audit info for: %s</title>

`
const bottomHTML = `
`

// LaTeX code from vision.tex:
const texTemplate = `\documentclass[11pt,oneside,openright]{memoir}

\usepackage{tcolorbox}
\usepackage[scaled]{beramono}
\renewcommand*\familydefault{\ttdefault}
\usepackage[T1]{fontenc}
\usepackage{tabularx}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{tikz}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{xcolor}

{{ .ColorDef }}

% from https://tex.stackexchange.com/a/128040/110842
% filter to only get the current row in \pgfplotsinvokeforeach
\pgfplotsset{
    select row/.style={
        x filter/.code={\ifnum\coordindex=#1\else\def\pgfmathresult{}\fi}
    }
}

\pgfplotstableread[col sep=comma]{
start,end,Name,color
{{ $startTime := (index .AuditInfos 0).StartTime }}
{{ range $i, $v := .AuditInfos }}{{ durtomillisint (timesub $v.StartTime $startTime) }},{{ durtomillisint (timesub $v.FinishTime $startTime) }},{{ strrepl .ProcessName "_" "\\_" }},color{{ $i }}
{{ end }}
}\loadedtable
\pgfplotstablegetrowsof{\loadedtable}
\pgfplotsset{compat=1.13}
\pgfmathsetmacro{\tablerows}{int(\pgfplotsretval-1)}

\begin{document}
\pagestyle{plain}
\noindent
\begin{minipage}{\textwidth}
    \vspace{-8em}\hspace{-8em}
    %\includegraphics[width=9em]{images/scipipe_logo_bluegrey.png}
\end{minipage}

\noindent
{\huge\textbf{SciPipe Audit Report}} \\
\vspace{10pt}

    \begin{tcolorbox}[ title=Workflow for file: {{ (strrepl (strrepl .FileName ".audit.json" "") "_" "\\_") }} ]
    \small
\begin{tabular}{rp{0.72\linewidth}}
SciPipe version: & {{ .ScipipeVer }} \\
Start time:  & {{ timetomillis (index .AuditInfos 0).StartTime }} \\
Finish time: & {{ timetomillis (index .AuditInfos (sub (len .AuditInfos) 1)).FinishTime }} \\
Run time: & {{ durtomillis .RunTime }}  \\
\end{tabular}
    \end{tcolorbox}

\setlength{\fboxsep}{0pt}
\noindent

%\hspace{-0.1725\textwidth}\fbox{\includegraphics[width=1.35\textwidth]{images/cawpre.pdf}}

\section*{Execution timeline}

\begin{tikzpicture}
\begin{axis}[
    xbar, xmin=0,
    y axis line style = { opacity = 0 },
    tickwidth         = 0pt,
	width=10cm,
	height={{ .ChartHeight }}cm,
    % next two lines also from https://tex.stackexchange.com/a/128040/110842,
    ytick={0,...,\tablerows},
    yticklabels from table={\loadedtable}{Name},
    xbar stacked,
    bar shift=0pt,
    y dir=reverse,
    xtick={1, 60000, 120000, 180000, 240000, 300000, 600000, 900000, 1200000},
    xticklabels={0, 1 min, 2 min, 3 min, 4 min, 5 min, 10 min, 15 min, 20 min},
    scaled x ticks=false,
]

\pgfplotsinvokeforeach{0,...,\tablerows}{
    % get color from table, commands defined must be individual for each plot
    % because the color is used in \end{axis} and therefore would otherwise
    % use the last definition
    \pgfplotstablegetelem{#1}{color}\of{\loadedtable}
    \expandafter\edef\csname barcolor.#1\endcsname{\pgfplotsretval}
    \addplot+[color=\csname barcolor.#1\endcsname] table [select row=#1, x expr=\thisrow{end}-\thisrow{start}, y expr=#1]{\loadedtable};
}
\end{axis}
\end{tikzpicture}

\newpage

\section*{Tasks}
    \lstset{ breaklines=true,
            postbreak=\mbox{\textcolor{red}{$\hookrightarrow$}\space},
            aboveskip=8pt,belowskip=8pt}

{{ range $i, $v := .AuditInfos }}
   \begin{tcolorbox}[ title={{ (strrepl $v.ProcessName "_" "\\_") }},
                      colbacktitle=color{{ $i }}!63!white,
                      colback=color{{ $i }}!37!white,
                      coltitle=black ]
       \small
       \begin{tabular}{rp{0.72\linewidth}}
ID: & {{ $v.ID }} \\
Process: & {{ (strrepl $v.ProcessName "_" "\\_") }} \\
Command: & \begin{lstlisting}
{{ strrepl $v.Command "_" "\\_" }}
\end{lstlisting} \\
Parameters:& {{ range $k, $v := $v.Params }}{{- $k -}}={{- $v -}}{{ end }} \\
Tags: & {{ range $k, $v := $v.Tags }}{{- $k -}}={{- $v -}}{{ end }} \\
Start time:  & {{ timetomillis $v.StartTime }} \\
Finish time: & {{ timetomillis $v.FinishTime }} \\
Execution time: & {{ durtomillis $v.ExecTimeNS }} \\
        \end{tabular}
	\end{tcolorbox}
{{ end }}

\end{document}`
<commit_msg>Put filename of corresponding file in subtitle in audit report<commit_after>package main

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/scipipe/scipipe"
)

// AuditReport is a container for data to be parsed into an audit report, in
// HTML, TeX or other format
type auditReport struct {
	FileName    string
	ScipipeVer  string
	RunTime     time.Duration
	AuditInfos  []*scipipe.AuditInfo
	ColorDef    string
	ChartHeight string
}

var (
	tplFuncs = template.FuncMap{
		"strrepl":        func(subj string, find string, repl string) string { return strings.Replace(subj, find, repl, -1) },
		"sub":            func(val1 int, val2 int) int { return val1 - val2 },
		"timesub":        func(t1 time.Time, t2 time.Time) time.Duration { return t1.Sub(t2) },
		"durtomillis":    func(exact time.Duration) (rounded time.Duration) { return exact.Truncate(1e6 * time.Nanosecond) },
		"timetomillis":   func(exact time.Time) (rounded time.Time) { return exact.Truncate(1e6 * time.Nanosecond) },
		"durtomillisint": func(exact time.Duration) (millis int) { return int(exact.Nanoseconds() / 1000000) },
	}
)

func auditInfoToHTML(inFilePath string, outFilePath string, flatten bool) error {
	ip := scipipe.NewFileIP(strings.Replace(inFilePath, ".audit.json", "", 1))
	auditInfo := ip.AuditInfo()

	outHTML := fmt.Sprintf(headHTMLPattern, ip.Path())
	if flatten {
		auditInfosByID := extractAuditInfosByID(auditInfo)
		auditInfosByStartTime := sortAuditInfosByStartTime(auditInfosByID)
		for _, ai := range auditInfosByStartTime {
			ai.Upstream = nil
			outHTML += formatTaskHTML(ai.ProcessName, ai)
		}
	} else {
		outHTML += formatTaskHTML(ip.Path(), auditInfo)
	}
	outHTML += bottomHTML

	if _, err := os.Stat(outFilePath); os.IsExist(err) {
		return errWrap(err, "File already exists:"+outFilePath)
	}
	outFile, err := os.Create(outFilePath)
	if err != nil {
		return errWrap(err, "Could not create file:"+outFilePath)
	}
	outFile.WriteString(outHTML)
	outFile.Close()
	fmt.Println("Wrote audit HTML file to: " + outFilePath)
	return nil
}

func formatTaskHTML(fileName string, auditInfo *scipipe.AuditInfo) (outHTML string) {
	outHTML = "
\n"
	outHTML += fmt.Sprintf("
%s / %s
\n", auditInfo.ProcessName, auditInfo.ID, auditInfo.ID, auditInfo.ID)
	outHTML += fmt.Sprintf("

%s

\n", auditInfo.Command)

	params := []string{}
	for pname, p := range auditInfo.Params {
		params = append(params, fmt.Sprintf("%s: %s", pname, p))
	}
	outHTML += fmt.Sprintf("
Parameters:	%s
\n", strings.Join(params, ", "))
	tags := []string{}
	for pname, p := range auditInfo.Tags {
		tags = append(tags, fmt.Sprintf("%s: %s", pname, p))
	}
	outHTML += fmt.Sprintf("
Tags:
%v

\n", strings.Join(tags, ", "))

	outHTML += fmt.Sprintf("
Start time:	%s
\n", auditInfo.StartTime.Format(`2006-01-02 15:04:05<span class="greyout">.000 -0700 MST</span>`))
	outHTML += fmt.Sprintf("
Finish time:	%s
\n", auditInfo.FinishTime.Format(`2006-01-02 15:04:05<span class="greyout">.000 -0700 MST</span>`))
	et := auditInfo.ExecTimeNS
	outHTML += fmt.Sprintf("
Execution time:	%s
\n", et.Truncate(time.Millisecond).String())
	//upStreamHTML := ""
	//for filePath, uai := range auditInfo.Upstream {
	//	upStreamHTML += formatTaskHTML(filePath, uai)
	//}
	//if outHTML != "" {
	//	outHTML += "
Upstreams:	" + upStreamHTML + "
\n"
	//}
	outHTML += "
\n"
	return
}

func auditInfoToTeX(inFilePath string, outFilePath string, flatten bool) error {
	outFile, err := os.Create(outFilePath)
	scipipe.CheckWithMsg(err, "Could not create TeX file")

	auditInfo := scipipe.UnmarshalAuditInfoJSONFile(inFilePath)
	auditInfosByID := extractAuditInfosByID(auditInfo)
	auditInfosByStartTime := sortAuditInfosByStartTime(auditInfosByID)

	texTpl := template.New("TeX").Funcs(tplFuncs)
	texTpl, err = texTpl.Parse(texTemplate)
	scipipe.CheckWithMsg(err, "Could not parse TeX template")

	runTime := time.Duration(0)
	for _, auInfo := range auditInfosByStartTime {
		runTime += auInfo.ExecTimeNS
	}

	report := auditReport{
		FileName:    inFilePath,
		ScipipeVer:  scipipe.Version,
		RunTime:     runTime,
		AuditInfos:  auditInfosByStartTime,
		ChartHeight: fmt.Sprintf("%.03f", 1.0+float64(len(auditInfosByStartTime))*0.5),
	}

	palette := palettes[1]
	if len(report.AuditInfos) <= 50 {
		palette = palettes[len(report.AuditInfos)]
	} else {
		palette = palettes[len(report.AuditInfos)%50]
	}
	for i, p := range palette {
		report.ColorDef += fmt.Sprintf("\\definecolor{color%d}{RGB}{%d,%d,%d}\n", i, p.r, p.g, p.b)
	}

	texTpl.Execute(outFile, report)
	fmt.Println("Wrote audit TeX file to: " + outFilePath)
	return nil
}

func extractAuditInfosByID(auditInfo *scipipe.AuditInfo) (auditInfosByID map[string]*scipipe.AuditInfo) {
	auditInfosByID = make(map[string]*scipipe.AuditInfo)
	auditInfosByID[auditInfo.ID] = auditInfo
	for _, ai := range auditInfo.Upstream {
		auditInfosByID = mergeStringAuditInfoMaps(auditInfosByID, extractAuditInfosByID(ai))
	}
	return auditInfosByID
}

func mergeStringAuditInfoMaps(ms ...map[string]*scipipe.AuditInfo) (merged map[string]*scipipe.AuditInfo) {
	merged = make(map[string]*scipipe.AuditInfo)
	for _, m := range ms {
		for k, v := range m {
			merged[k] = v
		}
	}
	return merged
}

func sortAuditInfosByStartTime(auditInfosByID map[string]*scipipe.AuditInfo) []*scipipe.AuditInfo {
	sorted := []*scipipe.AuditInfo{}

	auditInfosByStartTime := map[time.Time]*scipipe.AuditInfo{}
	startTimes := []time.Time{}
	for _, ai := range auditInfosByID {
		auditInfosByStartTime[ai.StartTime] = ai
		startTimes = append(startTimes, ai.StartTime)
	}
	sort.Slice(startTimes, func(i, j int) bool { return startTimes[i].Before(startTimes[j]) })
	for _, t := range startTimes {
		sorted = append(sorted, auditInfosByStartTime[t])
	}
	return sorted
}

const headHTMLPattern = `

<title>Audit info for: %s</title>

`
const bottomHTML = `
`

// LaTeX code from vision.tex:
const texTemplate = `\documentclass[11pt,oneside,openright]{memoir}

\usepackage{tcolorbox}
\usepackage[scaled]{beramono}
\renewcommand*\familydefault{\ttdefault}
\usepackage[T1]{fontenc}
\usepackage{tabularx}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{tikz}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{xcolor}

{{ .ColorDef }}

% from https://tex.stackexchange.com/a/128040/110842
% filter to only get the current row in \pgfplotsinvokeforeach
\pgfplotsset{
    select row/.style={
        x filter/.code={\ifnum\coordindex=#1\else\def\pgfmathresult{}\fi}
    }
}

\pgfplotstableread[col sep=comma]{
start,end,Name,color
{{ $startTime := (index .AuditInfos 0).StartTime }}
{{ range $i, $v := .AuditInfos }}{{ durtomillisint (timesub $v.StartTime $startTime) }},{{ durtomillisint (timesub $v.FinishTime $startTime) }},{{ strrepl .ProcessName "_" "\\_" }},color{{ $i }}
{{ end }}
}\loadedtable
\pgfplotstablegetrowsof{\loadedtable}
\pgfplotsset{compat=1.13}
\pgfmathsetmacro{\tablerows}{int(\pgfplotsretval-1)}

\begin{document}
\pagestyle{plain}
\noindent
\begin{minipage}{\textwidth}
    \vspace{-8em}\hspace{-8em}
    %\includegraphics[width=9em]{images/scipipe_logo_bluegrey.png}
\end{minipage}

\noindent
{\huge\textbf{SciPipe Audit Report}} \\
{\large\textbf{For file: {{ (strrepl (strrepl .FileName ".audit.json" "") "_" "\\_") }}} \\
\vspace{10pt}

    \begin{tcolorbox}[ title=Summary information ]
    \small
\begin{tabular}{rp{0.72\linewidth}}
SciPipe version: & {{ .ScipipeVer }} \\
Start time:  & {{ timetomillis (index .AuditInfos 0).StartTime }} \\
Finish time: & {{ timetomillis (index .AuditInfos (sub (len .AuditInfos) 1)).FinishTime }} \\
Run time: & {{ durtomillis .RunTime }}  \\
\end{tabular}
    \end{tcolorbox}

\setlength{\fboxsep}{0pt}
\noindent

%\hspace{-0.1725\textwidth}\fbox{\includegraphics[width=1.35\textwidth]{images/cawpre.pdf}}

\section*{Execution timeline}

\begin{tikzpicture}
\begin{axis}[
    xbar, xmin=0,
    y axis line style = { opacity = 0 },
    tickwidth         = 0pt,
	width=10cm,
	height={{ .ChartHeight }}cm,
    % next two lines also from https://tex.stackexchange.com/a/128040/110842,
    ytick={0,...,\tablerows},
    yticklabels from table={\loadedtable}{Name},
    xbar stacked,
    bar shift=0pt,
    y dir=reverse,
    xtick={1, 60000, 120000, 180000, 240000, 300000, 600000, 900000, 1200000},
    xticklabels={0, 1 min, 2 min, 3 min, 4 min, 5 min, 10 min, 15 min, 20 min},
    scaled x ticks=false,
]

\pgfplotsinvokeforeach{0,...,\tablerows}{
    % get color from table, commands defined must be individual for each plot
    % because the color is used in \end{axis} and therefore would otherwise
    % use the last definition
    \pgfplotstablegetelem{#1}{color}\of{\loadedtable}
    \expandafter\edef\csname barcolor.#1\endcsname{\pgfplotsretval}
    \addplot+[color=\csname barcolor.#1\endcsname] table [select row=#1, x expr=\thisrow{end}-\thisrow{start}, y expr=#1]{\loadedtable};
}
\end{axis}
\end{tikzpicture}

\newpage

\section*{Tasks}
    \lstset{ breaklines=true,
            postbreak=\mbox{\textcolor{red}{$\hookrightarrow$}\space},
            aboveskip=8pt,belowskip=8pt}

{{ range $i, $v := .AuditInfos }}
   \begin{tcolorbox}[ title={{ (strrepl $v.ProcessName "_" "\\_") }},
                      colbacktitle=color{{ $i }}!63!white,
                      colback=color{{ $i }}!37!white,
                      coltitle=black ]
       \small
       \begin{tabular}{rp{0.72\linewidth}}
ID: & {{ $v.ID }} \\
Process: & {{ (strrepl $v.ProcessName "_" "\\_") }} \\
Command: & \begin{lstlisting}
{{ strrepl $v.Command "_" "\\_" }}
\end{lstlisting} \\
Parameters:& {{ range $k, $v := $v.Params }}{{- $k -}}={{- $v -}}{{ end }} \\
Tags: & {{ range $k, $v := $v.Tags }}{{- $k -}}={{- $v -}}{{ end }} \\
Start time:  & {{ timetomillis $v.StartTime }} \\
Finish time: & {{ timetomillis $v.FinishTime }} \\
Execution time: & {{ durtomillis $v.ExecTimeNS }} \\
        \end{tabular}
	\end{tcolorbox}
{{ end }}

\end{document}`
<|endoftext|>


<commit_before>package apns

import (
	"bytes"
	"crypto/tls"
	"encoding/binary"
	"errors"
	"github.com/zhangpeihao/log"
	"net"
	"time"
)

const (
	MIN_QUEUE_SIZE = 8
)

var (
	LOG_HEADERS  = []string{"Out", "Out_E"}
	logger       *log.Logger
	READ_TIMEOUT = time.Second * 3600
)

var (
	ErrClosed  = errors.New("Closed")
	ErrBlocked = errors.New("Blocked")
	ErrTimeout = errors.New("Timeout")
)

func InitLog(l *log.Logger) {
	logger = l
	logger.Println("InitLog()")
}

type Conn struct {
	c           *tls.Conn
	sendTimeout time.Duration
	exit        bool
}

func Dial(serverAddress string, cert []tls.Certificate,
	sendTimeout time.Duration) (c *Conn, err error) {
	var conn net.Conn
	if conn, err = net.DialTimeout("tcp", serverAddress, sendTimeout); err != nil {
		return
	}
	tlsConn := tls.Client(conn, &tls.Config{
		Certificates:       cert,
		InsecureSkipVerify: true,
	})
	if err = tlsConn.SetWriteDeadline(time.Now().Add(sendTimeout)); err != nil {
		return
	}
	handshakeChan := make(chan bool)
	go func(ch chan<- bool) {
		logger.Debugln("apnd.Dial() Handshake")
		if err = tlsConn.Handshake(); err != nil {
			logger.Debugln("apnd.Dial() Handshake failed")
			ch <- false
			return
		}
		logger.Debugln("apnd.Dial() Handshake success")
		ch <- true
	}(handshakeChan)
	select {
	case b := <-handshakeChan:
		if !b {
			return
		}
	case <-time.After(time.Second * time.Duration(5)):
		logger.Debugln("apnd.Dial() Handshake timeout")
		tlsConn.Close()
		err = ErrTimeout
		return
	}
	c = &Conn{
		c:           tlsConn,
		sendTimeout: sendTimeout,
	}

	go c.readLoop()
	return
}

func (c *Conn) Close() {
	c.exit = true
	c.c.Close()
}

func (c *Conn) readLoop() {
	//	var err error
	if err := c.c.SetReadDeadline(time.Unix(9999999999, 0)); err != nil {
		logger.Add("Out_E", int64(1))
		logger.Warningln("apns.Conn::readLoop() SetReadDeadline err:", err)
		c.Close()
		return
	}
	buf := make([]byte, 6)
	for !c.exit {
		// read response
		if n, err := c.c.Read(buf); err != nil {
			netErr, ok := err.(net.Error)
			if ok && netErr.Temporary() {
				time.Sleep(time.Second)
			} else {
				logger.Add("Out_E", int64(1))
				logger.Debugln("apns.Conn::readLoop() Read err:", err)
				if n > 0 {
					logger.Debugf("APNS read %02X\n", buf)
				}
				break
			}
		} else {
			logger.Debugf("APNS read %02X\n", buf)
		}
	}
	c.Close()
}

func (c *Conn) Send(data []byte) (err error) {
	if c.exit {
		return ErrClosed
	}
	if err = c.c.SetWriteDeadline(time.Now().Add(c.sendTimeout)); err != nil {
		logger.Add("Out_E", int64(1))
		logger.Warningln("apns.Conn::Send() SetWriteDeadline err:", err)
		return
	}
	logger.Debugf("sendLoop() data: % 02X\n", data)
	if _, err = c.c.Write(data); err != nil {
		logger.Add("Out_E", int64(1))
		logger.Warningln("apns.Conn::Send() Write err:", err)
		return
	}

	logger.Add("Out", int64(1))
	return
}

func (c *Conn) SendMessage(deviceToken []byte, message []byte) (err error) {
	buf := new(bytes.Buffer)
	if _, err = buf.Write([]byte{0, 0, 32}); err != nil {
		return
	}
	if _, err = buf.Write(deviceToken); err != nil {
		return
	}
	if err = binary.Write(buf, binary.BigEndian, uint16(len(message))); err != nil {
		return
	}
	if _, err = buf.Write(message); err != nil {
		return
	}
	return c.Send(buf.Bytes())
}
<commit_msg>Append Closed status<commit_after>package apns

import (
	"bytes"
	"crypto/tls"
	"encoding/binary"
	"errors"
	"github.com/zhangpeihao/log"
	"net"
	"time"
)

const (
	MIN_QUEUE_SIZE = 8
)

var (
	LOG_HEADERS  = []string{"Out", "Out_E"}
	logger       *log.Logger
	READ_TIMEOUT = time.Second * 3600
)

var (
	ErrClosed  = errors.New("Closed")
	ErrBlocked = errors.New("Blocked")
	ErrTimeout = errors.New("Timeout")
)

func InitLog(l *log.Logger) {
	logger = l
	logger.Println("InitLog()")
}

type Conn struct {
	c           *tls.Conn
	sendTimeout time.Duration
	exit        bool
}

func Dial(serverAddress string, cert []tls.Certificate,
	sendTimeout time.Duration) (c *Conn, err error) {
	var conn net.Conn
	if conn, err = net.DialTimeout("tcp", serverAddress, sendTimeout); err != nil {
		return
	}
	tlsConn := tls.Client(conn, &tls.Config{
		Certificates:       cert,
		InsecureSkipVerify: true,
	})
	if err = tlsConn.SetWriteDeadline(time.Now().Add(sendTimeout)); err != nil {
		return
	}
	handshakeChan := make(chan bool)
	go func(ch chan<- bool) {
		logger.Debugln("apnd.Dial() Handshake")
		if err = tlsConn.Handshake(); err != nil {
			logger.Debugln("apnd.Dial() Handshake failed")
			ch <- false
			return
		}
		logger.Debugln("apnd.Dial() Handshake success")
		ch <- true
	}(handshakeChan)
	select {
	case b := <-handshakeChan:
		if !b {
			return
		}
	case <-time.After(time.Second * time.Duration(5)):
		logger.Debugln("apnd.Dial() Handshake timeout")
		tlsConn.Close()
		err = ErrTimeout
		return
	}
	c = &Conn{
		c:           tlsConn,
		sendTimeout: sendTimeout,
	}

	go c.readLoop()
	return
}

func (c *Conn) Closed() bool {
	return c.exit
}

func (c *Conn) Close() {
	c.exit = true
	c.c.Close()
}

func (c *Conn) readLoop() {
	//	var err error
	if err := c.c.SetReadDeadline(time.Unix(9999999999, 0)); err != nil {
		logger.Add("Out_E", int64(1))
		logger.Warningln("apns.Conn::readLoop() SetReadDeadline err:", err)
		c.Close()
		return
	}
	buf := make([]byte, 6)
	for !c.exit {
		// read response
		if n, err := c.c.Read(buf); err != nil {
			netErr, ok := err.(net.Error)
			if ok && netErr.Temporary() {
				time.Sleep(time.Second)
			} else {
				logger.Add("Out_E", int64(1))
				logger.Debugln("apns.Conn::readLoop() Read err:", err)
				if n > 0 {
					logger.Debugf("APNS read %02X\n", buf)
				}
				break
			}
		} else {
			logger.Debugf("APNS read %02X\n", buf)
		}
	}
	c.Close()
}

func (c *Conn) Send(data []byte) (err error) {
	if c.exit {
		return ErrClosed
	}
	if err = c.c.SetWriteDeadline(time.Now().Add(c.sendTimeout)); err != nil {
		logger.Add("Out_E", int64(1))
		logger.Warningln("apns.Conn::Send() SetWriteDeadline err:", err)
		return
	}
	logger.Debugf("sendLoop() data: % 02X\n", data)
	if _, err = c.c.Write(data); err != nil {
		logger.Add("Out_E", int64(1))
		logger.Warningln("apns.Conn::Send() Write err:", err)
		return
	}

	logger.Add("Out", int64(1))
	return
}

func (c *Conn) SendMessage(deviceToken []byte, message []byte) (err error) {
	buf := new(bytes.Buffer)
	if _, err = buf.Write([]byte{0, 0, 32}); err != nil {
		return
	}
	if _, err = buf.Write(deviceToken); err != nil {
		return
	}
	if err = binary.Write(buf, binary.BigEndian, uint16(len(message))); err != nil {
		return
	}
	if _, err = buf.Write(message); err != nil {
		return
	}
	return c.Send(buf.Bytes())
}
<|endoftext|>


<commit_before>// Copyright 2017 Jonathan Pincas

// 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 core

import (
	"os"

	"github.com/spf13/cobra"
)

func init() {

	RootCmd.AddCommand(initCmd)
	initCmd.AddCommand(initDBCmd)
	initCmd.AddCommand(initFoldersCmd)

}

// initCmd represents the init command
var initCmd = &cobra.Command{
	Use:   "init",
	Short: "Complete initial setup of database and folder structure",
	Long:  `Performs a complete initialisation of the database and folder structure for EcoSystem`,
	RunE:  initAll,
}

// initDBCmd initialiseds the database
var initDBCmd = &cobra.Command{
	Use:   "db",
	Short: "Perform the database initialisation for built in tables, roles and permissions",
	Long: `Executes the initialisation SQL which sets up the built-in tables, as well
	as creating built-in roles anon,admin, web and server and assigning permissions.
	Tables will not be overwritten if they already exist.`,
	RunE: initDB,
}

// initCmd initialises the folder structure
var initFoldersCmd = &cobra.Command{
	Use:   "folders",
	Short: "Creates EcoSystem folder structure",
	Long: `Performs a complete initialisation of the folder structure for EcoSystem.
	Folders that already exist will not be overwritten.`,
	RunE: initFolders,
}

//initAll
func initAll(cmd *cobra.Command, args []string) error {

	c := AskForConfirmation("This will perform a complete (re)initialisation and may perform overwrites. Do you with to proceed?")

	if c {
		initDB(cmd, args)
		initFolders(cmd, args)
		Log(LogEntry{"CORE.INIT", true, "Successfully completed EcoSystem initialisation"})
		return nil
	}

	Log(LogEntry{"CORE.INIT", false, "Aborted by user"})

	return nil
}

//initDB initialises the built-in database tables, roles and permissions
func initDB(cmd *cobra.Command, args []string) error {

	readConfig()

	//Establish a temporary connection as the super user
	db := SuperUserDBConfig.ReturnDBConnection("")
	defer db.Close()

	//Run initialisation SQL
	var err error
	_, err = db.Exec(SQLToCreateAdminRole)
	_, err = db.Exec(SQLToGrantAdminPermissions) //Do this first so everything created after will have correct admin permissions by default
	_, err = db.Exec(SQLToCreateUUIDExtension)
	_, err = db.Exec(SQLToCreateUsersTable)
	_, err = db.Exec(SQLToCreateFuncToGenerateNewUserID)
	_, err = db.Exec(SQLToCreateTriggerOnNewUserInsert)
	_, err = db.Exec(SQLToCreateServerRole)
	_, err = db.Exec(SQLToCreateAnonRole)
	_, err = db.Exec(SQLToGrantBuiltInPermissions)

	if err != nil {
		LogFatal(LogEntry{"CORE.INIT", false, "Could not complete database setup: " + err.Error()})
	}

	Log(LogEntry{"CORE.INIT", true, "Successfully completed EcoSystem database initialisation"})
	return nil

}

//initFolders initialises the filesystem used by EcoSystem
func initFolders(cmd *cobra.Command, args []string) error {

	readConfig()

	var err error
	err = os.Mkdir("./bundles", os.ModePerm)

	if err != nil {
		Log(LogEntry{"CORE.INIT", false, "Could not complete folder setup: " + err.Error()})
	}

	Log(LogEntry{"CORE.INIT", true, "Successfully completed EcoSystem folder initialisation"})
	return nil
}
<commit_msg>Added noprompt flag to init<commit_after>// Copyright 2017 Jonathan Pincas

// 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 core

import (
	"os"

	"github.com/spf13/cobra"
)

var isNoPrompt bool

func init() {

	RootCmd.AddCommand(initCmd)
	initCmd.AddCommand(initDBCmd)
	initCmd.AddCommand(initFoldersCmd)

	initCmd.Flags().BoolVarP(&isNoPrompt, "noprompt", "n", false, "Don't prompt for confirmation")

}

// initCmd represents the init command
var initCmd = &cobra.Command{
	Use:   "init",
	Short: "Complete initial setup of database and folder structure",
	Long:  `Performs a complete initialisation of the database and folder structure for EcoSystem`,
	RunE:  initAll,
}

// initDBCmd initialiseds the database
var initDBCmd = &cobra.Command{
	Use:   "db",
	Short: "Perform the database initialisation for built in tables, roles and permissions",
	Long: `Executes the initialisation SQL which sets up the built-in tables, as well
	as creating built-in roles anon,admin, web and server and assigning permissions.
	Tables will not be overwritten if they already exist.`,
	RunE: initDB,
}

// initCmd initialises the folder structure
var initFoldersCmd = &cobra.Command{
	Use:   "folders",
	Short: "Creates EcoSystem folder structure",
	Long: `Performs a complete initialisation of the folder structure for EcoSystem.
	Folders that already exist will not be overwritten.`,
	RunE: initFolders,
}

//initAll
func initAll(cmd *cobra.Command, args []string) error {

	//If user has used -noprompt flag then we don't prompt for confirmation
	var proceedWithInit = false
	if isNoPrompt {
		proceedWithInit = true
	} else {
		proceedWithInit = AskForConfirmation("This will perform a complete (re)initialisation and may perform overwrites. Do you with to proceed?")
	}

	if proceedWithInit {
		initDB(cmd, args)
		initFolders(cmd, args)
		Log(LogEntry{"CORE.INIT", true, "Successfully completed EcoSystem initialisation"})
		return nil
	}

	Log(LogEntry{"CORE.INIT", false, "Aborted by user"})

	return nil
}

//initDB initialises the built-in database tables, roles and permissions
func initDB(cmd *cobra.Command, args []string) error {

	readConfig()

	//Establish a temporary connection as the super user
	db := SuperUserDBConfig.ReturnDBConnection("")
	defer db.Close()

	//Run initialisation SQL
	var err error
	_, err = db.Exec(SQLToCreateAdminRole)
	_, err = db.Exec(SQLToGrantAdminPermissions) //Do this first so everything created after will have correct admin permissions by default
	_, err = db.Exec(SQLToCreateUUIDExtension)
	_, err = db.Exec(SQLToCreateUsersTable)
	_, err = db.Exec(SQLToCreateFuncToGenerateNewUserID)
	_, err = db.Exec(SQLToCreateTriggerOnNewUserInsert)
	_, err = db.Exec(SQLToCreateServerRole)
	_, err = db.Exec(SQLToCreateAnonRole)
	_, err = db.Exec(SQLToGrantBuiltInPermissions)

	if err != nil {
		LogFatal(LogEntry{"CORE.INIT", false, "Could not complete database setup: " + err.Error()})
	}

	Log(LogEntry{"CORE.INIT", true, "Successfully completed EcoSystem database initialisation"})
	return nil

}

//initFolders initialises the filesystem used by EcoSystem
func initFolders(cmd *cobra.Command, args []string) error {

	readConfig()

	var err error
	err = os.Mkdir("./bundles", os.ModePerm)

	if err != nil {
		Log(LogEntry{"CORE.INIT", false, "Could not complete folder setup: " + err.Error()})
	}

	Log(LogEntry{"CORE.INIT", true, "Successfully completed EcoSystem folder initialisation"})
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"sync"

	"github.com/thomersch/grandine/lib/cugdf"
	"github.com/thomersch/grandine/lib/spatial"

	"github.com/thomersch/gosmparse"
)

type dataHandler struct {
	cond condition

	ec *elemCache

	nodes    []gosmparse.Node
	nodesMtx sync.Mutex
	ways     []gosmparse.Way
	waysMtx  sync.Mutex
	rels     []gosmparse.Relation
	relsMtx  sync.Mutex
}

func (d *dataHandler) ReadNode(n gosmparse.Node) {
	// TODO: make it possible to specify condition type (node/way/rel)
	if v, ok := n.Tags[d.cond.key]; ok {
		if len(d.cond.value) != 0 && d.cond.value != v {
			return
		}
		d.nodesMtx.Lock()
		d.nodes = append(d.nodes, n)
		d.nodesMtx.Unlock()
	}
}

func (d *dataHandler) ReadWay(w gosmparse.Way) {
	// TODO: make it possible to specify condition type (node/way/rel)
	if v, ok := w.Tags[d.cond.key]; ok {
		if len(d.cond.value) != 0 && d.cond.value != v {
			return
		}
		d.ec.AddNodes(w.NodeIDs...)
		d.ec.setMembers(w.ID, w.NodeIDs)

		d.waysMtx.Lock()
		d.ways = append(d.ways, w)
		d.waysMtx.Unlock()
	}
}

func (d *dataHandler) ReadRelation(r gosmparse.Relation) {
	// TODO: make it possible to specify condition type (node/way/rel)
	if v, ok := r.Tags[d.cond.key]; ok {
		if len(d.cond.value) != 0 && d.cond.value != v {
			return
		}

		d.relsMtx.Lock()
		d.rels = append(d.rels, r)
		d.relsMtx.Unlock()

		for _, memb := range r.Members {
			switch memb.Type {
			case gosmparse.WayType:
				d.ec.AddWay(memb.ID)
			} // TODO: check if relations of nodes/relations are necessary
		}
	}
}

type elemCache struct {
	nodes    map[int64]spatial.Point
	nodesMtx sync.Mutex
	ways     map[int64][]int64
	waysMtx  sync.Mutex
}

func NewElemCache() *elemCache {
	return &elemCache{
		nodes: map[int64]spatial.Point{},
		ways:  map[int64][]int64{},
	}
}

func (d *elemCache) AddNodes(nIDs ...int64) {
	d.nodesMtx.Lock()
	for _, nID := range nIDs {
		d.nodes[nID] = spatial.Point{}
	}
	d.nodesMtx.Unlock()
}

func (d *elemCache) AddWay(wID int64) {
	d.waysMtx.Lock()
	d.ways[wID] = []int64{}
	d.waysMtx.Unlock()
}

func (d *elemCache) SetCoord(nID int64, coord spatial.Point) {
	d.nodesMtx.Lock()
	d.nodes[nID] = coord
	d.nodesMtx.Unlock()
}

func (d *elemCache) setMembers(wID int64, members []int64) {
	d.waysMtx.Lock()
	d.ways[wID] = members
	d.waysMtx.Unlock()
}

func (d *elemCache) ReadWay(w gosmparse.Way) {
	d.waysMtx.Lock()
	_, ok := d.ways[w.ID]
	d.waysMtx.Unlock()
	if ok {
		d.setMembers(w.ID, w.NodeIDs)
		d.AddNodes(w.NodeIDs...)
	}
}

func (d *elemCache) Line(wID int64) spatial.Line {
	// check if mutex is needed
	membs, ok := d.ways[wID]
	if !ok {
		log.Fatalf("missing referenced way: %v", wID)
	}

	var l spatial.Line
	for _, memb := range membs {
		l = append(l, d.nodes[memb])
	}
	return l
}

// Interface enforces this. Probably I should change the behavior.
func (d *elemCache) ReadNode(n gosmparse.Node)         {}
func (d *elemCache) ReadRelation(r gosmparse.Relation) {}

type nodeCollector struct {
	ec *elemCache
}

func (d *nodeCollector) ReadNode(n gosmparse.Node) {
	d.ec.SetCoord(n.ID, spatial.Point{float64(n.Lon), float64(n.Lat)})
}
func (d *nodeCollector) ReadWay(w gosmparse.Way)           {}
func (d *nodeCollector) ReadRelation(r gosmparse.Relation) {}

// const (
// 	typAny      = 0
// 	typNode     = 1
// 	typWay      = 2
// 	typRelation = 3
// )

type condition struct {
	key   string
	value string
}

func main() {
	cond := condition{"building", ""}

	source := flag.String("src", "osm.pbf", "")
	outfile := flag.String("out", "osm.cugdf", "")
	flag.Parse()

	f, err := os.Open(*source)
	if err != nil {
		log.Fatal(err)
	}
	dec := gosmparse.NewDecoder(f)

	// First pass
	ec := NewElemCache()
	dh := dataHandler{
		cond: cond,
		ec:   ec,
	}
	log.Println("Starting 3 step parsing")
	log.Println("Reading data (1/3)...")
	err = dec.Parse(&dh)
	if err != nil {
		log.Fatal(err)
	}

	_, err = f.Seek(0, 0) // jumps to beginning of file
	if err != nil {
		log.Fatal(err)
	}

	// Second pass
	log.Println("Collecting nodes (2/3)...")
	err = dec.Parse(ec)
	if err != nil {
		log.Fatal(err)
	}

	_, err = f.Seek(0, 0)
	if err != nil {
		log.Fatal(err)
	}

	// Third pass
	log.Println("Resolving dependent objects (3/3)...")
	rc := nodeCollector{
		ec: ec,
	}
	err = dec.Parse(&rc)
	if err != nil {
		log.Fatal(err)
	}

	var fc []spatial.Feature

	log.Println("Parsing completed.")

	log.Println("Collecting points...")
	for _, pt := range dh.nodes {
		props := map[string]interface{}{}
		for k, v := range pt.Tags {
			props[k] = v
		}
		fc = append(fc, spatial.Feature{
			Props:    props,
			Geometry: spatial.MustNewGeom(spatial.Point{float64(pt.Lon), float64(pt.Lat)}),
		})
	}

	log.Println("Assembling ways...")
	// TODO: auto-detect if linestring or polygon, based on tags
	for _, wy := range dh.ways {
		props := map[string]interface{}{}
		for k, v := range wy.Tags {
			props[k] = v
		}
		fc = append(fc, spatial.Feature{
			Props:    props,
			Geometry: spatial.MustNewGeom(ec.Line(wy.ID)),
		})
	}

	log.Println("Assembling relations...")
	for _, rl := range dh.rels {
		if v, ok := rl.Tags["type"]; !ok || v != "multipolygon" {
			continue
		}
		var poly spatial.Polygon

		for _, memb := range rl.Members {
			if memb.Role == "outer" {
				if len(poly) != 0 {
					// TODO: allow polygons with multiple outer rings and split them
					break
				}
				poly = append(poly, ec.Line(memb.ID))
			} else if memb.Role == "inner" {
				poly = append(poly, ec.Line(memb.ID))
			}

		}
	}

	log.Println("Writing out")
	of, err := os.Create(*outfile)
	if err != nil {
		log.Fatal(err)
	}
	err = cugdf.Marshal(fc, of)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>cmd/spatialize: ensure correct winding order<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"sync"

	"github.com/thomersch/grandine/lib/cugdf"
	"github.com/thomersch/grandine/lib/spatial"

	"github.com/thomersch/gosmparse"
)

type dataHandler struct {
	cond condition

	ec *elemCache

	nodes    []gosmparse.Node
	nodesMtx sync.Mutex
	ways     []gosmparse.Way
	waysMtx  sync.Mutex
	rels     []gosmparse.Relation
	relsMtx  sync.Mutex
}

func (d *dataHandler) ReadNode(n gosmparse.Node) {
	// TODO: make it possible to specify condition type (node/way/rel)
	if v, ok := n.Tags[d.cond.key]; ok {
		if len(d.cond.value) != 0 && d.cond.value != v {
			return
		}
		d.nodesMtx.Lock()
		d.nodes = append(d.nodes, n)
		d.nodesMtx.Unlock()
	}
}

func (d *dataHandler) ReadWay(w gosmparse.Way) {
	// TODO: make it possible to specify condition type (node/way/rel)
	if v, ok := w.Tags[d.cond.key]; ok {
		if len(d.cond.value) != 0 && d.cond.value != v {
			return
		}
		d.ec.AddNodes(w.NodeIDs...)
		d.ec.setMembers(w.ID, w.NodeIDs)

		d.waysMtx.Lock()
		d.ways = append(d.ways, w)
		d.waysMtx.Unlock()
	}
}

func (d *dataHandler) ReadRelation(r gosmparse.Relation) {
	// TODO: make it possible to specify condition type (node/way/rel)
	if v, ok := r.Tags[d.cond.key]; ok {
		if len(d.cond.value) != 0 && d.cond.value != v {
			return
		}

		d.relsMtx.Lock()
		d.rels = append(d.rels, r)
		d.relsMtx.Unlock()

		for _, memb := range r.Members {
			switch memb.Type {
			case gosmparse.WayType:
				d.ec.AddWay(memb.ID)
			} // TODO: check if relations of nodes/relations are necessary
		}
	}
}

type elemCache struct {
	nodes    map[int64]spatial.Point
	nodesMtx sync.Mutex
	ways     map[int64][]int64
	waysMtx  sync.Mutex
}

func NewElemCache() *elemCache {
	return &elemCache{
		nodes: map[int64]spatial.Point{},
		ways:  map[int64][]int64{},
	}
}

func (d *elemCache) AddNodes(nIDs ...int64) {
	d.nodesMtx.Lock()
	for _, nID := range nIDs {
		d.nodes[nID] = spatial.Point{}
	}
	d.nodesMtx.Unlock()
}

func (d *elemCache) AddWay(wID int64) {
	d.waysMtx.Lock()
	d.ways[wID] = []int64{}
	d.waysMtx.Unlock()
}

func (d *elemCache) SetCoord(nID int64, coord spatial.Point) {
	d.nodesMtx.Lock()
	d.nodes[nID] = coord
	d.nodesMtx.Unlock()
}

func (d *elemCache) setMembers(wID int64, members []int64) {
	d.waysMtx.Lock()
	d.ways[wID] = members
	d.waysMtx.Unlock()
}

func (d *elemCache) ReadWay(w gosmparse.Way) {
	d.waysMtx.Lock()
	_, ok := d.ways[w.ID]
	d.waysMtx.Unlock()
	if ok {
		d.setMembers(w.ID, w.NodeIDs)
		d.AddNodes(w.NodeIDs...)
	}
}

func (d *elemCache) Line(wID int64) spatial.Line {
	// check if mutex is needed
	membs, ok := d.ways[wID]
	if !ok {
		log.Fatalf("missing referenced way: %v", wID)
	}

	var l spatial.Line
	for _, memb := range membs {
		l = append(l, d.nodes[memb])
	}
	return l
}

// Interface enforces this. Probably I should change the behavior.
func (d *elemCache) ReadNode(n gosmparse.Node)         {}
func (d *elemCache) ReadRelation(r gosmparse.Relation) {}

type nodeCollector struct {
	ec *elemCache
}

func (d *nodeCollector) ReadNode(n gosmparse.Node) {
	d.ec.SetCoord(n.ID, spatial.Point{float64(n.Lon), float64(n.Lat)})
}
func (d *nodeCollector) ReadWay(w gosmparse.Way)           {}
func (d *nodeCollector) ReadRelation(r gosmparse.Relation) {}

// const (
// 	typAny      = 0
// 	typNode     = 1
// 	typWay      = 2
// 	typRelation = 3
// )

type condition struct {
	key   string
	value string
}

func main() {
	cond := condition{"building", ""}

	source := flag.String("src", "osm.pbf", "")
	outfile := flag.String("out", "osm.cugdf", "")
	flag.Parse()

	f, err := os.Open(*source)
	if err != nil {
		log.Fatal(err)
	}
	dec := gosmparse.NewDecoder(f)

	// First pass
	ec := NewElemCache()
	dh := dataHandler{
		cond: cond,
		ec:   ec,
	}
	log.Println("Starting 3 step parsing")
	log.Println("Reading data (1/3)...")
	err = dec.Parse(&dh)
	if err != nil {
		log.Fatal(err)
	}

	_, err = f.Seek(0, 0) // jumps to beginning of file
	if err != nil {
		log.Fatal(err)
	}

	// Second pass
	log.Println("Collecting nodes (2/3)...")
	err = dec.Parse(ec)
	if err != nil {
		log.Fatal(err)
	}

	_, err = f.Seek(0, 0)
	if err != nil {
		log.Fatal(err)
	}

	// Third pass
	log.Println("Resolving dependent objects (3/3)...")
	rc := nodeCollector{
		ec: ec,
	}
	err = dec.Parse(&rc)
	if err != nil {
		log.Fatal(err)
	}

	var fc []spatial.Feature

	log.Println("Parsing completed.")

	log.Println("Collecting points...")
	for _, pt := range dh.nodes {
		props := map[string]interface{}{}
		for k, v := range pt.Tags {
			props[k] = v
		}
		fc = append(fc, spatial.Feature{
			Props:    props,
			Geometry: spatial.MustNewGeom(spatial.Point{float64(pt.Lon), float64(pt.Lat)}),
		})
	}

	log.Println("Assembling ways...")
	// TODO: auto-detect if linestring or polygon, based on tags
	for _, wy := range dh.ways {
		props := map[string]interface{}{}
		for k, v := range wy.Tags {
			props[k] = v
		}
		ln := ec.Line(wy.ID)
		if !ln.Clockwise() {
			ln.Reverse()
		}
		fc = append(fc, spatial.Feature{
			Props:    props,
			Geometry: spatial.MustNewGeom(ln),
		})
	}

	log.Println("Assembling relations...")
	for _, rl := range dh.rels {
		if v, ok := rl.Tags["type"]; !ok || v != "multipolygon" {
			continue
		}
		var poly spatial.Polygon

		for _, memb := range rl.Members {
			if memb.Role == "outer" || memb.Role == "inner" {
				ring := ec.Line(memb.ID)
				if memb.Role == "outer" {
					if !ring.Clockwise() {
						ring.Reverse()
					}
				}
				if memb.Role == "inner" {
					if ring.Clockwise() {
						ring.Reverse()
					}
				}
				poly = append(poly, ring)
			}
		}
	}

	log.Println("Writing out")
	of, err := os.Create(*outfile)
	if err != nil {
		log.Fatal(err)
	}
	err = cugdf.Marshal(fc, of)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


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

package log

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/inconshreveable/log15"
)

// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type FileLogWriter struct {
	mw *MuxWriter

	Format           log15.Format
	Filename         string
	Maxlines         int
	maxlinesCurlines int

	// Rotate at size
	Maxsize        int
	maxsizeCursize int

	// Rotate daily
	Daily         bool
	Maxdays       int64
	dailyOpendate int

	Rotate    bool
	startLock sync.Mutex
}

// an *os.File writer with locker.
type MuxWriter struct {
	sync.Mutex
	fd *os.File
}

// write to os.File.
func (l *MuxWriter) Write(b []byte) (int, error) {
	l.Lock()
	defer l.Unlock()
	return l.fd.Write(b)
}

// set os.File in writer.
func (l *MuxWriter) SetFd(fd *os.File) {
	if l.fd != nil {
		l.fd.Close()
	}
	l.fd = fd
}

// create a FileLogWriter returning as LoggerInterface.
func NewFileWriter() *FileLogWriter {
	w := &FileLogWriter{
		Filename: "",
		Format:   log15.LogfmtFormat(),
		Maxlines: 1000000,
		Maxsize:  1 << 28, // 256 MB
		Daily:    true,
		Maxdays:  7,
		Rotate:   true,
	}
	// use MuxWriter instead direct use os.File for lock write when rotate
	w.mw = new(MuxWriter)
	return w
}

func (w *FileLogWriter) Log(r *log15.Record) error {
	data := w.Format.Format(r)
	w.docheck(len(data))
	_, err := w.mw.Write(data)
	return err
}

func (w *FileLogWriter) Init() error {
	if len(w.Filename) == 0 {
		return errors.New("config must have filename")
	}
	return w.StartLogger()
}

// start file logger. create log file and set to locker-inside file writer.
func (w *FileLogWriter) StartLogger() error {
	fd, err := w.createLogFile()
	if err != nil {
		return err
	}
	w.mw.SetFd(fd)
	return w.initFd()
}

func (w *FileLogWriter) docheck(size int) {
	w.startLock.Lock()
	defer w.startLock.Unlock()
	if w.Rotate && ((w.Maxlines > 0 && w.maxlinesCurlines >= w.Maxlines) ||
		(w.Maxsize > 0 && w.maxsizeCursize >= w.Maxsize) ||
		(w.Daily && time.Now().Day() != w.dailyOpendate)) {
		if err := w.DoRotate(); err != nil {
			fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
			return
		}
	}
	w.maxlinesCurlines++
	w.maxsizeCursize += size
}

func (w *FileLogWriter) createLogFile() (*os.File, error) {
	// Open the log file
	return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
}

func (w *FileLogWriter) lineCounter() (int, error) {
	r, err := os.OpenFile(w.Filename, os.O_RDONLY, 0644)
	if err != nil {
		return 0, fmt.Errorf("lineCounter Open File : %s", err)
	}
	buf := make([]byte, 32*1024)
	count := 0

	for {
		c, err := r.Read(buf)
		count += bytes.Count(buf[:c], []byte{'\n'})
		switch {
		case errors.Is(err, io.EOF):
			if err := r.Close(); err != nil {
				return count, err
			}
			return count, nil
		case err != nil:
			return count, err
		}
	}
}

func (w *FileLogWriter) initFd() error {
	fd := w.mw.fd
	finfo, err := fd.Stat()
	if err != nil {
		return fmt.Errorf("get stat: %s", err)
	}
	w.maxsizeCursize = int(finfo.Size())
	w.dailyOpendate = time.Now().Day()
	if finfo.Size() > 0 {
		count, err := w.lineCounter()
		if err != nil {
			return err
		}
		w.maxlinesCurlines = count
	} else {
		w.maxlinesCurlines = 0
	}
	return nil
}

// DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error {
	_, err := os.Lstat(w.Filename)
	if err == nil { // file exists
		// Find the next available number
		num := 1
		fname := ""
		for ; err == nil && num <= 999; num++ {
			fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
			_, err = os.Lstat(fname)
		}
		// return error if the last file checked still existed
		if err == nil {
			return fmt.Errorf("rotate: cannot find free log number to rename %s", w.Filename)
		}

		// block Logger's io.Writer
		w.mw.Lock()
		defer w.mw.Unlock()

		fd := w.mw.fd
		fd.Close()

		// close fd before rename
		// Rename the file to its newfound home
		if err = os.Rename(w.Filename, fname); err != nil {
			return fmt.Errorf("rotate: %s", err)
		}

		// re-start logger
		if err = w.StartLogger(); err != nil {
			return fmt.Errorf("rotate StartLogger: %s", err)
		}

		go w.deleteOldLog()
	}

	return nil
}

func (w *FileLogWriter) deleteOldLog() {
	dir := filepath.Dir(w.Filename)
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
		defer func() {
			if r := recover(); r != nil {
				returnErr = fmt.Errorf("unable to delete old log '%s', error: %+v", path, r)
			}
		}()

		if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) &&
			strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
			returnErr = os.Remove(path)
			return
		}
		return
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
	}
}

// destroy file logger, close file writer.
func (w *FileLogWriter) Close() {
	w.mw.fd.Close()
}

// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func (w *FileLogWriter) Flush() {
	if err := w.mw.fd.Sync(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
	}
}

// Reload file logger
func (w *FileLogWriter) Reload() {
	// block Logger's io.Writer
	w.mw.Lock()
	defer w.mw.Unlock()

	// Close
	fd := w.mw.fd
	fd.Close()

	// Open again
	err := w.StartLogger()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Reload StartLogger: %s\n", err)
	}
}
<commit_msg>Log: Use os.Open to open file for reading (#29483)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package log

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/inconshreveable/log15"
)

// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type FileLogWriter struct {
	mw *MuxWriter

	Format           log15.Format
	Filename         string
	Maxlines         int
	maxlinesCurlines int

	// Rotate at size
	Maxsize        int
	maxsizeCursize int

	// Rotate daily
	Daily         bool
	Maxdays       int64
	dailyOpendate int

	Rotate    bool
	startLock sync.Mutex
}

// an *os.File writer with locker.
type MuxWriter struct {
	sync.Mutex
	fd *os.File
}

// write to os.File.
func (l *MuxWriter) Write(b []byte) (int, error) {
	l.Lock()
	defer l.Unlock()
	return l.fd.Write(b)
}

// set os.File in writer.
func (l *MuxWriter) SetFd(fd *os.File) {
	if l.fd != nil {
		l.fd.Close()
	}
	l.fd = fd
}

// create a FileLogWriter returning as LoggerInterface.
func NewFileWriter() *FileLogWriter {
	w := &FileLogWriter{
		Filename: "",
		Format:   log15.LogfmtFormat(),
		Maxlines: 1000000,
		Maxsize:  1 << 28, // 256 MB
		Daily:    true,
		Maxdays:  7,
		Rotate:   true,
	}
	// use MuxWriter instead direct use os.File for lock write when rotate
	w.mw = new(MuxWriter)
	return w
}

func (w *FileLogWriter) Log(r *log15.Record) error {
	data := w.Format.Format(r)
	w.docheck(len(data))
	_, err := w.mw.Write(data)
	return err
}

func (w *FileLogWriter) Init() error {
	if len(w.Filename) == 0 {
		return errors.New("config must have filename")
	}
	return w.StartLogger()
}

// start file logger. create log file and set to locker-inside file writer.
func (w *FileLogWriter) StartLogger() error {
	fd, err := w.createLogFile()
	if err != nil {
		return err
	}
	w.mw.SetFd(fd)
	return w.initFd()
}

func (w *FileLogWriter) docheck(size int) {
	w.startLock.Lock()
	defer w.startLock.Unlock()
	if w.Rotate && ((w.Maxlines > 0 && w.maxlinesCurlines >= w.Maxlines) ||
		(w.Maxsize > 0 && w.maxsizeCursize >= w.Maxsize) ||
		(w.Daily && time.Now().Day() != w.dailyOpendate)) {
		if err := w.DoRotate(); err != nil {
			fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
			return
		}
	}
	w.maxlinesCurlines++
	w.maxsizeCursize += size
}

func (w *FileLogWriter) createLogFile() (*os.File, error) {
	// Open the log file
	return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
}

func (w *FileLogWriter) lineCounter() (int, error) {
	r, err := os.Open(w.Filename)
	if err != nil {
		return 0, fmt.Errorf("failed to open file %q: %w", w.Filename, err)
	}

	buf := make([]byte, 32*1024)
	count := 0
	for {
		c, err := r.Read(buf)
		count += bytes.Count(buf[:c], []byte{'\n'})
		switch {
		case errors.Is(err, io.EOF):
			if err := r.Close(); err != nil {
				return count, err
			}
			return count, nil
		case err != nil:
			return count, err
		}
	}
}

func (w *FileLogWriter) initFd() error {
	fd := w.mw.fd
	finfo, err := fd.Stat()
	if err != nil {
		return fmt.Errorf("get stat: %s", err)
	}
	w.maxsizeCursize = int(finfo.Size())
	w.dailyOpendate = time.Now().Day()
	if finfo.Size() > 0 {
		count, err := w.lineCounter()
		if err != nil {
			return err
		}
		w.maxlinesCurlines = count
	} else {
		w.maxlinesCurlines = 0
	}
	return nil
}

// DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error {
	_, err := os.Lstat(w.Filename)
	if err == nil { // file exists
		// Find the next available number
		num := 1
		fname := ""
		for ; err == nil && num <= 999; num++ {
			fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
			_, err = os.Lstat(fname)
		}
		// return error if the last file checked still existed
		if err == nil {
			return fmt.Errorf("rotate: cannot find free log number to rename %s", w.Filename)
		}

		// block Logger's io.Writer
		w.mw.Lock()
		defer w.mw.Unlock()

		fd := w.mw.fd
		fd.Close()

		// close fd before rename
		// Rename the file to its newfound home
		if err = os.Rename(w.Filename, fname); err != nil {
			return fmt.Errorf("rotate: %s", err)
		}

		// re-start logger
		if err = w.StartLogger(); err != nil {
			return fmt.Errorf("rotate StartLogger: %s", err)
		}

		go w.deleteOldLog()
	}

	return nil
}

func (w *FileLogWriter) deleteOldLog() {
	dir := filepath.Dir(w.Filename)
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
		defer func() {
			if r := recover(); r != nil {
				returnErr = fmt.Errorf("unable to delete old log '%s', error: %+v", path, r)
			}
		}()

		if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) &&
			strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
			returnErr = os.Remove(path)
			return
		}
		return
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
	}
}

// destroy file logger, close file writer.
func (w *FileLogWriter) Close() {
	w.mw.fd.Close()
}

// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func (w *FileLogWriter) Flush() {
	if err := w.mw.fd.Sync(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
	}
}

// Reload file logger
func (w *FileLogWriter) Reload() {
	// block Logger's io.Writer
	w.mw.Lock()
	defer w.mw.Unlock()

	// Close
	fd := w.mw.fd
	fd.Close()

	// Open again
	err := w.StartLogger()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Reload StartLogger: %s\n", err)
	}
}
<|endoftext|>


<commit_before>package nasa

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"time"
)

var nasaKey = os.Getenv("NASAKEY")

func init() {
	if nasaKey == "" {
		nasaKey = "DEMO_KEY"
	}
}

// APODEndpoint is the NASA API APOD endpoint
const APODEndpoint = "https://api.nasa.gov/planetary/apod"

// Image defines the structure of NASA images
type Image struct {
	Date        string `json:"date"`
	Title       string `json:"title"`
	URL         string `json:"url"`
	HDURL       string `json:"hdurl"`
	Explanation string `json:"explanation"`

	ApodDate time.Time `json:",omitempty"`
}

func (ni Image) String() string {
	return fmt.Sprintf(`Title: %s
Date: %s
Image: %s
HD Image: %s
About:
%s
`, ni.Title, ni.Date, ni.URL, ni.HDURL, ni.Explanation)
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

// RandomAPOD returns an Astronomy Picture of the Day based on a random date
// Picks any image shared between the last 2 years
func RandomAPOD() (*Image, error) {
	days := 2 * 365 // Any day in last 2 years
	randDaysOld := time.Duration(rand.Intn(days))
	t := time.Now().Add(-(time.Hour * 24 * randDaysOld))
	return ApodImage(t)
}

// ApodImage returns the NASA Astronomy Picture of the Day
func ApodImage(t time.Time) (*Image, error) {
	if t.After(time.Now()) {
		t = time.Now()
	}
	date := t.Format("2006-01-02")
	u, err := url.Parse(APODEndpoint)
	if err != nil {
		return nil, err
	}
	q := u.Query()
	q.Set("date", date)
	q.Add("api_key", nasaKey)
	u.RawQuery = q.Encode()
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{Timeout: time.Second * 30}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("unable to connect to NASA API, %v", err)
	}
	defer func() { _ = resp.Body.Close() }()
	dat, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	_ = resp.Body.Close()
	var ni Image
	err = json.Unmarshal(dat, &ni)
	if err != nil {
		return nil, err
	}
	if t, err := time.Parse("2006-01-02", ni.Date); err == nil {
		ni.ApodDate = t
	}
	return &ni, nil
}
<commit_msg>Check for NASA API downtime :(<commit_after>package nasa

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"time"
)

var nasaKey = os.Getenv("NASAKEY")

func init() {
	if nasaKey == "" {
		nasaKey = "DEMO_KEY"
	}
}

// APODEndpoint is the NASA API APOD endpoint
const APODEndpoint = "https://api.nasa.gov/planetary/apod"

// Image defines the structure of NASA images
type Image struct {
	Date        string `json:"date"`
	Title       string `json:"title"`
	URL         string `json:"url"`
	HDURL       string `json:"hdurl"`
	Explanation string `json:"explanation"`

	ApodDate time.Time `json:",omitempty"`
}

func (ni Image) String() string {
	return fmt.Sprintf(`Title: %s
Date: %s
Image: %s
HD Image: %s
About:
%s
`, ni.Title, ni.Date, ni.URL, ni.HDURL, ni.Explanation)
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

// RandomAPOD returns an Astronomy Picture of the Day based on a random date
// Picks any image shared between the last 2 years
func RandomAPOD() (*Image, error) {
	days := 2 * 365 // Any day in last 2 years
	randDaysOld := time.Duration(rand.Intn(days))
	t := time.Now().Add(-(time.Hour * 24 * randDaysOld))
	return ApodImage(t)
}

// ApodImage returns the NASA Astronomy Picture of the Day
func ApodImage(t time.Time) (*Image, error) {
	if t.After(time.Now()) {
		t = time.Now()
	}
	date := t.Format("2006-01-02")
	u, err := url.Parse(APODEndpoint)
	if err != nil {
		return nil, err
	}
	q := u.Query()
	q.Set("date", date)
	q.Add("api_key", nasaKey)
	u.RawQuery = q.Encode()
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	client := &http.Client{Timeout: time.Second * 30}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("unable to connect to NASA API, %v", err)
	}
	defer func() { _ = resp.Body.Close() }()
	dat, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	_ = resp.Body.Close()
	var ni Image
	err = json.Unmarshal(dat, &ni)
	if err != nil {
		return nil, err
	}
	if ni.URL == "" && ni.HDURL == "" {
		return nil, errors.New("NASA APOD API is returned an invalid response, may be down temporarily")
	}
	if t, err := time.Parse("2006-01-02", ni.Date); err == nil {
		ni.ApodDate = t
	}
	return &ni, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// Package watcher provides a way of watching for filesystem events and
// notifying observers when they occur.
package watcher

type OpType int

const (
	Create OpType = iota
	Update
	Delete
)

// Event is a generalisation of events sent from the watcher to its listeners.
type Event struct {
	Op       OpType
	Pathname string
}

// Watcher describes an interface for filesystem watching.
type Watcher interface {
	Add(name string, handle int) error
	Close() error
	Remove(name string) error
	Events() (handle int, ch <-chan Event)
}
<commit_msg>Change the zero OpType to not be valid.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// Package watcher provides a way of watching for filesystem events and
// notifying observers when they occur.
package watcher

type OpType int

const (
	_ OpType = iota
	Create
	Update
	Delete
)

// Event is a generalisation of events sent from the watcher to its listeners.
type Event struct {
	Op       OpType
	Pathname string
}

// Watcher describes an interface for filesystem watching.
type Watcher interface {
	Add(name string, handle int) error
	Close() error
	Remove(name string) error
	Events() (handle int, ch <-chan Event)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chromium 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 main

import (
	"fmt"

	"chromium.googlesource.com/infra/swarming/client-go/swarming"
	"github.com/kr/pretty"
	"github.com/maruel/subcommands"
)

var cmdRequestShow = &subcommands.Command{
	UsageLine: "request-show <task_id>",
	ShortDesc: "returns properties of a request",
	LongDesc:  "Returns the properties, what, when, by who, about a request on the Swarming server.",
	CommandRun: func() subcommands.CommandRun {
		r := &requestShowRun{}
		r.Init()
		return r
	},
}

type requestShowRun struct {
	commonFlags
}

func (c *requestShowRun) main(a subcommands.Application, taskid string) error {
	if err := c.Parse(a); err != nil {
		return err
	}
	s, err := swarming.NewSwarming(c.serverURL)
	if err != nil {
		return err
	}
	r, err := s.FetchRequest(swarming.TaskID(taskid))
	if err != nil {
		return fmt.Errorf("failed to load task %s: %s", taskid, err)
	}
	_ = pretty.Println(r)
	return err
}

func (c *requestShowRun) Run(a subcommands.Application, args []string) int {
	if len(args) != 1 {
		fmt.Fprintf(a.GetErr(), "%s: Must only provide a task id.\n", a.GetName())
		return 1
	}
	if err := c.main(a, args[0]); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	return 0
}
<commit_msg>Fix compile error.<commit_after>// Copyright 2015 The Chromium 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 main

import (
	"fmt"

	"chromium.googlesource.com/infra/swarming/client-go/swarming"
	"github.com/kr/pretty"
	"github.com/maruel/subcommands"
)

var cmdRequestShow = &subcommands.Command{
	UsageLine: "request-show <task_id>",
	ShortDesc: "returns properties of a request",
	LongDesc:  "Returns the properties, what, when, by who, about a request on the Swarming server.",
	CommandRun: func() subcommands.CommandRun {
		r := &requestShowRun{}
		r.Init()
		return r
	},
}

type requestShowRun struct {
	commonFlags
}

func (c *requestShowRun) main(a subcommands.Application, taskid string) error {
	if err := c.Parse(a); err != nil {
		return err
	}
	s, err := swarming.NewSwarming(c.serverURL)
	if err != nil {
		return err
	}
	r, err := s.FetchRequest(swarming.TaskID(taskid))
	if err != nil {
		return fmt.Errorf("failed to load task %s: %s", taskid, err)
	}
	_, _ = pretty.Println(r)
	return err
}

func (c *requestShowRun) Run(a subcommands.Application, args []string) int {
	if len(args) != 1 {
		fmt.Fprintf(a.GetErr(), "%s: Must only provide a task id.\n", a.GetName())
		return 1
	}
	if err := c.main(a, args[0]); err != nil {
		fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
		return 1
	}
	return 0
}
<|endoftext|>


<commit_before>package kato

//-----------------------------------------------------------------------------
// Package factored import statement:
//-----------------------------------------------------------------------------

import (

	// Stdlib:
	"encoding/json"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

//-----------------------------------------------------------------------------
// WaitChan stuff:
//-----------------------------------------------------------------------------

// WaitChan is used to handle errors that occur in some goroutines.
type WaitChan struct {
	WaitGrp sync.WaitGroup
	ErrChan chan error
	EndChan chan bool
}

// NewWaitChan initializes a WaitChan struct.
func NewWaitChan(len int) *WaitChan {
	wch := new(WaitChan)
	wch.WaitGrp.Add(len)
	wch.ErrChan = make(chan error, 1)
	wch.EndChan = make(chan bool, 1)
	return wch
}

// WaitErr waits for any error or for all go routines to finish.
func (wch *WaitChan) WaitErr() error {

	// Put the wait group in a go routine:
	go func() {
		wch.WaitGrp.Wait()
		wch.EndChan <- true
	}()

	// This select will block:
	select {
	case <-wch.EndChan:
		return nil
	case err := <-wch.ErrChan:
		return err
	}
}

//-----------------------------------------------------------------------------
// func: DumpState
//-----------------------------------------------------------------------------

// DumpState serializes the given state as a clusterID JSON file.
func DumpState(s interface{}, clusterID string) error {

	// Marshal the data:
	data, err := json.MarshalIndent(s, "", "  ")
	if err != nil {
		return err
	}

	// Create the state directory:
	path := os.Getenv("HOME") + "/.kato"
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(path, 0700)
			if err != nil {
				return err
			}
		}
	}

	// Write the state file:
	err = ioutil.WriteFile(path+"/"+clusterID+".json", data, 0600)
	if err != nil {
		return err
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: ReadState
//-----------------------------------------------------------------------------

// ReadState reads the current ClusterID state file.
func ReadState(clusterID string) ([]byte, error) {

	// Read data from state file:
	stateFile := os.Getenv("HOME") + "/.kato/" + clusterID + ".json"
	raw, err := ioutil.ReadFile(stateFile)
	if err != nil {
		return nil, err
	}

	return raw, nil
}

//-----------------------------------------------------------------------------
// func: CountNodes
//-----------------------------------------------------------------------------

// CountNodes returns the count of <role> nodes defined in <quads>.
func CountNodes(quads []string, role string) (count int) {

	// Default to zero:
	count = 0

	// Get the role count:
	for _, q := range quads {
		if strings.Contains(q, role) {
			s := strings.Split(q, ":")
			count, _ = strconv.Atoi(s[0])
			break
		}
	}

	return
}

//-----------------------------------------------------------------------------
// func: CreateDNSZones
//-----------------------------------------------------------------------------

// CreateDNSZones creates (int|ext).<domain> zones using <provider>.
func CreateDNSZones(wch *WaitChan, provider, apiKey, domain string) {

	// Decrement:
	defer wch.WaitGrp.Done()

	// Forge the zone command:
	cmd := exec.Command("katoctl", provider,
		"--api-key", apiKey, "zone", "add",
		domain, "int."+domain, "ext."+domain)

	// Execute the zone command:
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		wch.ErrChan <- err
	}
}

//-----------------------------------------------------------------------------
// func: ExecutePipeline
//-----------------------------------------------------------------------------

// ExecutePipeline takes two commands and pipes the stdout of the first one
// into the stdin of the second one. Returns the output as []byte.
func ExecutePipeline(cmd1, cmd2 *exec.Cmd) ([]byte, error) {

	var err error

	// Adjust the stderr:
	cmd1.Stderr = os.Stderr
	cmd2.Stderr = os.Stderr

	// Connect both commands:
	cmd2.Stdin, err = cmd1.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Get cmd2 stdout:
	stdout, err := cmd2.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Execute the pipeline:
	if err = cmd2.Start(); err != nil {
		return nil, err
	}
	if err = cmd1.Run(); err != nil {
		return nil, err
	}

	// Read the cmd2 output:
	out, err := ioutil.ReadAll(stdout)
	if err != nil {
		return nil, err
	}

	// Wait and return:
	if err = cmd2.Wait(); err != nil {
		return nil, err
	}

	return out, nil
}

//-----------------------------------------------------------------------------
// func: NewEtcdToken
//-----------------------------------------------------------------------------

// NewEtcdToken takes quorumCount and returns a valid etcd bootstrap token:
func NewEtcdToken(wch *WaitChan, quorumCount int, token *string) {

	// Decrement:
	defer wch.WaitGrp.Done()

	// Request an etcd bootstrap token:
	res, err := http.Get("https://discovery.etcd.io/new?size=" + strconv.Itoa(quorumCount))
	if err != nil {
		wch.ErrChan <- err
	}

	// Retrieve the token URL:
	tokenURL, err := ioutil.ReadAll(res.Body)
	if err != nil {
		wch.ErrChan <- err
	}

	// Call the close method:
	_ = res.Body.Close()

	// Return the token ID:
	slice := strings.Split(string(tokenURL), "/")
	*token = slice[len(slice)-1]
}

//-----------------------------------------------------------------------------
// func: OffsetIP
//-----------------------------------------------------------------------------

// OffsetIP takes a CIDR and an offset and returns the IP address at the offset
// position starting at the beginning of the CIDR's subnet:
func OffsetIP(cidr string, offset int) string {

	// Parse the CIDR:
	ip1, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return ""
	}

	// Compute the IP:
	ip2 := ip1.Mask(ipnet.Mask)
	a := int(ipToI32(ip2[len(ip2)-4:]))

	// Return:
	return i32ToIP(int32(a + offset)).String()
}

func ipToI32(ip net.IP) int32 {
	ip = ip.To4()
	return int32(ip[0])<<24 | int32(ip[1])<<16 | int32(ip[2])<<8 | int32(ip[3])
}

func i32ToIP(a int32) net.IP {
	return net.IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
}
<commit_msg>Test whether etcd token pattern matches string<commit_after>package kato

//-----------------------------------------------------------------------------
// Package factored import statement:
//-----------------------------------------------------------------------------

import (

	// Stdlib:
	"encoding/json"
	"errors"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

//-----------------------------------------------------------------------------
// WaitChan stuff:
//-----------------------------------------------------------------------------

// WaitChan is used to handle errors that occur in some goroutines.
type WaitChan struct {
	WaitGrp sync.WaitGroup
	ErrChan chan error
	EndChan chan bool
}

// NewWaitChan initializes a WaitChan struct.
func NewWaitChan(len int) *WaitChan {
	wch := new(WaitChan)
	wch.WaitGrp.Add(len)
	wch.ErrChan = make(chan error, 1)
	wch.EndChan = make(chan bool, 1)
	return wch
}

// WaitErr waits for any error or for all go routines to finish.
func (wch *WaitChan) WaitErr() error {

	// Put the wait group in a go routine:
	go func() {
		wch.WaitGrp.Wait()
		wch.EndChan <- true
	}()

	// This select will block:
	select {
	case <-wch.EndChan:
		return nil
	case err := <-wch.ErrChan:
		return err
	}
}

//-----------------------------------------------------------------------------
// func: DumpState
//-----------------------------------------------------------------------------

// DumpState serializes the given state as a clusterID JSON file.
func DumpState(s interface{}, clusterID string) error {

	// Marshal the data:
	data, err := json.MarshalIndent(s, "", "  ")
	if err != nil {
		return err
	}

	// Create the state directory:
	path := os.Getenv("HOME") + "/.kato"
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(path, 0700)
			if err != nil {
				return err
			}
		}
	}

	// Write the state file:
	err = ioutil.WriteFile(path+"/"+clusterID+".json", data, 0600)
	if err != nil {
		return err
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: ReadState
//-----------------------------------------------------------------------------

// ReadState reads the current ClusterID state file.
func ReadState(clusterID string) ([]byte, error) {

	// Read data from state file:
	stateFile := os.Getenv("HOME") + "/.kato/" + clusterID + ".json"
	raw, err := ioutil.ReadFile(stateFile)
	if err != nil {
		return nil, err
	}

	return raw, nil
}

//-----------------------------------------------------------------------------
// func: CountNodes
//-----------------------------------------------------------------------------

// CountNodes returns the count of <role> nodes defined in <quads>.
func CountNodes(quads []string, role string) (count int) {

	// Default to zero:
	count = 0

	// Get the role count:
	for _, q := range quads {
		if strings.Contains(q, role) {
			s := strings.Split(q, ":")
			count, _ = strconv.Atoi(s[0])
			break
		}
	}

	return
}

//-----------------------------------------------------------------------------
// func: CreateDNSZones
//-----------------------------------------------------------------------------

// CreateDNSZones creates (int|ext).<domain> zones using <provider>.
func CreateDNSZones(wch *WaitChan, provider, apiKey, domain string) {

	// Decrement:
	defer wch.WaitGrp.Done()

	// Forge the zone command:
	cmd := exec.Command("katoctl", provider,
		"--api-key", apiKey, "zone", "add",
		domain, "int."+domain, "ext."+domain)

	// Execute the zone command:
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		wch.ErrChan <- err
	}
}

//-----------------------------------------------------------------------------
// func: ExecutePipeline
//-----------------------------------------------------------------------------

// ExecutePipeline takes two commands and pipes the stdout of the first one
// into the stdin of the second one. Returns the output as []byte.
func ExecutePipeline(cmd1, cmd2 *exec.Cmd) ([]byte, error) {

	var err error

	// Adjust the stderr:
	cmd1.Stderr = os.Stderr
	cmd2.Stderr = os.Stderr

	// Connect both commands:
	cmd2.Stdin, err = cmd1.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Get cmd2 stdout:
	stdout, err := cmd2.StdoutPipe()
	if err != nil {
		return nil, err
	}

	// Execute the pipeline:
	if err = cmd2.Start(); err != nil {
		return nil, err
	}
	if err = cmd1.Run(); err != nil {
		return nil, err
	}

	// Read the cmd2 output:
	out, err := ioutil.ReadAll(stdout)
	if err != nil {
		return nil, err
	}

	// Wait and return:
	if err = cmd2.Wait(); err != nil {
		return nil, err
	}

	return out, nil
}

//-----------------------------------------------------------------------------
// func: NewEtcdToken
//-----------------------------------------------------------------------------

// NewEtcdToken takes quorumCount and returns a valid etcd bootstrap token:
func NewEtcdToken(wch *WaitChan, quorumCount int, token *string) {

	// Decrement:
	defer wch.WaitGrp.Done()

	// Send the request:
	const etcdIO = "https://discovery.etcd.io/"
	res, err := http.Get(etcdIO + "new?size=" + strconv.Itoa(quorumCount))
	if err != nil {
		wch.ErrChan <- err
		return
	}

	// Get the response body:
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		wch.ErrChan <- err
		return
	}

	// Call the close method:
	if err := res.Body.Close(); err != nil {
		wch.ErrChan <- err
		return
	}

	// Test whether pattern matches string:
	match, err := regexp.MatchString(etcdIO+"([a-z,0-9]+$)", string(body))
	if err != nil {
		wch.ErrChan <- err
		return
	}

	// Return if invalid:
	if !match {
		wch.ErrChan <- errors.New("Invalid etcd token retrieved")
		return
	}

	// Return the token ID:
	slice := strings.Split(string(body), "/")
	*token = slice[len(slice)-1]
}

//-----------------------------------------------------------------------------
// func: OffsetIP
//-----------------------------------------------------------------------------

// OffsetIP takes a CIDR and an offset and returns the IP address at the offset
// position starting at the beginning of the CIDR's subnet:
func OffsetIP(cidr string, offset int) string {

	// Parse the CIDR:
	ip1, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return ""
	}

	// Compute the IP:
	ip2 := ip1.Mask(ipnet.Mask)
	a := int(ipToI32(ip2[len(ip2)-4:]))

	// Return:
	return i32ToIP(int32(a + offset)).String()
}

func ipToI32(ip net.IP) int32 {
	ip = ip.To4()
	return int32(ip[0])<<24 | int32(ip[1])<<16 | int32(ip[2])<<8 | int32(ip[3])
}

func i32ToIP(a int32) net.IP {
	return net.IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
}
<|endoftext|>


<commit_before>package kubelego

import (
	"reflect"
	"time"

	"github.com/jetstack/kube-lego/pkg/ingress"

	k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	watch "k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	k8sExtensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)

func ingressListFunc(c *kubernetes.Clientset, ns string) func(k8sMeta.ListOptions) (runtime.Object, error) {
	return func(opts k8sMeta.ListOptions) (runtime.Object, error) {
		return c.Extensions().Ingresses(ns).List(opts)
	}
}

func ingressWatchFunc(c *kubernetes.Clientset, ns string) func(options k8sMeta.ListOptions) (watch.Interface, error) {
	return func(options k8sMeta.ListOptions) (watch.Interface, error) {
		return c.Extensions().Ingresses(ns).Watch(options)
	}
}

func (kl *KubeLego) requestReconfigure() {
	kl.workQueue.Add(true)
}

func (kl *KubeLego) WatchReconfigure() {

	kl.workQueue = workqueue.New()

	// handle worker shutdown
	go func() {
		<-kl.stopCh
		kl.workQueue.ShutDown()
	}()

	go func() {
		kl.waitGroup.Add(1)
		defer kl.waitGroup.Done()
		for {
			item, quit := kl.workQueue.Get()
			if quit {
				return
			}
			kl.Log().Debugf("worker: begin processing %v", item)
			kl.Reconfigure()
			kl.Log().Debugf("worker: done processing %v", item)
			kl.workQueue.Done(item)
		}
	}()
}

func (kl *KubeLego) WatchEvents() {

	kl.Log().Debugf("start watching ingress objects")

	resyncPeriod := 60 * time.Second

	ingEventHandler := cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			addIng := obj.(*k8sExtensions.Ingress)
			if ingress.IgnoreIngress(addIng) != nil {
				return
			}
			kl.Log().Debugf("CREATE ingress/%s/%s", addIng.Namespace, addIng.Name)
			kl.workQueue.Add(true)
		},
		DeleteFunc: func(obj interface{}) {
			delIng := obj.(*k8sExtensions.Ingress)
			if ingress.IgnoreIngress(delIng) != nil {
				return
			}
			kl.Log().Debugf("DELETE ingress/%s/%s", delIng.Namespace, delIng.Name)
			kl.workQueue.Add(true)
		},
		UpdateFunc: func(old, cur interface{}) {
			if !reflect.DeepEqual(old, cur) {
				upIng := cur.(*k8sExtensions.Ingress)
				if ingress.IgnoreIngress(upIng) != nil {
					return
				}
				kl.Log().Debugf("UPDATE ingress/%s/%s", upIng.Namespace, upIng.Name)
				kl.workQueue.Add(true)
			}
		},
	}

	_, controller := cache.NewInformer(
		&cache.ListWatch{
			ListFunc:  ingressListFunc(kl.kubeClient, kl.legoWatchNamespace),
			WatchFunc: ingressWatchFunc(kl.kubeClient, kl.legoWatchNamespace),
		},
		&k8sExtensions.Ingress{},
		resyncPeriod,
		ingEventHandler,
	)

	go controller.Run(kl.stopCh)
}
<commit_msg>break up infinite update loop<commit_after>package kubelego

import (
	"reflect"
	"time"

	"github.com/jetstack/kube-lego/pkg/ingress"

	k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	watch "k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	k8sExtensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)

func ingressListFunc(c *kubernetes.Clientset, ns string) func(k8sMeta.ListOptions) (runtime.Object, error) {
	return func(opts k8sMeta.ListOptions) (runtime.Object, error) {
		return c.Extensions().Ingresses(ns).List(opts)
	}
}

func ingressWatchFunc(c *kubernetes.Clientset, ns string) func(options k8sMeta.ListOptions) (watch.Interface, error) {
	return func(options k8sMeta.ListOptions) (watch.Interface, error) {
		return c.Extensions().Ingresses(ns).Watch(options)
	}
}

func (kl *KubeLego) requestReconfigure() {
	kl.workQueue.Add(true)
}

func (kl *KubeLego) WatchReconfigure() {

	kl.workQueue = workqueue.New()

	// handle worker shutdown
	go func() {
		<-kl.stopCh
		kl.workQueue.ShutDown()
	}()

	go func() {
		kl.waitGroup.Add(1)
		defer kl.waitGroup.Done()
		for {
			item, quit := kl.workQueue.Get()
			if quit {
				return
			}
			kl.Log().Debugf("worker: begin processing %v", item)
			kl.Reconfigure()
			kl.Log().Debugf("worker: done processing %v", item)
			kl.workQueue.Done(item)
		}
	}()
}

func (kl *KubeLego) WatchEvents() {

	kl.Log().Debugf("start watching ingress objects")

	resyncPeriod := 60 * time.Second

	ingEventHandler := cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			addIng := obj.(*k8sExtensions.Ingress)
			if ingress.IgnoreIngress(addIng) != nil {
				return
			}
			kl.Log().Debugf("CREATE ingress/%s/%s", addIng.Namespace, addIng.Name)
			kl.workQueue.Add(true)
		},
		DeleteFunc: func(obj interface{}) {
			delIng := obj.(*k8sExtensions.Ingress)
			if ingress.IgnoreIngress(delIng) != nil {
				return
			}
			kl.Log().Debugf("DELETE ingress/%s/%s", delIng.Namespace, delIng.Name)
			kl.workQueue.Add(true)
		},
		UpdateFunc: func(old, cur interface{}) {
			oldIng := old.(*k8sExtensions.Ingress)
			upIng := cur.(*k8sExtensions.Ingress)

			//ignore resource version in equality check
			oldIng.ResourceVersion = ""
			upIng.ResourceVersion = ""

			if !reflect.DeepEqual(oldIng, upIng) {
				upIng := cur.(*k8sExtensions.Ingress)
				if ingress.IgnoreIngress(upIng) != nil {
					return
				}
				kl.Log().Debugf("UPDATE ingress/%s/%s", upIng.Namespace, upIng.Name)
				kl.workQueue.Add(true)
			}
		},
	}

	_, controller := cache.NewInformer(
		&cache.ListWatch{
			ListFunc:  ingressListFunc(kl.kubeClient, kl.legoWatchNamespace),
			WatchFunc: ingressWatchFunc(kl.kubeClient, kl.legoWatchNamespace),
		},
		&k8sExtensions.Ingress{},
		resyncPeriod,
		ingEventHandler,
	)

	go controller.Run(kl.stopCh)
}
<|endoftext|>