<commit_before>package excelize

import (
	"archive/zip"
	"bytes"
	"encoding/xml"
	"strconv"
	"strings"
)

// File define a populated xlsx.File struct.
type File struct {
	XLSX       map[string]string
	Path       string
	SheetCount int
}

// OpenFile take the name of an XLSX file and returns a populated
// xlsx.File struct for it.
func OpenFile(filename string) (*File, error) {
	var f *zip.ReadCloser
	var err error
	file := make(map[string]string)
	sheetCount := 0
	f, err = zip.OpenReader(filename)
	if err != nil {
		return &File{}, err
	}
	file, sheetCount, _ = ReadZip(f)
	return &File{
		XLSX:       file,
		Path:       filename,
		SheetCount: sheetCount,
	}, nil
}

// SetCellValue provide function to set int or string type value of a cell.
func (f *File) SetCellValue(sheet string, axis string, value interface{}) {
	switch t := value.(type) {
	case int:
		f.SetCellInt(sheet, axis, value.(int))
	case int8:
		f.SetCellInt(sheet, axis, int(value.(int8)))
	case int16:
		f.SetCellInt(sheet, axis, int(value.(int16)))
	case int32:
		f.SetCellInt(sheet, axis, int(value.(int32)))
	case int64:
		f.SetCellInt(sheet, axis, int(value.(int64)))
	case float32:
		f.SetCellInt(sheet, axis, int(value.(float32)))
	case float64:
		f.SetCellInt(sheet, axis, int(value.(float64)))
	case string:
		f.SetCellStr(sheet, axis, t)
	case []byte:
		f.SetCellStr(sheet, axis, string(t))
	default:
		f.SetCellStr(sheet, axis, ``)
	}
}

// SetCellInt provide function to set int type value of a cell.
func (f *File) SetCellInt(sheet string, axis string, value int) {
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	col := string(strings.Map(letterOnlyMapF, axis))
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
	yAxis := titleToNumber(col)

	name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)

	rows := xAxis + 1
	cell := yAxis + 1

	xlsx = completeRow(xlsx, rows, cell)
	xlsx = completeCol(xlsx, rows, cell)

	xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
	xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)

	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

// SetCellStr provide function to set string type value of a cell.
func (f *File) SetCellStr(sheet string, axis string, value string) {
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	col := string(strings.Map(letterOnlyMapF, axis))
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
	yAxis := titleToNumber(col)

	name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)

	rows := xAxis + 1
	cell := yAxis + 1

	xlsx = completeRow(xlsx, rows, cell)
	xlsx = completeCol(xlsx, rows, cell)

	xlsx.SheetData.Row[xAxis].C[yAxis].T = `str`
	xlsx.SheetData.Row[xAxis].C[yAxis].V = value

	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

// Completion column element tags of XML in a sheet.
func completeCol(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet {
	if len(xlsx.SheetData.Row) < cell {
		for i := len(xlsx.SheetData.Row); i < cell; i++ {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
				R: i + 1,
			})
		}
	}
	buffer := bytes.Buffer{}
	for k, v := range xlsx.SheetData.Row {
		if len(v.C) < cell {
			start := len(v.C)
			for iii := start; iii < cell; iii++ {
				buffer.WriteString(toAlphaString(iii + 1))
				buffer.WriteString(strconv.Itoa(k + 1))
				xlsx.SheetData.Row[k].C = append(xlsx.SheetData.Row[k].C, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
		}
	}
	return xlsx
}

// Completion row element tags of XML in a sheet.
func completeRow(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet {
	currentRows := len(xlsx.SheetData.Row)
	if currentRows > 1 {
		lastRow := xlsx.SheetData.Row[currentRows-1].R
		if lastRow >= row {
			row = lastRow
		}
	}
	sheetData := xlsxSheetData{}
	existsRows := map[int]int{}
	for k, v := range xlsx.SheetData.Row {
		existsRows[v.R] = k
	}
	for i := 0; i < row; i++ {
		_, ok := existsRows[i+1]
		if ok {
			sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]])
			continue
		}
		sheetData.Row = append(sheetData.Row, xlsxRow{
			R: i + 1,
		})
	}
	buffer := bytes.Buffer{}
	for ii := 0; ii < row; ii++ {
		start := len(sheetData.Row[ii].C)
		if start == 0 {
			for iii := start; iii < cell; iii++ {
				buffer.WriteString(toAlphaString(iii + 1))
				buffer.WriteString(strconv.Itoa(ii + 1))
				sheetData.Row[ii].C = append(sheetData.Row[ii].C, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
		}
	}
	xlsx.SheetData = sheetData
	return xlsx
}

// Replace xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Office Excel 2007.
func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string {
	oldXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
	newXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">`
	workbookMarshal = strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
	return workbookMarshal
}

// Check XML tags and fix discontinuous case, for example:
//
//    <row r="15" spans="1:22" x14ac:dyDescent="0.2">
//        <c r="A15" s="2" />
//        <c r="B15" s="2" />
//        <c r="F15" s="1" />
//        <c r="G15" s="1" />
//    </row>
//
// in this case, we should to change it to
//
//    <row r="15" spans="1:22" x14ac:dyDescent="0.2">
//        <c r="A15" s="2" />
//        <c r="B15" s="2" />
//        <c r="C15" s="2" />
//        <c r="D15" s="2" />
//        <c r="E15" s="2" />
//        <c r="F15" s="1" />
//        <c r="G15" s="1" />
//    </row>
//
// Noteice: this method could be very slow for large spreadsheets (more than 3000 rows one sheet).
func checkRow(xlsx xlsxWorksheet) xlsxWorksheet {
	buffer := bytes.Buffer{}
	for k, v := range xlsx.SheetData.Row {
		lenCol := len(v.C)
		if lenCol < 1 {
			continue
		}
		endR := string(strings.Map(letterOnlyMapF, v.C[lenCol-1].R))
		endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, v.C[lenCol-1].R))
		endCol := titleToNumber(endR) + 1
		if lenCol < endCol {
			oldRow := xlsx.SheetData.Row[k].C
			xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
			tmp := []xlsxC{}
			for i := 0; i <= endCol; i++ {
				buffer.WriteString(toAlphaString(i + 1))
				buffer.WriteString(strconv.Itoa(endRow))
				tmp = append(tmp, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
			xlsx.SheetData.Row[k].C = tmp
			for _, y := range oldRow {
				colAxis := titleToNumber(string(strings.Map(letterOnlyMapF, y.R)))
				xlsx.SheetData.Row[k].C[colAxis] = y
			}
		}
	}
	return xlsx
}

// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
// Office Excel 2007 and 2010. This function will be remove value tag when met a
// cell have a linked value. Reference https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating?forum=excel
//
// Notice: after open XLSX file Excel will be update linked value and generate
// new value and will prompt save file or not.
//
// For example:
//
//    <row r="19" spans="2:2">
//        <c r="B19">
//            <f>SUM(Sheet2!D2,Sheet2!D11)</f>
//            <v>100</v>
//         </c>
//    </row>
//
// to
//
//    <row r="19" spans="2:2">
//        <c r="B19">
//            <f>SUM(Sheet2!D2,Sheet2!D11)</f>
//        </c>
//    </row>
func (f *File) UpdateLinkedValue() {
	for i := 1; i <= f.SheetCount; i++ {
		var xlsx xlsxWorksheet
		name := `xl/worksheets/sheet` + strconv.Itoa(i) + `.xml`
		xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
		for indexR, row := range xlsx.SheetData.Row {
			for indexC, col := range row.C {
				if col.F != nil && col.V != `` {
					xlsx.SheetData.Row[indexR].C[indexC].V = ``
					xlsx.SheetData.Row[indexR].C[indexC].T = ``
				}
			}
		}
		output, _ := xml.Marshal(xlsx)
		f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
	}
}
<commit_msg>Add setter for default cell value<commit_after>package excelize

import (
	"archive/zip"
	"bytes"
	"encoding/xml"
	"strconv"
	"strings"
)

// File define a populated xlsx.File struct.
type File struct {
	XLSX       map[string]string
	Path       string
	SheetCount int
}

// OpenFile take the name of an XLSX file and returns a populated
// xlsx.File struct for it.
func OpenFile(filename string) (*File, error) {
	var f *zip.ReadCloser
	var err error
	file := make(map[string]string)
	sheetCount := 0
	f, err = zip.OpenReader(filename)
	if err != nil {
		return &File{}, err
	}
	file, sheetCount, _ = ReadZip(f)
	return &File{
		XLSX:       file,
		Path:       filename,
		SheetCount: sheetCount,
	}, nil
}

// SetCellValue provide function to set int or string type value of a cell.
func (f *File) SetCellValue(sheet string, axis string, value interface{}) {
	switch t := value.(type) {
	case int:
		f.SetCellInt(sheet, axis, value.(int))
	case int8:
		f.SetCellInt(sheet, axis, int(value.(int8)))
	case int16:
		f.SetCellInt(sheet, axis, int(value.(int16)))
	case int32:
		f.SetCellInt(sheet, axis, int(value.(int32)))
	case int64:
		f.SetCellInt(sheet, axis, int(value.(int64)))
	case float32:
		f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32))
	case float64:
		f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float64)), 'f', -1, 64))
	case string:
		f.SetCellStr(sheet, axis, t)
	case []byte:
		f.SetCellStr(sheet, axis, string(t))
	default:
		f.SetCellStr(sheet, axis, ``)
	}
}

// SetCellInt provide function to set int type value of a cell.
func (f *File) SetCellInt(sheet string, axis string, value int) {
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	col := string(strings.Map(letterOnlyMapF, axis))
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
	yAxis := titleToNumber(col)

	name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)

	rows := xAxis + 1
	cell := yAxis + 1

	xlsx = completeRow(xlsx, rows, cell)
	xlsx = completeCol(xlsx, rows, cell)

	xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
	xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)

	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

// SetCellStr provide function to set string type value of a cell.
func (f *File) SetCellStr(sheet string, axis string, value string) {
	axis = strings.ToUpper(axis)
	var xlsx xlsxWorksheet
	col := string(strings.Map(letterOnlyMapF, axis))
	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
	xAxis := row - 1
	yAxis := titleToNumber(col)

	name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)

	rows := xAxis + 1
	cell := yAxis + 1

	xlsx = completeRow(xlsx, rows, cell)
	xlsx = completeCol(xlsx, rows, cell)

	xlsx.SheetData.Row[xAxis].C[yAxis].T = `str`
	xlsx.SheetData.Row[xAxis].C[yAxis].V = value

	output, _ := xml.Marshal(xlsx)
	f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

// SetCellDefault provides function to set string type value of a cell as default format without escaping the cell
func (f *File) SetCellDefault(sheet string, axis string, value string) {
    axis = strings.ToUpper(axis)
    var xlsx xlsxWorksheet
    col := string(strings.Map(letterOnlyMapF, axis))
    row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
    xAxis := row - 1
    yAxis := titleToNumber(col)

    name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
    xml.Unmarshal([]byte(f.readXML(name)), &xlsx)

    rows := xAxis + 1
    cell := yAxis + 1

    xlsx = completeRow(xlsx, rows, cell)
    xlsx = completeCol(xlsx, rows, cell)

    xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
    xlsx.SheetData.Row[xAxis].C[yAxis].V = value

    output, _ := xml.Marshal(xlsx)
    f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

// Completion column element tags of XML in a sheet.
func completeCol(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet {
	if len(xlsx.SheetData.Row) < cell {
		for i := len(xlsx.SheetData.Row); i < cell; i++ {
			xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
				R: i + 1,
			})
		}
	}
	buffer := bytes.Buffer{}
	for k, v := range xlsx.SheetData.Row {
		if len(v.C) < cell {
			start := len(v.C)
			for iii := start; iii < cell; iii++ {
				buffer.WriteString(toAlphaString(iii + 1))
				buffer.WriteString(strconv.Itoa(k + 1))
				xlsx.SheetData.Row[k].C = append(xlsx.SheetData.Row[k].C, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
		}
	}
	return xlsx
}

// Completion row element tags of XML in a sheet.
func completeRow(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet {
	currentRows := len(xlsx.SheetData.Row)
	if currentRows > 1 {
		lastRow := xlsx.SheetData.Row[currentRows-1].R
		if lastRow >= row {
			row = lastRow
		}
	}
	sheetData := xlsxSheetData{}
	existsRows := map[int]int{}
	for k, v := range xlsx.SheetData.Row {
		existsRows[v.R] = k
	}
	for i := 0; i < row; i++ {
		_, ok := existsRows[i+1]
		if ok {
			sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]])
			continue
		}
		sheetData.Row = append(sheetData.Row, xlsxRow{
			R: i + 1,
		})
	}
	buffer := bytes.Buffer{}
	for ii := 0; ii < row; ii++ {
		start := len(sheetData.Row[ii].C)
		if start == 0 {
			for iii := start; iii < cell; iii++ {
				buffer.WriteString(toAlphaString(iii + 1))
				buffer.WriteString(strconv.Itoa(ii + 1))
				sheetData.Row[ii].C = append(sheetData.Row[ii].C, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
		}
	}
	xlsx.SheetData = sheetData
	return xlsx
}

// Replace xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Office Excel 2007.
func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string {
	oldXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
	newXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">`
	workbookMarshal = strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
	return workbookMarshal
}

// Check XML tags and fix discontinuous case, for example:
//
//    <row r="15" spans="1:22" x14ac:dyDescent="0.2">
//        <c r="A15" s="2" />
//        <c r="B15" s="2" />
//        <c r="F15" s="1" />
//        <c r="G15" s="1" />
//    </row>
//
// in this case, we should to change it to
//
//    <row r="15" spans="1:22" x14ac:dyDescent="0.2">
//        <c r="A15" s="2" />
//        <c r="B15" s="2" />
//        <c r="C15" s="2" />
//        <c r="D15" s="2" />
//        <c r="E15" s="2" />
//        <c r="F15" s="1" />
//        <c r="G15" s="1" />
//    </row>
//
// Noteice: this method could be very slow for large spreadsheets (more than 3000 rows one sheet).
func checkRow(xlsx xlsxWorksheet) xlsxWorksheet {
	buffer := bytes.Buffer{}
	for k, v := range xlsx.SheetData.Row {
		lenCol := len(v.C)
		if lenCol < 1 {
			continue
		}
		endR := string(strings.Map(letterOnlyMapF, v.C[lenCol-1].R))
		endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, v.C[lenCol-1].R))
		endCol := titleToNumber(endR) + 1
		if lenCol < endCol {
			oldRow := xlsx.SheetData.Row[k].C
			xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
			tmp := []xlsxC{}
			for i := 0; i <= endCol; i++ {
				buffer.WriteString(toAlphaString(i + 1))
				buffer.WriteString(strconv.Itoa(endRow))
				tmp = append(tmp, xlsxC{
					R: buffer.String(),
				})
				buffer.Reset()
			}
			xlsx.SheetData.Row[k].C = tmp
			for _, y := range oldRow {
				colAxis := titleToNumber(string(strings.Map(letterOnlyMapF, y.R)))
				xlsx.SheetData.Row[k].C[colAxis] = y
			}
		}
	}
	return xlsx
}

// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
// Office Excel 2007 and 2010. This function will be remove value tag when met a
// cell have a linked value. Reference https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating?forum=excel
//
// Notice: after open XLSX file Excel will be update linked value and generate
// new value and will prompt save file or not.
//
// For example:
//
//    <row r="19" spans="2:2">
//        <c r="B19">
//            <f>SUM(Sheet2!D2,Sheet2!D11)</f>
//            <v>100</v>
//         </c>
//    </row>
//
// to
//
//    <row r="19" spans="2:2">
//        <c r="B19">
//            <f>SUM(Sheet2!D2,Sheet2!D11)</f>
//        </c>
//    </row>
func (f *File) UpdateLinkedValue() {
	for i := 1; i <= f.SheetCount; i++ {
		var xlsx xlsxWorksheet
		name := `xl/worksheets/sheet` + strconv.Itoa(i) + `.xml`
		xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
		for indexR, row := range xlsx.SheetData.Row {
			for indexC, col := range row.C {
				if col.F != nil && col.V != `` {
					xlsx.SheetData.Row[indexR].C[indexC].V = ``
					xlsx.SheetData.Row[indexR].C[indexC].T = ``
				}
			}
		}
		output, _ := xml.Marshal(xlsx)
		f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/workers"
	"os"
)

type Options struct {
	PathToConfigFile string
	Region           string
	Bucket           string
	KeyPrefix        string
	Format           string
	Limit            int
	Concurrency      int
}

func main() {
	opts := parseCommandLine()
	config, err := models.LoadConfigFile(opts.PathToConfigFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}
	_context := context.NewContext(config)

	aptAuditList, err := workers.NewAPTAuditList(_context,
		opts.Region, opts.Bucket, opts.KeyPrefix,
		opts.Format, opts.Limit, opts.Concurrency)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}

	aptAuditList.Run()
}

// See if you can figure out from the function name what this does.
func parseCommandLine() Options {
	var pathToConfigFile string
	var region string
	var bucket string
	var keyPrefix string
	var format string
	var limit int
	var concurrency int
	flag.StringVar(&pathToConfigFile, "config", "", "Path to APTrust config file (required)")
	flag.StringVar(&region, "region", "", "AWS region to check (required)")
	flag.StringVar(&bucket, "bucket", "", "The bucket to list (required)")
	flag.StringVar(&keyPrefix, "prefix", "", "List keys starting with this prefix")
	flag.StringVar(&format, "format", "tsv", "Output data in this format")
	flag.IntVar(&limit, "limit", 50, "List no more than this many files")
	flag.IntVar(&concurrency, "concurrency", 4, "Use this many concurrent HTTP connections")

	flag.Parse()
	if pathToConfigFile == "" || region == "" || bucket == "" {
		fmt.Fprintln(os.Stderr, "Params config, region, and bucket are required")
		printUsage()
		os.Exit(1)
	}
	options := Options{
		PathToConfigFile: pathToConfigFile,
		Region:           region,
		Bucket:           bucket,
		KeyPrefix:        keyPrefix,
		Format:           format,
		Limit:            limit,
		Concurrency:      concurrency,
	}
	return options
}

// Tell the user about the program.
func printUsage() {
	message := `
apt_bucket_reader: Reads the contents of S3 receiving buckets, and creates
WorkItem entries and NSQ entries for bags awaiting ingest in those buckets.

Usage: apt_audit_list -config=<path> \
                      -region=<aws region> \
                      -bucket=<bucket name> \
                      -prefix=<key prefix> \
                      -format=<output format> \
                      -limit=<max items to list> \
                      -concurrency=<max simultaneous clients>

Starred (*) params are required.

Param -config (*) is the path to the APTrust config file.
Param -region (*) is the AWS region containing the S3 or Glacier bucket.
       "us-east-1" is Virginia (All buckets other than APTrust Glacier)
       "us-west-2" is Oregon (APTrust Glacier only)
Param -bucket (*) is the name of the bucket.
       "aptrust-dpn-preservation" is the DPN production bucket
       "aptrust-dpn-test" is the DPN test bucket
       "aptrust-preservation-oregon" is the APTrust Glacier bucket
       "aptrust-preservation-storage" is the APTrust production S3 bucket
       "aptrust-test-preservation" is the APTrust test bucket
Param -prefix will limit the list to keys beginning with the specified
       prefix. To list all keys, omit the prefix flag.
Param -format specifies the output format. The default is "tsv",
       but any of the following are valid:
       "json" - JSON data
       "csv"  - Comma-separated values
       "tsv"  - Tab-separated values
Param -limit is the maximum number of records to fetch. The default is 50.
Param -concurrency is the number of concurrent HTTP requests to issue when
       building the list. The default is 4, and the max is 32.
`
	fmt.Println(message)
}
<commit_msg>Added to apt_audit_list help doc<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/workers"
	"os"
)

type Options struct {
	PathToConfigFile string
	Region           string
	Bucket           string
	KeyPrefix        string
	Format           string
	Limit            int
	Concurrency      int
}

func main() {
	opts := parseCommandLine()
	config, err := models.LoadConfigFile(opts.PathToConfigFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}
	_context := context.NewContext(config)

	aptAuditList, err := workers.NewAPTAuditList(_context,
		opts.Region, opts.Bucket, opts.KeyPrefix,
		opts.Format, opts.Limit, opts.Concurrency)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		os.Exit(1)
	}

	aptAuditList.Run()
}

// See if you can figure out from the function name what this does.
func parseCommandLine() Options {
	var pathToConfigFile string
	var region string
	var bucket string
	var keyPrefix string
	var format string
	var limit int
	var concurrency int
	flag.StringVar(&pathToConfigFile, "config", "", "Path to APTrust config file (required)")
	flag.StringVar(&region, "region", "", "AWS region to check (required)")
	flag.StringVar(&bucket, "bucket", "", "The bucket to list (required)")
	flag.StringVar(&keyPrefix, "prefix", "", "List keys starting with this prefix")
	flag.StringVar(&format, "format", "tsv", "Output data in this format")
	flag.IntVar(&limit, "limit", 50, "List no more than this many files")
	flag.IntVar(&concurrency, "concurrency", 4, "Use this many concurrent HTTP connections")

	flag.Parse()
	if pathToConfigFile == "" || region == "" || bucket == "" {
		fmt.Fprintln(os.Stderr, "Params config, region, and bucket are required")
		printUsage()
		os.Exit(1)
	}
	options := Options{
		PathToConfigFile: pathToConfigFile,
		Region:           region,
		Bucket:           bucket,
		KeyPrefix:        keyPrefix,
		Format:           format,
		Limit:            limit,
		Concurrency:      concurrency,
	}
	return options
}

// Tell the user about the program.
func printUsage() {
	message := `
apt_bucket_reader: Reads the contents of S3 receiving buckets, and creates
WorkItem entries and NSQ entries for bags awaiting ingest in those buckets.

Usage: apt_audit_list -config=<path> \
                      -region=<aws region> \
                      -bucket=<bucket name> \
                      -prefix=<key prefix> \
                      -format=<output format> \
                      -limit=<max items to list> \
                      -concurrency=<max simultaneous clients>

Starred (*) params are required.

Param -config (*) is the path to the APTrust config file. It can be an
       absolute path, or config/<file.json> if it's in the config directory
       of $EXCHANGE_HOME.
Param -region (*) is the AWS region containing the S3 or Glacier bucket.
       "us-east-1" is Virginia (All buckets other than APTrust Glacier)
       "us-west-2" is Oregon (APTrust Glacier only)
Param -bucket (*) is the name of the bucket.
       "aptrust.dpn.preservation"     - DPN production bucket (us-east-1)
       "aptrust.dpn.test"             - DPN test bucket (us-east-1)
       "aptrust.preservation.oregon"  - APTrust Glacier bucket (us-west-2)
       "aptrust.preservation.storage" - APTrust production S3 bucket (us-east-1)
       "aptrust.test.preservation"    - APTrust test bucket (us-east-1)
Param -prefix will limit the list to keys beginning with the specified
       prefix. To list all keys, omit the prefix flag.
Param -format specifies the output format. The default is "tsv",
       but any of the following are valid:
       "json" - JSON data
       "csv"  - Comma-separated values
       "tsv"  - Tab-separated values
Param -limit is the maximum number of records to fetch. The default is 50.
Param -concurrency is the number of concurrent HTTP requests to issue when
       building the list. The default is 4, and the max is 32.

Examples
--------

List the first 100 items from the main preservation bucket in S3/Virginia:

apt_audit_list -config=config/production.json -region="us-east-1" \
               -bucket="aptrust.preservation.storage" -limit=100

List the first 100 items from Glacier/Oregon whose keys start with "a00"
and use JSON as the output format:

apt_audit_list -config=config/production.json -region="us-west-2" \
               -bucket="aptrust.preservation.oregon" -prefix="a00" \
               -limit=100 -format=json

List the first 100 items in the DPN preservation bucket, using 10
concurrent connections:

apt_audit_list -config=config/production.json -region="us-east-1" \
               -bucket="aptrust.dpn.preservaton" -limit=100 \
               -concurrency=10

`
	fmt.Println(message)
}
<|endoftext|>


<commit_before>//   Copyright 2016 DigitalOcean
//
//   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.

// Command ceph_exporter provides a Prometheus exporter for a Ceph cluster.
package main

import (
	"flag"
	"log"
	"net/http"
	"sync"

	"github.com/digitalocean/ceph_exporter/collectors"

	"github.com/ceph/go-ceph/rados"
	"github.com/prometheus/client_golang/prometheus"
)

// CephExporter wraps all the ceph collectors and provides a single global
// exporter to extracts metrics out of. It also ensures that the collection
// is done in a thread-safe manner, the necessary requirement stated by
// prometheus. It also implements a prometheus.Collector interface in order
// to register it correctly.
type CephExporter struct {
	mu         sync.Mutex
	collectors []prometheus.Collector
}

// Verify that the exporter implements the interface correctly.
var _ prometheus.Collector = &CephExporter{}

// NewCephExporter creates an instance to CephExporter and returns a reference
// to it. We can choose to enable a collector to extract stats out of by adding
// it to the list of collectors.
func NewCephExporter(conn *rados.Conn, cluster string) *CephExporter {
	return &CephExporter{
		collectors: []prometheus.Collector{
			collectors.NewClusterUsageCollector(conn, cluster),
			collectors.NewPoolUsageCollector(conn, cluster),
			collectors.NewClusterHealthCollector(conn, cluster),
			collectors.NewMonitorCollector(conn, cluster),
			collectors.NewOSDCollector(conn, cluster),
		},
	}
}

// Describe sends all the descriptors of the collectors included to
// the provided channel.
func (c *CephExporter) Describe(ch chan<- *prometheus.Desc) {
	for _, cc := range c.collectors {
		cc.Describe(ch)
	}
}

// Collect sends the collected metrics from each of the collectors to
// prometheus. Collect could be called several times concurrently
// and thus its run is protected by a single mutex.
func (c *CephExporter) Collect(ch chan<- prometheus.Metric) {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, cc := range c.collectors {
		cc.Collect(ch)
	}
}

func main() {
	var (
		addr        = flag.String("telemetry.addr", ":9128", "host:port for ceph exporter")
		metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics")
		cephConfig  = flag.String("ceph.config", "", "path to ceph config file")
		cephUser    = flag.String("ceph.user", "admin", "Ceph user to connect to cluster.")

		exporterConfig = flag.String("exporter.config", "/etc/ceph/exporter.yml", "Path to ceph exporter config.")
	)
	flag.Parse()

	if fileExists(*exporterConfig) {

		cfg, err := ParseConfig(*exporterConfig)
		if err != nil {
			log.Fatalf("Error: %v", err)
		}

		for _, cluster := range cfg.Cluster {

			conn, err := rados.NewConnWithUser(cluster.User)
			if err != nil {
				log.Fatalf("cannot create new ceph connection: %s", err)
			}

			err = conn.ReadConfigFile(cluster.ConfigFile)
			if err != nil {
				log.Fatalf("cannot read ceph config file: %s", err)
			}

			if err := conn.Connect(); err != nil {
				log.Fatalf("cannot connect to ceph cluster: %s", err)
			}
			// defer Shutdown to program exit
			defer conn.Shutdown()

			log.Printf("Starting ceph exporter for cluster: %s", cluster.ClusterLabel)
			err = prometheus.Register(NewCephExporter(conn, cluster.ClusterLabel))
			if err != nil {
				log.Fatalf("cannot export cluster: %s error: %v", cluster.ClusterLabel, err)
			}
		}
	} else {
		conn, err := rados.NewConnWithUser(*cephUser)
		if err != nil {
			log.Fatalf("cannot create new ceph connection: %s", err)
		}

		if *cephConfig != "" {
			err = conn.ReadConfigFile(*cephConfig)
		} else {
			err = conn.ReadDefaultConfigFile()
		}
		if err != nil {
			log.Fatalf("cannot read ceph config file: %s", err)
		}

		if err := conn.Connect(); err != nil {
			log.Fatalf("cannot connect to ceph cluster: %s", err)
		}
		defer conn.Shutdown()

		prometheus.MustRegister(NewCephExporter(conn, "ceph"))
	}

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
			<title>Ceph Exporter</title>

Ceph Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})

	log.Printf("Starting ceph exporter on %q", *addr)
	if err := http.ListenAndServe(*addr, nil); err != nil {
		log.Fatalf("cannot start ceph exporter: %s", err)
	}
}
<commit_msg>update /metrics handler to promhttp.Handler()<commit_after>//   Copyright 2016 DigitalOcean
//
//   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.

// Command ceph_exporter provides a Prometheus exporter for a Ceph cluster.
package main

import (
	"flag"
	"log"
	"net/http"
	"sync"

	"github.com/ceph/go-ceph/rados"
	"github.com/digitalocean/ceph_exporter/collectors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// CephExporter wraps all the ceph collectors and provides a single global
// exporter to extracts metrics out of. It also ensures that the collection
// is done in a thread-safe manner, the necessary requirement stated by
// prometheus. It also implements a prometheus.Collector interface in order
// to register it correctly.
type CephExporter struct {
	mu         sync.Mutex
	collectors []prometheus.Collector
}

// Verify that the exporter implements the interface correctly.
var _ prometheus.Collector = &CephExporter{}

// NewCephExporter creates an instance to CephExporter and returns a reference
// to it. We can choose to enable a collector to extract stats out of by adding
// it to the list of collectors.
func NewCephExporter(conn *rados.Conn, cluster string) *CephExporter {
	return &CephExporter{
		collectors: []prometheus.Collector{
			collectors.NewClusterUsageCollector(conn, cluster),
			collectors.NewPoolUsageCollector(conn, cluster),
			collectors.NewClusterHealthCollector(conn, cluster),
			collectors.NewMonitorCollector(conn, cluster),
			collectors.NewOSDCollector(conn, cluster),
		},
	}
}

// Describe sends all the descriptors of the collectors included to
// the provided channel.
func (c *CephExporter) Describe(ch chan<- *prometheus.Desc) {
	for _, cc := range c.collectors {
		cc.Describe(ch)
	}
}

// Collect sends the collected metrics from each of the collectors to
// prometheus. Collect could be called several times concurrently
// and thus its run is protected by a single mutex.
func (c *CephExporter) Collect(ch chan<- prometheus.Metric) {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, cc := range c.collectors {
		cc.Collect(ch)
	}
}

func main() {
	var (
		addr        = flag.String("telemetry.addr", ":9128", "host:port for ceph exporter")
		metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics")
		cephConfig  = flag.String("ceph.config", "", "path to ceph config file")
		cephUser    = flag.String("ceph.user", "admin", "Ceph user to connect to cluster.")

		exporterConfig = flag.String("exporter.config", "/etc/ceph/exporter.yml", "Path to ceph exporter config.")
	)
	flag.Parse()

	if fileExists(*exporterConfig) {

		cfg, err := ParseConfig(*exporterConfig)
		if err != nil {
			log.Fatalf("Error: %v", err)
		}

		for _, cluster := range cfg.Cluster {

			conn, err := rados.NewConnWithUser(cluster.User)
			if err != nil {
				log.Fatalf("cannot create new ceph connection: %s", err)
			}

			err = conn.ReadConfigFile(cluster.ConfigFile)
			if err != nil {
				log.Fatalf("cannot read ceph config file: %s", err)
			}

			if err := conn.Connect(); err != nil {
				log.Fatalf("cannot connect to ceph cluster: %s", err)
			}
			// defer Shutdown to program exit
			defer conn.Shutdown()

			log.Printf("Starting ceph exporter for cluster: %s", cluster.ClusterLabel)
			err = prometheus.Register(NewCephExporter(conn, cluster.ClusterLabel))
			if err != nil {
				log.Fatalf("cannot export cluster: %s error: %v", cluster.ClusterLabel, err)
			}
		}
	} else {
		conn, err := rados.NewConnWithUser(*cephUser)
		if err != nil {
			log.Fatalf("cannot create new ceph connection: %s", err)
		}

		if *cephConfig != "" {
			err = conn.ReadConfigFile(*cephConfig)
		} else {
			err = conn.ReadDefaultConfigFile()
		}
		if err != nil {
			log.Fatalf("cannot read ceph config file: %s", err)
		}

		if err := conn.Connect(); err != nil {
			log.Fatalf("cannot connect to ceph cluster: %s", err)
		}
		defer conn.Shutdown()

		prometheus.MustRegister(NewCephExporter(conn, "ceph"))
	}

	http.Handle(*metricsPath, promhttp.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
			<title>Ceph Exporter</title>

Ceph Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})

	log.Printf("Starting ceph exporter on %q", *addr)
	if err := http.ListenAndServe(*addr, nil); err != nil {
		log.Fatalf("cannot start ceph exporter: %s", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"log"
	"os"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

type rsyslogType int

const (
	rsyslogUnknown rsyslogType = iota
	rsyslogAction
	rsyslogInput
	rsyslogQueue
	rsyslogResource
)

type rsyslogExporter struct {
	started bool
	logfile *os.File
	scanner *bufio.Scanner
	pointStore
}

func newRsyslogExporter() *rsyslogExporter {
	e := &rsyslogExporter{
		scanner: bufio.NewScanner(os.Stdin),
		pointStore: pointStore{
			pointMap: make(map[string]*point),
			lock:     &sync.RWMutex{},
		},
	}
	return e
}

func (re *rsyslogExporter) handleStatLine(buf []byte) {
	pstatType := getStatType(buf)
	log.Printf("pstatType: %+v", pstatType)

	switch pstatType {
	case rsyslogAction:
		a, err := newActionFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range a.toPoints() {
			re.set(p)
		}

	case rsyslogInput:
		i, err := newInputFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range i.toPoints() {
			re.set(p)
		}

	case rsyslogQueue:
		q, err := newQueueFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range q.toPoints() {
			re.set(p)
		}

	case rsyslogResource:
		r, err := newResourceFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range r.toPoints() {
			re.set(p)
		}

	default:
		log.Printf("unknown pstat type: %v", pstatType)
	}
}

// Describe sends the description of currently known metrics collected
// by this Collector to the provided channel. Note that this implementation
// does not necessarily send the "super-set of all possible descriptors" as
// defined by the Collector interface spec, depending on the timing of when
// it is called. The rsyslog exporter does not know all possible metrics
// it will export until the first full batch of rsyslog impstats messages
// are received via stdin. This is ok for now.
func (re *rsyslogExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- prometheus.NewDesc(
		prometheus.BuildFQName("", "rsyslog", "scrapes"),
		"times exporter has been scraped",
		nil, nil,
	)

	keys := re.keys()

	for _, k := range keys {
		p, err := re.get(k)
		if err != nil {
			ch <- p.promDescription()
		}
	}
}

// Collect is called by Prometheus when collecting metrics.
func (re *rsyslogExporter) Collect(ch chan<- prometheus.Metric) {
	keys := re.keys()

	for _, k := range keys {
		p, err := re.get(k)
		if err != nil {
			continue
		}

		metric := prometheus.MustNewConstMetric(
			p.promDescription(),
			p.promType(),
			p.promValue(),
			p.promLabelValue(),
		)

		ch <- metric
	}
}

func (re *rsyslogExporter) run() {
	for re.scanner.Scan() {
		re.handleStatLine(re.scanner.Bytes())
	}
	os.Exit(0)
}
<commit_msg>check for unusual end of input<commit_after>package main

import (
	"bufio"
	"log"
	"os"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

type rsyslogType int

const (
	rsyslogUnknown rsyslogType = iota
	rsyslogAction
	rsyslogInput
	rsyslogQueue
	rsyslogResource
)

type rsyslogExporter struct {
	started bool
	logfile *os.File
	scanner *bufio.Scanner
	pointStore
}

func newRsyslogExporter() *rsyslogExporter {
	e := &rsyslogExporter{
		scanner: bufio.NewScanner(os.Stdin),
		pointStore: pointStore{
			pointMap: make(map[string]*point),
			lock:     &sync.RWMutex{},
		},
	}
	return e
}

func (re *rsyslogExporter) handleStatLine(buf []byte) {
	pstatType := getStatType(buf)
	log.Printf("pstatType: %+v", pstatType)

	switch pstatType {
	case rsyslogAction:
		a, err := newActionFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range a.toPoints() {
			re.set(p)
		}

	case rsyslogInput:
		i, err := newInputFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range i.toPoints() {
			re.set(p)
		}

	case rsyslogQueue:
		q, err := newQueueFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range q.toPoints() {
			re.set(p)
		}

	case rsyslogResource:
		r, err := newResourceFromJSON(buf)
		if err != nil {
			log.Print(err)
			return
		}
		for _, p := range r.toPoints() {
			re.set(p)
		}

	default:
		log.Printf("unknown pstat type: %v", pstatType)
	}
}

// Describe sends the description of currently known metrics collected
// by this Collector to the provided channel. Note that this implementation
// does not necessarily send the "super-set of all possible descriptors" as
// defined by the Collector interface spec, depending on the timing of when
// it is called. The rsyslog exporter does not know all possible metrics
// it will export until the first full batch of rsyslog impstats messages
// are received via stdin. This is ok for now.
func (re *rsyslogExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- prometheus.NewDesc(
		prometheus.BuildFQName("", "rsyslog", "scrapes"),
		"times exporter has been scraped",
		nil, nil,
	)

	keys := re.keys()

	for _, k := range keys {
		p, err := re.get(k)
		if err != nil {
			ch <- p.promDescription()
		}
	}
}

// Collect is called by Prometheus when collecting metrics.
func (re *rsyslogExporter) Collect(ch chan<- prometheus.Metric) {
	keys := re.keys()

	for _, k := range keys {
		p, err := re.get(k)
		if err != nil {
			continue
		}

		metric := prometheus.MustNewConstMetric(
			p.promDescription(),
			p.promType(),
			p.promValue(),
			p.promLabelValue(),
		)

		ch <- metric
	}
}

func (re *rsyslogExporter) run() {
	for re.scanner.Scan() {
		re.handleStatLine(re.scanner.Bytes())
	}
	if err := re.scanner.Err(); err != nil {
		log.Printf("error reading input: %v", err)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package installer

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/badgerodon/ioutil"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/oauth2"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/pkg/browser"
	log "github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
	"github.com/flynn/flynn/pkg/azure"
	"github.com/flynn/flynn/pkg/cors"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/sse"
)

type assetManifest struct {
	Assets map[string]string `json:"assets"`
}

type htmlTemplateData struct {
	ApplicationJSPath  string
	NormalizeCSSPath   string
	FontAwesomeCSSPath string
	ApplicationCSSPath string
	ReactJSPath        string
}

type installerJSConfig struct {
	Endpoints            map[string]string `json:"endpoints"`
	HasAWSEnvCredentials bool              `json:"has_aws_env_credentials"`
	AWSEnvCredentialsID  string            `json:"aws_env_credentials_id,omitempty"`
}

type httpAPI struct {
	AWSEnvCreds  aws.CredentialsProvider
	Installer    *Installer
	logger       log.Logger
	clientConfig installerJSConfig
}

func ServeHTTP() error {
	logger := log.New()
	installer := NewInstaller(logger)

	api := &httpAPI{
		Installer: installer,
		logger:    logger,
		clientConfig: installerJSConfig{
			Endpoints: map[string]string{
				"clusters":           "/clusters",
				"cluster":            "/clusters/:id",
				"events":             "/events",
				"prompt":             "/clusters/:id/prompts/:prompt_id",
				"credentials":        "/credentials",
				"regions":            "/regions",
				"azureSubscriptions": "/azure/subscriptions",
			},
		},
	}

	if creds, err := aws.EnvCreds(); err == nil {
		api.AWSEnvCreds = creds
		if c, err := creds.Credentials(); err == nil {
			api.clientConfig.HasAWSEnvCredentials = true
			api.clientConfig.AWSEnvCredentialsID = c.AccessKeyID
		}
	}

	httpRouter := httprouter.New()

	httpRouter.GET("/", api.ServeTemplate)
	httpRouter.GET("/credentials", api.ServeTemplate)
	httpRouter.GET("/credentials/:id", api.ServeTemplate)
	httpRouter.GET("/clusters/:id", api.ServeTemplate)
	httpRouter.GET("/clusters/:id/delete", api.ServeTemplate)
	httpRouter.GET("/oauth/azure", api.ServeTemplate)
	httpRouter.DELETE("/clusters/:id", api.DeleteCluster)
	httpRouter.POST("/clusters", api.LaunchCluster)
	httpRouter.GET("/events", api.Events)
	httpRouter.POST("/clusters/:id/prompts/:prompt_id", api.Prompt)
	httpRouter.GET("/assets/*assetPath", api.ServeAsset)
	httpRouter.POST("/credentials", api.NewCredential)
	httpRouter.DELETE("/credentials/:type/:id", api.DeleteCredential)
	httpRouter.GET("/regions", api.GetCloudRegions)
	httpRouter.GET("/azure/subscriptions", api.GetAzureSubscriptions)

	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return err
	}
	addr := fmt.Sprintf("http://localhost:%d", l.Addr().(*net.TCPAddr).Port)
	fmt.Printf("Open %s in your browser to continue.\n", addr)
	browser.OpenURL(addr)
	return http.Serve(l, api.CorsHandler(httpRouter, addr))
}

func (api *httpAPI) CorsHandler(main http.Handler, addr string) http.Handler {
	return (&cors.Options{
		AllowOrigins:     []string{addr},
		AllowMethods:     []string{"GET", "POST"},
		AllowHeaders:     []string{"Authorization", "Accept", "Content-Type", "If-Match", "If-None-Match"},
		ExposeHeaders:    []string{"ETag"},
		AllowCredentials: false,
		MaxAge:           time.Hour,
	}).Handler(main)
}

func (api *httpAPI) Asset(path string) (io.ReadSeeker, error) {
	data, err := Asset(path)
	if err != nil {
		return nil, err
	}
	return bytes.NewReader(data), nil
}

func (api *httpAPI) AssetManifest() (*assetManifest, error) {
	data, err := api.Asset(filepath.Join("app", "build", "manifest.json"))
	if err != nil {
		return nil, err
	}
	dec := json.NewDecoder(data)
	var manifest *assetManifest
	if err := dec.Decode(&manifest); err != nil {
		return nil, err
	}
	return manifest, nil
}

func (api *httpAPI) LaunchCluster(w http.ResponseWriter, req *http.Request, params httprouter.Params) {

	var inputJSON bytes.Buffer
	if _, err := inputJSON.ReadFrom(req.Body); err != nil {
		httphelper.Error(w, err)
		return
	}

	decodeJSON := func(dst interface{}) error {
		return json.Unmarshal(inputJSON.Bytes(), dst)
	}

	var base *BaseCluster
	if err := decodeJSON(&base); err != nil {
		httphelper.Error(w, err)
		return
	}

	if base.CredentialID == "" && base.Type != "ssh" {
		httphelper.ValidationError(w, "credential_id", "Missing credential id")
		return
	}

	var creds *Credential
	if base.Type == "aws" && base.CredentialID == "aws_env" {
		creds = &Credential{
			ID: base.CredentialID,
		}
	} else if base.Type != "ssh" {
		var err error
		creds, err = api.Installer.FindCredentials(base.CredentialID)
		if err != nil {
			httphelper.ValidationError(w, "credential_id", "Invalid credential id")
			return
		}
	}

	var cluster Cluster
	switch base.Type {
	case "aws":
		cluster = &AWSCluster{}
	case "digital_ocean":
		cluster = &DigitalOceanCluster{}
	case "azure":
		cluster = &AzureCluster{}
	case "ssh":
		cluster = &SSHCluster{}
	default:
		httphelper.ValidationError(w, "type", fmt.Sprintf("Invalid type \"%s\"", base.Type))
		return
	}

	base.ID = fmt.Sprintf("flynn-%d", time.Now().Unix())
	base.State = "starting"
	base.installer = api.Installer

	if err := decodeJSON(&cluster); err != nil {
		httphelper.Error(w, err)
		return
	}

	cluster.SetBase(base)

	if err := cluster.SetCreds(creds); err != nil {
		httphelper.Error(w, err)
		return
	}

	if err := api.Installer.LaunchCluster(cluster); err != nil {
		httphelper.Error(w, err)
		return
	}
	httphelper.JSON(w, 200, base)
}

func (api *httpAPI) DeleteCluster(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if err := api.Installer.DeleteCluster(params.ByName("id")); err != nil {
		if err == ClusterNotFoundError {
			httphelper.ObjectNotFoundError(w, err.Error())
			return
		}
		httphelper.Error(w, err)
		return
	}
	w.WriteHeader(200)
}

func (api *httpAPI) Events(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	eventChan := make(chan *Event)
	lastEventID := req.Header.Get("Last-Event-ID")
	sub := api.Installer.Subscribe(eventChan, lastEventID)
	defer api.Installer.Unsubscribe(sub)
	sse.ServeStream(w, eventChan, api.logger)
}

func (api *httpAPI) Prompt(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	s, err := api.Installer.FindBaseCluster(params.ByName("id"))
	if err != nil {
		httphelper.ObjectNotFoundError(w, "cluster not found")
		return
	}
	prompt, err := s.findPrompt(params.ByName("prompt_id"))
	if err != nil {
		httphelper.ObjectNotFoundError(w, "prompt not found")
		return
	}

	var input *Prompt
	if err := httphelper.DecodeJSON(req, &input); err != nil {
		httphelper.Error(w, err)
		return
	}
	prompt.Resolve(input)
	w.WriteHeader(200)
}

func (api *httpAPI) NewCredential(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	creds := &Credential{}
	if err := httphelper.DecodeJSON(req, &creds); err != nil {
		httphelper.Error(w, err)
		return
	}
	if creds.Type == "azure" {
		oauthCreds := make([]*OAuthCredential, 0, 2)
		for _, resource := range []string{azure.JSONAPIResource, azure.XMLAPIResource} {
			token, err := azure.OAuth2Config(creds.ID, creds.Endpoint, resource).Exchange(oauth2.NoContext, creds.Secret)
			if err != nil {
				httphelper.Error(w, err)
				return
			}
			oauthCreds = append(oauthCreds, &OAuthCredential{
				ClientID:     creds.ID,
				AccessToken:  token.AccessToken,
				RefreshToken: token.RefreshToken,
				ExpiresAt:    &token.Expiry,
				Scope:        resource,
			})
		}
		creds.Secret = ""
		creds.OAuthCreds = oauthCreds
	}
	if err := api.Installer.SaveCredentials(creds); err != nil {
		if err == credentialExistsError {
			httphelper.ObjectExistsError(w, err.Error())
			return
		}
		httphelper.Error(w, err)
		return
	}
	w.WriteHeader(200)
}

func (api *httpAPI) DeleteCredential(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if err := api.Installer.DeleteCredentials(params.ByName("id")); err != nil {
		httphelper.Error(w, err)
		return
	}
	w.WriteHeader(200)
}

func (api *httpAPI) GetCloudRegions(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	params := req.URL.Query()
	cloud := params.Get("cloud")
	if cloud != "digital_ocean" && cloud != "azure" {
		httphelper.ObjectNotFoundError(w, "")
		return
	}
	credentialID := params.Get("credential_id")
	creds, err := api.Installer.FindCredentials(credentialID)
	if err != nil {
		httphelper.ValidationError(w, "credential_id", "Invalid credential id")
		return
	}
	var res interface{}
	switch cloud {
	case "digital_ocean":
		res, err = api.Installer.ListDigitalOceanRegions(creds)
	case "azure":
		res, err = api.Installer.ListAzureRegions(creds)
	}
	if err != nil {
		httphelper.Error(w, err)
		return
	}
	httphelper.JSON(w, 200, res)
}

func (api *httpAPI) GetAzureSubscriptions(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	params := req.URL.Query()
	credentialID := params.Get("credential_id")
	creds, err := api.Installer.FindCredentials(credentialID)
	if err != nil {
		httphelper.ValidationError(w, "credential_id", "Invalid credential id")
		return
	}
	client := api.Installer.azureClient(creds)
	res, err := client.ListSubscriptions()
	if err != nil {
		httphelper.Error(w, err)
		return
	}
	httphelper.JSON(w, 200, res)
}

func (api *httpAPI) ServeApplicationJS(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	path := filepath.Join("app", "build", params.ByName("assetPath"))
	data, err := api.Asset(path)
	if err != nil {
		httphelper.Error(w, err)
		api.logger.Debug(err.Error())
		return
	}

	var jsConf bytes.Buffer
	jsConf.Write([]byte("window.InstallerConfig = "))
	json.NewEncoder(&jsConf).Encode(api.clientConfig)
	jsConf.Write([]byte(";\n"))

	r := ioutil.NewMultiReadSeeker(bytes.NewReader(jsConf.Bytes()), data)

	http.ServeContent(w, req, path, time.Now(), r)
}

func (api *httpAPI) ServeAsset(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if strings.HasPrefix(params.ByName("assetPath"), "/application-") && strings.HasSuffix(params.ByName("assetPath"), ".js") {
		api.ServeApplicationJS(w, req, params)
	} else {
		path := filepath.Join("app", "build", params.ByName("assetPath"))
		data, err := api.Asset(path)
		if err != nil {
			httphelper.Error(w, err)
			return
		}
		http.ServeContent(w, req, path, time.Now(), data)
	}
}

func (api *httpAPI) ServeTemplate(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if req.Header.Get("Accept") == "application/json" {
		s, err := api.Installer.FindBaseCluster(params.ByName("id"))
		if err != nil {
			httphelper.ObjectNotFoundError(w, err.Error())
			return
		}
		httphelper.JSON(w, 200, s)
		return
	}

	manifest, err := api.AssetManifest()
	if err != nil {
		httphelper.Error(w, err)
		api.logger.Debug(err.Error())
		return
	}

	w.Header().Add("Content-Type", "text/html; charset=utf-8")
	w.Header().Add("Cache-Control", "max-age=0")

	err = htmlTemplate.Execute(w, &htmlTemplateData{
		ApplicationJSPath:  manifest.Assets["application.js"],
		NormalizeCSSPath:   manifest.Assets["normalize.css"],
		FontAwesomeCSSPath: manifest.Assets["font-awesome.css"],
		ApplicationCSSPath: manifest.Assets["application.css"],
		ReactJSPath:        manifest.Assets["react.js"],
	})
	if err != nil {
		httphelper.Error(w, err)
		api.logger.Debug(err.Error())
		return
	}
}
<commit_msg>installer: Add redirect for /clusters -> /<commit_after>package installer

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/badgerodon/ioutil"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/oauth2"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/pkg/browser"
	log "github.com/flynn/flynn/Godeps/_workspace/src/gopkg.in/inconshreveable/log15.v2"
	"github.com/flynn/flynn/pkg/azure"
	"github.com/flynn/flynn/pkg/cors"
	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/flynn/flynn/pkg/sse"
)

type assetManifest struct {
	Assets map[string]string `json:"assets"`
}

type htmlTemplateData struct {
	ApplicationJSPath  string
	NormalizeCSSPath   string
	FontAwesomeCSSPath string
	ApplicationCSSPath string
	ReactJSPath        string
}

type installerJSConfig struct {
	Endpoints            map[string]string `json:"endpoints"`
	HasAWSEnvCredentials bool              `json:"has_aws_env_credentials"`
	AWSEnvCredentialsID  string            `json:"aws_env_credentials_id,omitempty"`
}

type httpAPI struct {
	AWSEnvCreds  aws.CredentialsProvider
	Installer    *Installer
	logger       log.Logger
	clientConfig installerJSConfig
}

func ServeHTTP() error {
	logger := log.New()
	installer := NewInstaller(logger)

	api := &httpAPI{
		Installer: installer,
		logger:    logger,
		clientConfig: installerJSConfig{
			Endpoints: map[string]string{
				"clusters":           "/clusters",
				"cluster":            "/clusters/:id",
				"events":             "/events",
				"prompt":             "/clusters/:id/prompts/:prompt_id",
				"credentials":        "/credentials",
				"regions":            "/regions",
				"azureSubscriptions": "/azure/subscriptions",
			},
		},
	}

	if creds, err := aws.EnvCreds(); err == nil {
		api.AWSEnvCreds = creds
		if c, err := creds.Credentials(); err == nil {
			api.clientConfig.HasAWSEnvCredentials = true
			api.clientConfig.AWSEnvCredentialsID = c.AccessKeyID
		}
	}

	httpRouter := httprouter.New()

	httpRouter.GET("/", api.ServeTemplate)
	httpRouter.GET("/credentials", api.ServeTemplate)
	httpRouter.GET("/credentials/:id", api.ServeTemplate)
	httpRouter.GET("/clusters/:id", api.ServeTemplate)
	httpRouter.GET("/clusters/:id/delete", api.ServeTemplate)
	httpRouter.GET("/oauth/azure", api.ServeTemplate)
	httpRouter.DELETE("/clusters/:id", api.DeleteCluster)
	httpRouter.GET("/clusters", api.RedirectRoot)
	httpRouter.POST("/clusters", api.LaunchCluster)
	httpRouter.GET("/events", api.Events)
	httpRouter.POST("/clusters/:id/prompts/:prompt_id", api.Prompt)
	httpRouter.GET("/assets/*assetPath", api.ServeAsset)
	httpRouter.POST("/credentials", api.NewCredential)
	httpRouter.DELETE("/credentials/:type/:id", api.DeleteCredential)
	httpRouter.GET("/regions", api.GetCloudRegions)
	httpRouter.GET("/azure/subscriptions", api.GetAzureSubscriptions)

	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return err
	}
	addr := fmt.Sprintf("http://localhost:%d", l.Addr().(*net.TCPAddr).Port)
	fmt.Printf("Open %s in your browser to continue.\n", addr)
	browser.OpenURL(addr)
	return http.Serve(l, api.CorsHandler(httpRouter, addr))
}

func (api *httpAPI) CorsHandler(main http.Handler, addr string) http.Handler {
	return (&cors.Options{
		AllowOrigins:     []string{addr},
		AllowMethods:     []string{"GET", "POST"},
		AllowHeaders:     []string{"Authorization", "Accept", "Content-Type", "If-Match", "If-None-Match"},
		ExposeHeaders:    []string{"ETag"},
		AllowCredentials: false,
		MaxAge:           time.Hour,
	}).Handler(main)
}

func (api *httpAPI) Asset(path string) (io.ReadSeeker, error) {
	data, err := Asset(path)
	if err != nil {
		return nil, err
	}
	return bytes.NewReader(data), nil
}

func (api *httpAPI) AssetManifest() (*assetManifest, error) {
	data, err := api.Asset(filepath.Join("app", "build", "manifest.json"))
	if err != nil {
		return nil, err
	}
	dec := json.NewDecoder(data)
	var manifest *assetManifest
	if err := dec.Decode(&manifest); err != nil {
		return nil, err
	}
	return manifest, nil
}

func (api *httpAPI) RedirectRoot(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	http.Redirect(w, req, "/", 302)
}

func (api *httpAPI) LaunchCluster(w http.ResponseWriter, req *http.Request, params httprouter.Params) {

	var inputJSON bytes.Buffer
	if _, err := inputJSON.ReadFrom(req.Body); err != nil {
		httphelper.Error(w, err)
		return
	}

	decodeJSON := func(dst interface{}) error {
		return json.Unmarshal(inputJSON.Bytes(), dst)
	}

	var base *BaseCluster
	if err := decodeJSON(&base); err != nil {
		httphelper.Error(w, err)
		return
	}

	if base.CredentialID == "" && base.Type != "ssh" {
		httphelper.ValidationError(w, "credential_id", "Missing credential id")
		return
	}

	var creds *Credential
	if base.Type == "aws" && base.CredentialID == "aws_env" {
		creds = &Credential{
			ID: base.CredentialID,
		}
	} else if base.Type != "ssh" {
		var err error
		creds, err = api.Installer.FindCredentials(base.CredentialID)
		if err != nil {
			httphelper.ValidationError(w, "credential_id", "Invalid credential id")
			return
		}
	}

	var cluster Cluster
	switch base.Type {
	case "aws":
		cluster = &AWSCluster{}
	case "digital_ocean":
		cluster = &DigitalOceanCluster{}
	case "azure":
		cluster = &AzureCluster{}
	case "ssh":
		cluster = &SSHCluster{}
	default:
		httphelper.ValidationError(w, "type", fmt.Sprintf("Invalid type \"%s\"", base.Type))
		return
	}

	base.ID = fmt.Sprintf("flynn-%d", time.Now().Unix())
	base.State = "starting"
	base.installer = api.Installer

	if err := decodeJSON(&cluster); err != nil {
		httphelper.Error(w, err)
		return
	}

	cluster.SetBase(base)

	if err := cluster.SetCreds(creds); err != nil {
		httphelper.Error(w, err)
		return
	}

	if err := api.Installer.LaunchCluster(cluster); err != nil {
		httphelper.Error(w, err)
		return
	}
	httphelper.JSON(w, 200, base)
}

func (api *httpAPI) DeleteCluster(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if err := api.Installer.DeleteCluster(params.ByName("id")); err != nil {
		if err == ClusterNotFoundError {
			httphelper.ObjectNotFoundError(w, err.Error())
			return
		}
		httphelper.Error(w, err)
		return
	}
	w.WriteHeader(200)
}

func (api *httpAPI) Events(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	eventChan := make(chan *Event)
	lastEventID := req.Header.Get("Last-Event-ID")
	sub := api.Installer.Subscribe(eventChan, lastEventID)
	defer api.Installer.Unsubscribe(sub)
	sse.ServeStream(w, eventChan, api.logger)
}

func (api *httpAPI) Prompt(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	s, err := api.Installer.FindBaseCluster(params.ByName("id"))
	if err != nil {
		httphelper.ObjectNotFoundError(w, "cluster not found")
		return
	}
	prompt, err := s.findPrompt(params.ByName("prompt_id"))
	if err != nil {
		httphelper.ObjectNotFoundError(w, "prompt not found")
		return
	}

	var input *Prompt
	if err := httphelper.DecodeJSON(req, &input); err != nil {
		httphelper.Error(w, err)
		return
	}
	prompt.Resolve(input)
	w.WriteHeader(200)
}

func (api *httpAPI) NewCredential(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	creds := &Credential{}
	if err := httphelper.DecodeJSON(req, &creds); err != nil {
		httphelper.Error(w, err)
		return
	}
	if creds.Type == "azure" {
		oauthCreds := make([]*OAuthCredential, 0, 2)
		for _, resource := range []string{azure.JSONAPIResource, azure.XMLAPIResource} {
			token, err := azure.OAuth2Config(creds.ID, creds.Endpoint, resource).Exchange(oauth2.NoContext, creds.Secret)
			if err != nil {
				httphelper.Error(w, err)
				return
			}
			oauthCreds = append(oauthCreds, &OAuthCredential{
				ClientID:     creds.ID,
				AccessToken:  token.AccessToken,
				RefreshToken: token.RefreshToken,
				ExpiresAt:    &token.Expiry,
				Scope:        resource,
			})
		}
		creds.Secret = ""
		creds.OAuthCreds = oauthCreds
	}
	if err := api.Installer.SaveCredentials(creds); err != nil {
		if err == credentialExistsError {
			httphelper.ObjectExistsError(w, err.Error())
			return
		}
		httphelper.Error(w, err)
		return
	}
	w.WriteHeader(200)
}

func (api *httpAPI) DeleteCredential(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if err := api.Installer.DeleteCredentials(params.ByName("id")); err != nil {
		httphelper.Error(w, err)
		return
	}
	w.WriteHeader(200)
}

func (api *httpAPI) GetCloudRegions(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	params := req.URL.Query()
	cloud := params.Get("cloud")
	if cloud != "digital_ocean" && cloud != "azure" {
		httphelper.ObjectNotFoundError(w, "")
		return
	}
	credentialID := params.Get("credential_id")
	creds, err := api.Installer.FindCredentials(credentialID)
	if err != nil {
		httphelper.ValidationError(w, "credential_id", "Invalid credential id")
		return
	}
	var res interface{}
	switch cloud {
	case "digital_ocean":
		res, err = api.Installer.ListDigitalOceanRegions(creds)
	case "azure":
		res, err = api.Installer.ListAzureRegions(creds)
	}
	if err != nil {
		httphelper.Error(w, err)
		return
	}
	httphelper.JSON(w, 200, res)
}

func (api *httpAPI) GetAzureSubscriptions(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	params := req.URL.Query()
	credentialID := params.Get("credential_id")
	creds, err := api.Installer.FindCredentials(credentialID)
	if err != nil {
		httphelper.ValidationError(w, "credential_id", "Invalid credential id")
		return
	}
	client := api.Installer.azureClient(creds)
	res, err := client.ListSubscriptions()
	if err != nil {
		httphelper.Error(w, err)
		return
	}
	httphelper.JSON(w, 200, res)
}

func (api *httpAPI) ServeApplicationJS(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	path := filepath.Join("app", "build", params.ByName("assetPath"))
	data, err := api.Asset(path)
	if err != nil {
		httphelper.Error(w, err)
		api.logger.Debug(err.Error())
		return
	}

	var jsConf bytes.Buffer
	jsConf.Write([]byte("window.InstallerConfig = "))
	json.NewEncoder(&jsConf).Encode(api.clientConfig)
	jsConf.Write([]byte(";\n"))

	r := ioutil.NewMultiReadSeeker(bytes.NewReader(jsConf.Bytes()), data)

	http.ServeContent(w, req, path, time.Now(), r)
}

func (api *httpAPI) ServeAsset(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if strings.HasPrefix(params.ByName("assetPath"), "/application-") && strings.HasSuffix(params.ByName("assetPath"), ".js") {
		api.ServeApplicationJS(w, req, params)
	} else {
		path := filepath.Join("app", "build", params.ByName("assetPath"))
		data, err := api.Asset(path)
		if err != nil {
			httphelper.Error(w, err)
			return
		}
		http.ServeContent(w, req, path, time.Now(), data)
	}
}

func (api *httpAPI) ServeTemplate(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	if req.Header.Get("Accept") == "application/json" {
		s, err := api.Installer.FindBaseCluster(params.ByName("id"))
		if err != nil {
			httphelper.ObjectNotFoundError(w, err.Error())
			return
		}
		httphelper.JSON(w, 200, s)
		return
	}

	manifest, err := api.AssetManifest()
	if err != nil {
		httphelper.Error(w, err)
		api.logger.Debug(err.Error())
		return
	}

	w.Header().Add("Content-Type", "text/html; charset=utf-8")
	w.Header().Add("Cache-Control", "max-age=0")

	err = htmlTemplate.Execute(w, &htmlTemplateData{
		ApplicationJSPath:  manifest.Assets["application.js"],
		NormalizeCSSPath:   manifest.Assets["normalize.css"],
		FontAwesomeCSSPath: manifest.Assets["font-awesome.css"],
		ApplicationCSSPath: manifest.Assets["application.css"],
		ReactJSPath:        manifest.Assets["react.js"],
	})
	if err != nil {
		httphelper.Error(w, err)
		api.logger.Debug(err.Error())
		return
	}
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package common

import (
	"github.com/webx-top/codec"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

// SetRandomSecret 设置随机密码
func SetRandomSecret(ctx echo.Context, sessionKey string, storeKey ...string) {
	secret := GenSecret(32)
	ctx.Session().Set(`secret_`+sessionKey, secret)
	if len(storeKey) > 0 {
		ctx.Set(storeKey[0], secret)
	} else {
		ctx.Set(sessionKey, secret)
	}
}

// DeleteRandomSecret 删除随机密码
func DeleteRandomSecret(ctx echo.Context, sessionKey string) {
	ctx.Session().Delete(`secret_` + sessionKey)
}

// DecryptedByRandomSecret 用上次设置的随机密码解密
func DecryptedByRandomSecret(ctx echo.Context, sessionKey string, datas ...*string) {
	secret := ctx.Session().Get(`secret_` + sessionKey)
	if secret != nil {
		secrets, _ := secret.(string)
		Decrypt(secrets, datas...)
	}
}

// Decrypt 数据解密
func Decrypt(secret string, datas ...*string) {
	crypto := codec.NewAesECBCrypto(`AES-256`)
	for _, data := range datas {
		if len(*data) == 0 {
			continue
		}
		*data = crypto.Decode(*data, secret)
	}
}

// Encrypt 数据加密
func Encrypt(secret string, datas ...*string) {
	crypto := codec.NewAesECBCrypto(`AES-256`)
	for _, data := range datas {
		if len(*data) == 0 {
			continue
		}
		*data = crypto.Encode(*data, secret)
	}
}

// GenSecret 生成随机密钥
func GenSecret(sizes ...int) string {
	var size int
	if len(sizes) > 0 {
		size = sizes[0]
	}
	if size < 1 {
		size = 32
	}
	secret := com.RandomAlphanumeric(32)
	return secret
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package common

import (
	"github.com/webx-top/codec"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

// SetRandomSecret 设置随机密码
func SetRandomSecret(ctx echo.Context, sessionKey string, storeKey ...string) {
	secret := GenSecret(32)
	ctx.Session().Set(`secret_`+sessionKey, secret)
	if len(storeKey) > 0 {
		ctx.Set(storeKey[0], secret)
	} else {
		ctx.Set(sessionKey, secret)
	}
}

// DeleteRandomSecret 删除随机密码
func DeleteRandomSecret(ctx echo.Context, sessionKey string) {
	ctx.Session().Delete(`secret_` + sessionKey)
}

// DecryptedByRandomSecret 用上次设置的随机密码解密
func DecryptedByRandomSecret(ctx echo.Context, sessionKey string, datas ...*string) {
	secret := ctx.Session().Get(`secret_` + sessionKey)
	if secret != nil {
		secrets, _ := secret.(string)
		Decrypt(secrets, datas...)
	}
}

// Decrypt 数据解密
func Decrypt(secret string, datas ...*string) {
	crypto := codec.NewAesECBCrypto(`AES-256`)
	for _, data := range datas {
		if len(*data) == 0 {
			continue
		}
		*data = crypto.Decode(*data, secret)
	}
}

// Encrypt 数据加密
func Encrypt(secret string, datas ...*string) {
	crypto := codec.NewAesECBCrypto(`AES-256`)
	for _, data := range datas {
		if len(*data) == 0 {
			continue
		}
		*data = crypto.Encode(*data, secret)
	}
}

// GenSecret 生成随机密钥
func GenSecret(sizes ...uint) string {
	var size uint
	if len(sizes) > 0 {
		size = sizes[0]
	}
	if size < 1 {
		size = 32
	}
	return com.RandomAlphanumeric(size)
}
<|endoftext|>


<commit_before>package routers

import (
	"fmt"
	"github.com/jpgneves/shorty/requests"
	"github.com/jpgneves/shorty/resources"
	"net/http"
)

type RoutingHandler struct {
	router Router
}

func MakeRoutingHandler(router Router) *RoutingHandler {
	return &RoutingHandler{router}
}

func (rh *RoutingHandler) SetRouter(r Router) {
	rh.router = r
}

func (rh *RoutingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var response *requests.Response
	if rh.router != nil {
		path := r.URL.Path
		match := rh.router.Route(path)
		if resource, ok := match.value.(resources.Resource); ok {
			req := &requests.Request{r, match.matches}
			switch r.Method {
			case "GET":
				response = resource.Get(req)
			case "POST":
				response = resource.Post(req)
			default:
				w.WriteHeader(http.StatusMethodNotAllowed)
				return
			}
			w.WriteHeader(response.StatusCode)
			fmt.Fprintf(w, response.Data)
		} else {
			w.WriteHeader(http.StatusNotFound)
		}
	} else {
		w.WriteHeader(http.StatusNotFound)
	}
	return
}
<commit_msg>Generalize request handling a bit more<commit_after>package routers

import (
	"fmt"
	"github.com/jpgneves/shorty/requests"
	"github.com/jpgneves/shorty/resources"
	"net/http"
)

type RoutingHandler struct {
	router Router
}

func MakeRoutingHandler(router Router) *RoutingHandler {
	return &RoutingHandler{router}
}

func (rh *RoutingHandler) SetRouter(r Router) {
	rh.router = r
}

func (rh *RoutingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var response *requests.Response
	if rh.router != nil {
		path := r.URL.Path
		match := rh.router.Route(path)
		if resource, ok := match.value.(resources.Resource); ok {
			req := &requests.Request{r, match.matches}
			switch r.Method {
			case "GET":
				response = resource.Get(req)
			case "POST":
				response = resource.Post(req)
			default:
				handleError(w, http.StatusMethodNotAllowed)
				return
			}
			handleResponse(w, r, response)
		} else {
			handleError(w, http.StatusNotFound)
		}
	} else {
		handleError(w, http.StatusNotFound)
	}
	return
}

func handleResponse(w http.ResponseWriter, r *http.Request, response *requests.Response) {
	switch response.StatusCode {
	case http.StatusTemporaryRedirect:
		fallthrough
	case http.StatusMovedPermanently:
		http.Redirect(w, r, response.Data, response.StatusCode)
	default:
		w.WriteHeader(response.StatusCode)
		fmt.Fprintf(w, response.Data)
	}
}

func handleError(w http.ResponseWriter, code int) {
	w.WriteHeader(code)
	fmt.Fprintf(w, http.StatusText(code))
}<|endoftext|>


<commit_before>package skyring

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/skyrings/skyring-common/conf"
	"github.com/skyrings/skyring-common/db"
	"github.com/skyrings/skyring-common/models"
	"github.com/skyrings/skyring-common/tools/logger"
	"github.com/skyrings/skyring-common/tools/uuid"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	rootTaskId = "00000000-0000-0000-0000-000000000000"
)

func (a *App) getTasks(rw http.ResponseWriter, req *http.Request) {
	ctxt, err := GetContext(req)
	if err != nil {
		logger.Get().Error("Error Getting the context. error: %v", err)
	}

	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()

	var filter bson.M = make(map[string]interface{})

	rootTask := req.URL.Query().Get("level")
	taskStatusArray := req.URL.Query()["state"]
	searchMessage := req.URL.Query().Get("searchmessage")

	if len(rootTask) != 0 {
		if strings.ToLower(rootTask) == "root" {
			filter["parentid"], err = uuid.Parse(rootTaskId)
			if err != nil {
				logger.Get().Error("%s-Unable to Parse the Id: %s. error: %v", ctxt, rootTaskId, err)
				HandleHttpError(rw, err)
				return
			}
		} else {
			logger.Get().Error("%s-Un-supported query param: %v", ctxt, rootTask)
			HttpResponse(rw, http.StatusInternalServerError, fmt.Sprintf("Un-supported query param: %s", rootTask))
			return
		}
	}

	taskStatusMap := make(map[string]string)
	for _, taskStatus := range taskStatusArray {
		taskStatusMap[strings.ToLower(taskStatus)] = taskStatus
	}

	if len(taskStatusMap) != 0 {
		_, inprogressErr := taskStatusMap["inprogress"]
		_, completedErr := taskStatusMap["completed"]
		_, failedErr := taskStatusMap["failed"]

		if inprogressErr && !completedErr {
			filter["completed"] = false
		}
		if !inprogressErr && completedErr {
			filter["completed"] = true
		}
		if failedErr {
			filter["status"] = models.TASK_STATUS_FAILURE
		}
	}
	if len(searchMessage) != 0 {
		filter["name"] = bson.M{"$regex": searchMessage, "$options": "$i"}
	}
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var tasks []models.AppTask
	pageNo, pageNoErr := strconv.Atoi(req.URL.Query().Get("pageNo"))
	pageSize, pageSizeErr := strconv.Atoi(req.URL.Query().Get("pageSize"))
	// time stamp format of RFC3339 = "2006-01-02T15:04:05Z07:00"
	fromDateTime, fromDateTimeErr := time.Parse(time.RFC3339, req.URL.Query().Get("fromdatetime"))
	toDateTime, toDateTimeErr := time.Parse(time.RFC3339, req.URL.Query().Get("todatetime"))
	if fromDateTimeErr == nil && toDateTimeErr == nil {
		filter["lastupdated"] = bson.M{
			"$gt": fromDateTime.UTC(),
			"$lt": toDateTime.UTC(),
		}
	} else if fromDateTimeErr != nil && toDateTimeErr == nil {
		filter["lastupdated"] = bson.M{
			"$lt": toDateTime.UTC(),
		}
	} else if fromDateTimeErr == nil && toDateTimeErr != nil {
		filter["lastupdated"] = bson.M{
			"$gt": fromDateTime.UTC(),
		}
	}
	if err := coll.Find(filter).Sort("-completed", "lastupdated").All(&tasks); err != nil {
		logger.Get().Error("%s-Unable to get tasks. error: %v", ctxt, err)
		HttpResponse(rw, http.StatusInternalServerError, err.Error())
		return
	}

	if len(tasks) == 0 {
		json.NewEncoder(rw).Encode([]models.AppTask{})
	} else {
		var startIndex int = 0
		var endIndex int = models.TASKS_PER_PAGE
		if pageNoErr == nil && pageSizeErr == nil {
			startIndex, endIndex = Paginate(pageNo, pageSize, models.TASKS_PER_PAGE)
		}
		if endIndex > len(tasks) {
			endIndex = len(tasks) - 1
		}

		json.NewEncoder(rw).Encode(
			struct {
				Totalcount int              `json:"totalcount"`
				Startindex int              `json:"startindex"`
				Endindex   int              `json:"endindex"`
				Tasks      []models.AppTask `json:"tasks"`
			}{len(tasks), startIndex, endIndex, tasks[startIndex : endIndex+1]})

	}
}

func (a *App) getTask(rw http.ResponseWriter, req *http.Request) {
	ctxt, err := GetContext(req)
	if err != nil {
		logger.Get().Error("Error Getting the context. error: %v", err)
	}

	vars := mux.Vars(req)
	taskId, err := uuid.Parse(vars["taskid"])
	if err != nil {
		logger.Get().Error("%s-Unable to Parse the Id: %s. error: %v", ctxt, vars["taskId"], err)
		HandleHttpError(rw, err)
		return
	}

	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": *taskId}).One(&task); err != nil {
		logger.Get().Error("%s-Unable to get task. error: %v", ctxt, err)
		if err == mgo.ErrNotFound {
			HttpResponse(rw, http.StatusNotFound, err.Error())
			return
		} else {
			logger.Get().Error("%s-Error getting the task details for %v. error: %v", ctxt, *taskId, err)
			HttpResponse(rw, http.StatusInternalServerError, err.Error())
			return
		}
	}
	json.NewEncoder(rw).Encode(task)
}

func (a *App) getSubTasks(rw http.ResponseWriter, req *http.Request) {
	ctxt, err := GetContext(req)
	if err != nil {
		logger.Get().Error("Error Getting the context. error: %v", err)
	}

	vars := mux.Vars(req)
	taskId, err := uuid.Parse(vars["taskid"])
	if err != nil {
		logger.Get().Error("%s-Unable to Parse the Id: %s. error: %v", ctxt, vars["taskId"], err)
		HandleHttpError(rw, err)
		return
	}

	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var tasks []models.AppTask
	if err := coll.Find(bson.M{"parentid": *taskId}).All(&tasks); err != nil {
		logger.Get().Error("%s-Unable to get tasks. error: %v", ctxt, err)
		HttpResponse(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if len(tasks) == 0 {
		json.NewEncoder(rw).Encode([]models.AppTask{})
	} else {
		json.NewEncoder(rw).Encode(tasks)
	}
}
<commit_msg>Add timeout tasks to filter and sorts tasks<commit_after>package skyring

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/skyrings/skyring-common/conf"
	"github.com/skyrings/skyring-common/db"
	"github.com/skyrings/skyring-common/models"
	"github.com/skyrings/skyring-common/tools/logger"
	"github.com/skyrings/skyring-common/tools/uuid"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	rootTaskId = "00000000-0000-0000-0000-000000000000"
)

func (a *App) getTasks(rw http.ResponseWriter, req *http.Request) {
	ctxt, err := GetContext(req)
	if err != nil {
		logger.Get().Error("Error Getting the context. error: %v", err)
	}

	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()

	var filter bson.M = make(map[string]interface{})

	rootTask := req.URL.Query().Get("level")
	taskStatusArray := req.URL.Query()["state"]
	searchMessage := req.URL.Query().Get("searchmessage")

	if len(rootTask) != 0 {
		if strings.ToLower(rootTask) == "root" {
			filter["parentid"], err = uuid.Parse(rootTaskId)
			if err != nil {
				logger.Get().Error("%s-Unable to Parse the Id: %s. error: %v", ctxt, rootTaskId, err)
				HandleHttpError(rw, err)
				return
			}
		} else {
			logger.Get().Error("%s-Un-supported query param: %v", ctxt, rootTask)
			HttpResponse(rw, http.StatusInternalServerError, fmt.Sprintf("Un-supported query param: %s", rootTask))
			return
		}
	}

	taskStatusMap := make(map[string]string)
	for _, taskStatus := range taskStatusArray {
		taskStatusMap[strings.ToLower(taskStatus)] = taskStatus
	}

	if len(taskStatusMap) != 0 {
		_, inprogressErr := taskStatusMap["inprogress"]
		_, completedErr := taskStatusMap["completed"]
		_, failedErr := taskStatusMap["failed"]

		if inprogressErr && !completedErr {
			filter["completed"] = false
		}
		if !inprogressErr && completedErr {
			filter["completed"] = true
		}
		if failedErr {
			var arr []interface{}
			arr = append(arr, bson.M{"status": models.TASK_STATUS_FAILURE})
			arr = append(arr, bson.M{"status": models.TASK_STATUS_TIMED_OUT})
			filter["$or"] = arr
		}
	}
	if len(searchMessage) != 0 {
		filter["name"] = bson.M{"$regex": searchMessage, "$options": "$i"}
	}
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var tasks []models.AppTask
	pageNo, pageNoErr := strconv.Atoi(req.URL.Query().Get("pageNo"))
	pageSize, pageSizeErr := strconv.Atoi(req.URL.Query().Get("pageSize"))
	// time stamp format of RFC3339 = "2006-01-02T15:04:05Z07:00"
	fromDateTime, fromDateTimeErr := time.Parse(time.RFC3339, req.URL.Query().Get("fromdatetime"))
	toDateTime, toDateTimeErr := time.Parse(time.RFC3339, req.URL.Query().Get("todatetime"))
	if fromDateTimeErr == nil && toDateTimeErr == nil {
		filter["lastupdated"] = bson.M{
			"$gt": fromDateTime.UTC(),
			"$lt": toDateTime.UTC(),
		}
	} else if fromDateTimeErr != nil && toDateTimeErr == nil {
		filter["lastupdated"] = bson.M{
			"$lt": toDateTime.UTC(),
		}
	} else if fromDateTimeErr == nil && toDateTimeErr != nil {
		filter["lastupdated"] = bson.M{
			"$gt": fromDateTime.UTC(),
		}
	}
	if err := coll.Find(filter).Sort("completed", "-lastupdated").All(&tasks); err != nil {
		logger.Get().Error("%s-Unable to get tasks. error: %v", ctxt, err)
		HttpResponse(rw, http.StatusInternalServerError, err.Error())
		return
	}

	if len(tasks) == 0 {
		json.NewEncoder(rw).Encode([]models.AppTask{})
	} else {
		var startIndex int = 0
		var endIndex int = models.TASKS_PER_PAGE
		if pageNoErr == nil && pageSizeErr == nil {
			startIndex, endIndex = Paginate(pageNo, pageSize, models.TASKS_PER_PAGE)
		}
		if endIndex > len(tasks) {
			endIndex = len(tasks) - 1
		}

		json.NewEncoder(rw).Encode(
			struct {
				Totalcount int              `json:"totalcount"`
				Startindex int              `json:"startindex"`
				Endindex   int              `json:"endindex"`
				Tasks      []models.AppTask `json:"tasks"`
			}{len(tasks), startIndex, endIndex, tasks[startIndex : endIndex+1]})

	}
}

func (a *App) getTask(rw http.ResponseWriter, req *http.Request) {
	ctxt, err := GetContext(req)
	if err != nil {
		logger.Get().Error("Error Getting the context. error: %v", err)
	}

	vars := mux.Vars(req)
	taskId, err := uuid.Parse(vars["taskid"])
	if err != nil {
		logger.Get().Error("%s-Unable to Parse the Id: %s. error: %v", ctxt, vars["taskId"], err)
		HandleHttpError(rw, err)
		return
	}

	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var task models.AppTask
	if err := coll.Find(bson.M{"id": *taskId}).One(&task); err != nil {
		logger.Get().Error("%s-Unable to get task. error: %v", ctxt, err)
		if err == mgo.ErrNotFound {
			HttpResponse(rw, http.StatusNotFound, err.Error())
			return
		} else {
			logger.Get().Error("%s-Error getting the task details for %v. error: %v", ctxt, *taskId, err)
			HttpResponse(rw, http.StatusInternalServerError, err.Error())
			return
		}
	}
	json.NewEncoder(rw).Encode(task)
}

func (a *App) getSubTasks(rw http.ResponseWriter, req *http.Request) {
	ctxt, err := GetContext(req)
	if err != nil {
		logger.Get().Error("Error Getting the context. error: %v", err)
	}

	vars := mux.Vars(req)
	taskId, err := uuid.Parse(vars["taskid"])
	if err != nil {
		logger.Get().Error("%s-Unable to Parse the Id: %s. error: %v", ctxt, vars["taskId"], err)
		HandleHttpError(rw, err)
		return
	}

	sessionCopy := db.GetDatastore().Copy()
	defer sessionCopy.Close()
	coll := sessionCopy.DB(conf.SystemConfig.DBConfig.Database).C(models.COLL_NAME_TASKS)
	var tasks []models.AppTask
	if err := coll.Find(bson.M{"parentid": *taskId}).All(&tasks); err != nil {
		logger.Get().Error("%s-Unable to get tasks. error: %v", ctxt, err)
		HttpResponse(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if len(tasks) == 0 {
		json.NewEncoder(rw).Encode([]models.AppTask{})
	} else {
		json.NewEncoder(rw).Encode(tasks)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2022 The Skaffold Authors

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 helm

import (
	"bytes"
	"context"
	"fmt"
	"io"

	apimachinery "k8s.io/apimachinery/pkg/runtime/schema"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/helm"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/generate"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/util"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	sUtil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

type Helm struct {
	configName string
	generate.Generator
	config *latest.Helm

	kubeContext string
	kubeConfig  string
	namespace   string
	configFile  string
	labels      map[string]string
	enableDebug bool

	transformAllowlist map[apimachinery.GroupKind]latest.ResourceFilter
	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
}

func (h Helm) EnableDebug() bool         { return h.enableDebug }
func (h Helm) ConfigFile() string        { return h.configFile }
func (h Helm) KubeContext() string       { return h.kubeContext }
func (h Helm) KubeConfig() string        { return h.kubeConfig }
func (h Helm) Labels() map[string]string { return h.labels }
func (h Helm) GlobalFlags() []string     { return h.config.Flags.Global }

func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string) (Helm, error) {
	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate)
	transformAllowlist, transformDenylist, err := util.ConsolidateTransformConfiguration(cfg)
	if err != nil {
		return Helm{}, err
	}
	return Helm{
		configName: configName,
		Generator:  generator,
		config:     rCfg.Helm,

		enableDebug: cfg.Mode() == config.RunModes.Debug,
		configFile:  cfg.ConfigurationFile(),
		kubeContext: cfg.GetKubeContext(),
		kubeConfig:  cfg.GetKubeConfig(),
		labels:      labels,

		transformAllowlist: transformAllowlist,
		transformDenylist:  transformDenylist,
	}, nil
}

func (h Helm) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, _ bool) (manifest.ManifestListByConfig, error) {
	_, endTrace := instrumentation.StartTrace(ctx, "Render_HelmManifests")
	log.Entry(ctx).Infof("rendering using helm")
	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
		"RendererType": "helm",
	})

	manifests, err := h.generateHelmManifests(ctx, builds)
	endTrace()
	manifestListByConfig := manifest.NewManifestListByConfig()
	manifestListByConfig.Add(h.configName, manifests)
	return manifestListByConfig, err
}

func (h Helm) generateHelmManifests(ctx context.Context, builds []graph.Artifact) (manifest.ManifestList, error) {
	var renderedManifests manifest.ManifestList
	helmEnv := sUtil.OSEnviron()
	var postRendererArgs []string

	if len(builds) > 0 {
		skaffoldBinary, filterEnv, cleanup, err := helm.PrepareSkaffoldFilter(h, builds)
		if err != nil {
			return nil, fmt.Errorf("could not prepare `skaffold filter`: %w", err)
		}
		// need to include current environment, specifically for HOME to lookup ~/.kube/config
		helmEnv = append(helmEnv, filterEnv...)
		postRendererArgs = []string{"--post-renderer", skaffoldBinary}
		defer cleanup()
	}

	for _, release := range h.config.Releases {
		releaseName, err := sUtil.ExpandEnvTemplateOrFail(release.Name, nil)
		if err != nil {
			return nil, helm.UserErr(fmt.Sprintf("cannot expand release name %q", release.Name), err)
		}

		args := []string{"template", releaseName, helm.ChartSource(release)}
		args = append(args, postRendererArgs...)
		if release.Packaged == nil && release.Version != "" {
			args = append(args, "--version", release.Version)
		}

		args, err = helm.ConstructOverrideArgs(&release, builds, args)
		if err != nil {
			return nil, helm.UserErr("construct override args", err)
		}

		namespace, err := helm.ReleaseNamespace(h.namespace, release)
		if err != nil {
			return nil, err
		}
		if namespace != "" {
			args = append(args, "--namespace", namespace)
		}

		if release.Repo != "" {
			args = append(args, "--repo")
			args = append(args, release.Repo)
		}

		outBuffer := new(bytes.Buffer)
		errBuffer := new(bytes.Buffer)
		if err := helm.ExecWithStdoutAndStderr(ctx, h, outBuffer, errBuffer, false, helmEnv, args...); err != nil {
			return nil, helm.UserErr("std out err", fmt.Errorf(outBuffer.String()))
		}
		log.Entry(ctx).Errorf(errBuffer.String())
		renderedManifests.Append(outBuffer.Bytes())
	}

	manifests, err := renderedManifests.SetLabels(h.labels, manifest.NewResourceSelectorLabels(h.transformAllowlist, h.transformDenylist))
	if err != nil {
		return nil, err
	}

	return manifests, nil
}
<commit_msg>fix: resolve issue where helm render did not respect --namespace flag (#7907)<commit_after>/*
Copyright 2022 The Skaffold Authors

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 helm

import (
	"bytes"
	"context"
	"fmt"
	"io"

	apimachinery "k8s.io/apimachinery/pkg/runtime/schema"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/helm"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/generate"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/util"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	sUtil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

type Helm struct {
	configName string
	generate.Generator
	config *latest.Helm

	kubeContext string
	kubeConfig  string
	namespace   string
	configFile  string
	labels      map[string]string
	enableDebug bool

	transformAllowlist map[apimachinery.GroupKind]latest.ResourceFilter
	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
}

func (h Helm) EnableDebug() bool         { return h.enableDebug }
func (h Helm) ConfigFile() string        { return h.configFile }
func (h Helm) KubeContext() string       { return h.kubeContext }
func (h Helm) KubeConfig() string        { return h.kubeConfig }
func (h Helm) Labels() map[string]string { return h.labels }
func (h Helm) GlobalFlags() []string     { return h.config.Flags.Global }

func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string) (Helm, error) {
	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate)
	transformAllowlist, transformDenylist, err := util.ConsolidateTransformConfiguration(cfg)
	if err != nil {
		return Helm{}, err
	}
	return Helm{
		configName: configName,
		Generator:  generator,
		config:     rCfg.Helm,

		enableDebug: cfg.Mode() == config.RunModes.Debug,
		configFile:  cfg.ConfigurationFile(),
		kubeContext: cfg.GetKubeContext(),
		kubeConfig:  cfg.GetKubeConfig(),
		labels:      labels,
		namespace:   cfg.GetNamespace(),

		transformAllowlist: transformAllowlist,
		transformDenylist:  transformDenylist,
	}, nil
}

func (h Helm) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, _ bool) (manifest.ManifestListByConfig, error) {
	_, endTrace := instrumentation.StartTrace(ctx, "Render_HelmManifests")
	log.Entry(ctx).Infof("rendering using helm")
	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
		"RendererType": "helm",
	})

	manifests, err := h.generateHelmManifests(ctx, builds)
	endTrace()
	manifestListByConfig := manifest.NewManifestListByConfig()
	manifestListByConfig.Add(h.configName, manifests)
	return manifestListByConfig, err
}

func (h Helm) generateHelmManifests(ctx context.Context, builds []graph.Artifact) (manifest.ManifestList, error) {
	var renderedManifests manifest.ManifestList
	helmEnv := sUtil.OSEnviron()
	var postRendererArgs []string

	if len(builds) > 0 {
		skaffoldBinary, filterEnv, cleanup, err := helm.PrepareSkaffoldFilter(h, builds)
		if err != nil {
			return nil, fmt.Errorf("could not prepare `skaffold filter`: %w", err)
		}
		// need to include current environment, specifically for HOME to lookup ~/.kube/config
		helmEnv = append(helmEnv, filterEnv...)
		postRendererArgs = []string{"--post-renderer", skaffoldBinary}
		defer cleanup()
	}

	for _, release := range h.config.Releases {
		releaseName, err := sUtil.ExpandEnvTemplateOrFail(release.Name, nil)
		if err != nil {
			return nil, helm.UserErr(fmt.Sprintf("cannot expand release name %q", release.Name), err)
		}

		args := []string{"template", releaseName, helm.ChartSource(release)}
		args = append(args, postRendererArgs...)
		if release.Packaged == nil && release.Version != "" {
			args = append(args, "--version", release.Version)
		}

		args, err = helm.ConstructOverrideArgs(&release, builds, args)
		if err != nil {
			return nil, helm.UserErr("construct override args", err)
		}

		namespace, err := helm.ReleaseNamespace(h.namespace, release)
		if err != nil {
			return nil, err
		}
		if h.namespace != "" {
			namespace = h.namespace
		}
		if namespace != "" {
			args = append(args, "--namespace", namespace)
		}

		if release.Repo != "" {
			args = append(args, "--repo")
			args = append(args, release.Repo)
		}

		outBuffer := new(bytes.Buffer)
		errBuffer := new(bytes.Buffer)
		if err := helm.ExecWithStdoutAndStderr(ctx, h, outBuffer, errBuffer, false, helmEnv, args...); err != nil {
			return nil, helm.UserErr("std out err", fmt.Errorf(outBuffer.String()))
		}
		log.Entry(ctx).Errorf(errBuffer.String())
		renderedManifests.Append(outBuffer.Bytes())
	}

	manifests, err := renderedManifests.SetLabels(h.labels, manifest.NewResourceSelectorLabels(h.transformAllowlist, h.transformDenylist))
	if err != nil {
		return nil, err
	}

	return manifests, nil
}
<|endoftext|>


<commit_before>package formjsonschema

import (
	"github.com/herb-go/herb/ui"
	"github.com/herb-go/gojsonschema"
	"github.com/herb-go/herb/ui/validator/formdata"
)

type Schema struct {
	schema *gojsonschema.Schema
}

func (s *Schema) ValidateJSON(data []byte) ([]*Result, error) {
	results, err := s.schema.Validate(gojsonschema.NewBytesLoader(data))
	if err != nil {
		return nil, err
	}
	rerrs := results.Errors()
	r := make([]*Result, len(rerrs))
	for k, v := range rerrs {
		r[k] = &Result{
			result: v,
		}
	}
	return r, nil
}

type Result struct {
	result gojsonschema.ResultError
}

func (r *Result) Field() string {
	return r.result.EncodedPointer()
}

func (r *Result) Label() string {
	return r.result.SchemaTitle()
}

func (r *Result) Message() string {
	return r.result.DescriptionFormat()
}

func (r *Result) MessageData() map[string]string {
	data := map[string]string{}
	for k, v := range r.result.Details() {
		str, ok := v.(string)
		if ok {
			data[k] = str
		}
	}
	return data
}

func (s *Schema) Validate(f formdata.RequestValidator, data []byte) error {
	rs, err := s.ValidateJSON(data)
	if err != nil {
		return err
	}
	for k := range rs {
		f.AddError(ui.Replace())
	}
	return nil
}
<commit_msg>update<commit_after>package formjsonschema

import (
	"github.com/herb-go/gojsonschema"
	"github.com/herb-go/herb/ui/validator/formdata"
)

type Schema struct {
	schema *gojsonschema.Schema
}

func (s *Schema) ValidateJSON(data []byte) ([]*Result, error) {
	results, err := s.schema.Validate(gojsonschema.NewBytesLoader(data))
	if err != nil {
		return nil, err
	}
	rerrs := results.Errors()
	r := make([]*Result, len(rerrs))
	for k, v := range rerrs {
		r[k] = &Result{
			result: v,
		}
	}
	return r, nil
}

type Result struct {
	result gojsonschema.ResultError
}

func (r *Result) Field() string {
	return r.result.EncodedPointer()
}

func (r *Result) Label() string {
	return r.result.SchemaTitle()
}

func (r *Result) Message() string {
	return r.result.DescriptionFormat()
}

func (r *Result) MessageData() map[string]string {
	data := map[string]string{}
	for k, v := range r.result.Details() {
		str, ok := v.(string)
		if ok {
			data[k] = str
		}
	}
	return data
}

func (s *Schema) Validate(f formdata.RequestValidator, data []byte) error {
	// rs, err := s.ValidateJSON(data)
	// if err != nil {
	// 	return err
	// }
	// for k := range rs {
	// 	f.AddError(ui.Replace())
	// }
	return nil
}
<|endoftext|>