<commit_before>//
// Package eprinttools is a collection of structures and functions for working with the E-Prints REST API
//
// @author R. S. Doiel, <rsdoiel@caltech.edu>
//
// Copyright (c) 2017, Caltech
// All rights not granted herein are expressly reserved by Caltech.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
package harvest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	// CaltechLibrary packages
	"github.com/caltechlibrary/dataset"
	"github.com/caltechlibrary/eprinttools"
)

var (
	// EPrintsExportBatchSize sets the summary output frequency when exporting content from E-Prints
	EPrintsExportBatchSize = 1000
)

type byURI []string

func (s byURI) Len() int {
	return len(s)
}

func (s byURI) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s byURI) Less(i, j int) bool {
	var (
		a1  int
		a2  int
		err error
	)
	s1 := strings.TrimSuffix(path.Base(s[i]), path.Ext(s[i]))
	s2 := strings.TrimSuffix(path.Base(s[j]), path.Ext(s[j]))
	a1, err = strconv.Atoi(s1)
	if err != nil {
		return false
	}
	a2, err = strconv.Atoi(s2)
	if err != nil {
		return false
	}
	//NOTE: We're creating a descending sort, so a1 should be larger than a2
	return a1 > a2
}

// ExportEPrintsKeyList export a list of eprints from a list of keys
func ExportEPrintsKeyList(api *eprinttools.EPrintsAPI, keys []string, saveKeys string, verbose bool) error {
	var (
		exportedKeys []string
		err          error
		src          []byte
	)

	c, err := dataset.Open(api.Dataset)
	if err != nil {
		return fmt.Errorf("ExportEPrintsKeyList() %s, %s", api.Dataset, err)
	}
	defer c.Close()

	uris := []string{}
	for _, key := range keys {
		uri := fmt.Sprintf("/rest/eprint/%s.xml", strings.TrimSpace(key))
		uris = append(uris, uri)
	}

	pid := os.Getpid()
	uriCount := len(uris)
	count := uriCount
	j := 0 // success count
	k := 0 // error count
	if verbose == true {
		log.Printf("(pid: %d) Exporting %d of %d uris", pid, count, uriCount)
	}
	for i := 0; i < uriCount && i < count; i++ {
		uri := uris[i]
		rec, xmlSrc, err := api.GetEPrint(uri)
		if err != nil {
			log.Printf("(pid: %d) Failed, %s\n", pid, err)
			k++
		} else {
			key := fmt.Sprintf("%d", rec.EPrintID)
			src, err = json.Marshal(rec)
			if err != nil {
				log.Printf("(pid: %d) can't marshal key %s, %s", pid, key, err)
			} else {
				// NOTE: Check to see if we're doing an update or create
				if c.HasKey(key) == true {
					err = c.UpdateJSON(key, src)
				} else {
					err = c.CreateJSON(key, src)
				}
			}
			if err == nil {
				if len(saveKeys) > 0 {
					exportedKeys = append(exportedKeys, key)
				}
				// We've exported a record successfully, now update select lists
				j++
			} else {
				if verbose == true {
					log.Printf("(pid: %d) Failed to save eprint %s (%s) to %s, %s\n", pid, key, uri, api.Dataset, err)
				}
				k++
			}
			c.AttachFile(key, key+".xml", bytes.NewReader(xmlSrc))
		}
		if verbose == true && (i%EPrintsExportBatchSize) == 0 {
			log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i+1, count, j, k)
		}
	}
	if verbose == true {
		log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, len(uris), count, j, k)
	}
	if len(saveKeys) > 0 {
		if err := ioutil.WriteFile(saveKeys, []byte(strings.Join(exportedKeys, "\n")), 0664); err != nil {
			return fmt.Errorf("Failed to export %s, %s", saveKeys, err)
		}
	}
	return nil
}

// ExportEPrints from highest ID to lowest for cnt. Saves each record in a DB and indexes published ones
func ExportEPrints(api *eprinttools.EPrintsAPI, count int, saveKeys string, verbose bool) error {
	var (
		exportedKeys []string
		err          error
		src          []byte
	)

	c, err := dataset.Open(api.Dataset)
	if err != nil {
		return fmt.Errorf("ExportEPrints() %s, %s", api.Dataset, err)
	}
	defer c.Close()

	uris, err := api.ListEPrintsURI()
	if err != nil {
		return fmt.Errorf("Export %s failed, %s", api.URL.String(), err)
	}

	// NOTE: I am sorting the URI by decscending ID number so that the
	// newest articles are exported first
	sort.Sort(byURI(uris))

	pid := os.Getpid()
	uriCount := len(uris)
	if count < 0 {
		count = uriCount
	}
	j := 0 // success count
	k := 0 // error count
	if verbose == true {
		log.Printf("(pid: %d) Exporting %d of %d uris", pid, count, uriCount)
	}
	i := 0
	for i = 0; i < uriCount && i < count; i++ {
		uri := uris[i]
		rec, xmlSrc, err := api.GetEPrint(uri)
		if err != nil {
			log.Printf("(pid: %d) Failed, %s\n", pid, err)
			k++
		} else {
			key := fmt.Sprintf("%d", rec.EPrintID)
			src, err = json.Marshal(rec)
			if err != nil {
				log.Printf("(pid: %d) Can't marshal key %s, %s", pid, key, err)
			} else {
				// NOTE: Check to see if we're doing an update or create
				if c.HasKey(key) == true {
					err = c.UpdateJSON(key, src)
				} else {
					err = c.CreateJSON(key, src)
				}
			}
			if err == nil {
				if len(saveKeys) > 0 {
					exportedKeys = append(exportedKeys, key)
				}
				// We've exported a record successfully, now update select lists
				j++
			} else {
				if verbose == true {
					log.Printf("(pid: %d) Failed to save eprint %s (%s) to %s, %s\n", pid, key, uri, api.Dataset, err)
				}
				k++
			}
			c.AttachFile(key, key+".xml", bytes.NewReader(xmlSrc))
		}
		if verbose == true && (i%EPrintsExportBatchSize) == 0 {
			log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i+1, count, j, k)
		}
	}
	if verbose == true {
		log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i, count, j, k)
	}
	if len(saveKeys) > 0 {
		if err := ioutil.WriteFile(saveKeys, []byte(strings.Join(exportedKeys, "\n")), 0664); err != nil {
			return fmt.Errorf("Failed to export %s, %s", saveKeys, err)
		}
	}
	return nil
}

// ExportModifiedEPrints returns a list of ids modified in one or between the start, end times
func ExportModifiedEPrints(api *eprinttools.EPrintsAPI, start, end time.Time, saveKeys string, verbose bool) error {
	var (
		exportedKeys []string
		err          error
		src          []byte
	)

	pid := os.Getpid()

	c, err := dataset.Open(api.Dataset)
	if err != nil {
		return fmt.Errorf("ExportModifiedEPrints() %s, %s", api.Dataset, err)
	}
	defer c.Close()

	uris, err := api.ListModifiedEPrintsURI(start, end, verbose)
	if err != nil {
		return fmt.Errorf("Export modified from %s to %s failed, %s", start, end, err)
	}

	log.Printf("DEBUG (pid: %d) %d uris found from %s to %s\n", pid, len(uris), start, end)
	// NOTE: I am sorting the URI by decscending ID number so that the
	// newest articles are exported first
	sort.Sort(byURI(uris))

	count := len(uris)
	j := 0 // success count
	k := 0 // error count
	if verbose == true {
		log.Printf("(pid: %d) Exporting %d uris", pid, count)
	}
	for i := 0; i < count; i++ {
		uri := uris[i]
		rec, xmlSrc, err := api.GetEPrint(uri)
		if err != nil {
			if verbose == true {
				log.Printf("(pid: %d) Failed to get %s, %s\n", pid, uri, err)
			}
			k++
		} else {
			key := fmt.Sprintf("%d", rec.EPrintID)
			src, err = json.Marshal(rec)
			if err != nil {
				log.Printf("(pid: %d) Can't marshel key %s, %s", pid, key, err)
			} else {
				// NOTE: Check to see if we're doing an update or create
				if c.HasKey(key) == true {
					err = c.UpdateJSON(key, src)
				} else {
					err = c.CreateJSON(key, src)
				}
			}
			if err == nil {
				if len(saveKeys) > 0 {
					exportedKeys = append(exportedKeys, key)
				}
				// We've exported a record successfully, now update select lists
				j++
			} else {
				if verbose == true {
					log.Printf("(pid: %d) Failed to save eprint %s (%s) to %s, %s\n", pid, key, uri, api.Dataset, err)
				}
				k++
			}
			c.AttachFile(key, key+".xml", bytes.NewReader(xmlSrc))
		}
		if verbose == true && (i%EPrintsExportBatchSize) == 0 {
			log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i+1, count, j, k)
		}
	}
	if verbose == true {
		log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, len(uris), count, j, k)
	}
	if len(saveKeys) > 0 {
		if err := ioutil.WriteFile(saveKeys, []byte(strings.Join(exportedKeys, "\n")), 0664); err != nil {
			return fmt.Errorf("Failed to export %s, %s", saveKeys, err)
		}
	}
	return nil
}
<commit_msg>prep for v0.0.24 release<commit_after>//
// Package eprinttools is a collection of structures and functions for working with the E-Prints REST API
//
// @author R. S. Doiel, <rsdoiel@caltech.edu>
//
// Copyright (c) 2017, Caltech
// All rights not granted herein are expressly reserved by Caltech.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
package harvest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	// CaltechLibrary packages
	"github.com/caltechlibrary/dataset"
	"github.com/caltechlibrary/eprinttools"
)

var (
	// EPrintsExportBatchSize sets the summary output frequency when exporting content from E-Prints
	EPrintsExportBatchSize = 1000
)

type byURI []string

func (s byURI) Len() int {
	return len(s)
}

func (s byURI) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s byURI) Less(i, j int) bool {
	var (
		a1  int
		a2  int
		err error
	)
	s1 := strings.TrimSuffix(path.Base(s[i]), path.Ext(s[i]))
	s2 := strings.TrimSuffix(path.Base(s[j]), path.Ext(s[j]))
	a1, err = strconv.Atoi(s1)
	if err != nil {
		return false
	}
	a2, err = strconv.Atoi(s2)
	if err != nil {
		return false
	}
	//NOTE: We're creating a descending sort, so a1 should be larger than a2
	return a1 > a2
}

// ExportEPrintsKeyList export a list of eprints from a list of keys
func ExportEPrintsKeyList(api *eprinttools.EPrintsAPI, keys []string, saveKeys string, verbose bool) error {
	var (
		exportedKeys []string
		err          error
		src          []byte
	)

	c, err := dataset.Open(api.Dataset)
	if err != nil {
		return fmt.Errorf("ExportEPrintsKeyList() %s, %s", api.Dataset, err)
	}
	defer c.Close()

	uris := []string{}
	for _, key := range keys {
		uri := fmt.Sprintf("/rest/eprint/%s.xml", strings.TrimSpace(key))
		uris = append(uris, uri)
	}

	pid := os.Getpid()
	uriCount := len(uris)
	count := uriCount
	j := 0 // success count
	k := 0 // error count
	if verbose == true {
		log.Printf("(pid: %d) Exporting %d of %d uris", pid, count, uriCount)
	}
	for i := 0; i < uriCount && i < count; i++ {
		uri := uris[i]
		rec, xmlSrc, err := api.GetEPrint(uri)
		if err != nil {
			log.Printf("(pid: %d) Failed, %s\n", pid, err)
			k++
		} else {
			key := fmt.Sprintf("%d", rec.EPrintID)
			src, err = json.Marshal(rec)
			if err != nil {
				log.Printf("(pid: %d) can't marshal key %s, %s", pid, key, err)
			} else {
				// NOTE: Check to see if we're doing an update or create
				if c.HasKey(key) == true {
					err = c.UpdateJSON(key, src)
				} else {
					err = c.CreateJSON(key, src)
				}
			}
			if err == nil {
				if len(saveKeys) > 0 {
					exportedKeys = append(exportedKeys, key)
				}
				// We've exported a record successfully, now update select lists
				j++
			} else {
				if verbose == true {
					log.Printf("(pid: %d) Failed to save eprint %s (%s) to %s, %s\n", pid, key, uri, api.Dataset, err)
				}
				k++
			}
			c.AttachFile(key, key+".xml", bytes.NewReader(xmlSrc))
		}
		if verbose == true && (i%EPrintsExportBatchSize) == 0 {
			log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i+1, count, j, k)
		}
	}
	if verbose == true {
		log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, len(uris), count, j, k)
	}
	if len(saveKeys) > 0 {
		if err := ioutil.WriteFile(saveKeys, []byte(strings.Join(exportedKeys, "\n")), 0664); err != nil {
			return fmt.Errorf("Failed to export %s, %s", saveKeys, err)
		}
	}
	return nil
}

// ExportEPrints from highest ID to lowest for cnt. Saves each record in a DB and indexes published ones
func ExportEPrints(api *eprinttools.EPrintsAPI, count int, saveKeys string, verbose bool) error {
	var (
		exportedKeys []string
		err          error
		src          []byte
	)

	c, err := dataset.Open(api.Dataset)
	if err != nil {
		return fmt.Errorf("ExportEPrints() %s, %s", api.Dataset, err)
	}
	defer c.Close()

	uris, err := api.ListEPrintsURI()
	if err != nil {
		return fmt.Errorf("Export %s failed, %s", api.URL.String(), err)
	}

	// NOTE: I am sorting the URI by decscending ID number so that the
	// newest articles are exported first
	sort.Sort(byURI(uris))

	pid := os.Getpid()
	uriCount := len(uris)
	if count < 0 {
		count = uriCount
	}
	j := 0 // success count
	k := 0 // error count
	if verbose == true {
		log.Printf("(pid: %d) Exporting %d of %d uris", pid, count, uriCount)
	}
	i := 0
	for i = 0; i < uriCount && i < count; i++ {
		uri := uris[i]
		rec, xmlSrc, err := api.GetEPrint(uri)
		if err != nil {
			log.Printf("(pid: %d) Failed, %s\n", pid, err)
			k++
		} else {
			key := fmt.Sprintf("%d", rec.EPrintID)
			src, err = json.Marshal(rec)
			if err != nil {
				log.Printf("(pid: %d) Can't marshal key %s, %s", pid, key, err)
			} else {
				// NOTE: Check to see if we're doing an update or create
				if c.HasKey(key) == true {
					err = c.UpdateJSON(key, src)
				} else {
					err = c.CreateJSON(key, src)
				}
			}
			if err == nil {
				if len(saveKeys) > 0 {
					exportedKeys = append(exportedKeys, key)
				}
				// We've exported a record successfully, now update select lists
				j++
			} else {
				if verbose == true {
					log.Printf("(pid: %d) Failed to save eprint %s (%s) to %s, %s\n", pid, key, uri, api.Dataset, err)
				}
				k++
			}
			c.AttachFile(key, key+".xml", bytes.NewReader(xmlSrc))
		}
		if verbose == true && (i%EPrintsExportBatchSize) == 0 {
			log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i+1, count, j, k)
		}
	}
	if verbose == true {
		log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i, count, j, k)
	}
	if len(saveKeys) > 0 {
		if err := ioutil.WriteFile(saveKeys, []byte(strings.Join(exportedKeys, "\n")), 0664); err != nil {
			return fmt.Errorf("Failed to export %s, %s", saveKeys, err)
		}
	}
	return nil
}

// ExportModifiedEPrints returns a list of ids modified in one or between the start, end times
func ExportModifiedEPrints(api *eprinttools.EPrintsAPI, start, end time.Time, saveKeys string, verbose bool) error {
	var (
		exportedKeys []string
		err          error
		src          []byte
	)

	pid := os.Getpid()

	c, err := dataset.Open(api.Dataset)
	if err != nil {
		return fmt.Errorf("ExportModifiedEPrints() %s, %s", api.Dataset, err)
	}
	defer c.Close()

	uris, err := api.ListModifiedEPrintsURI(start, end, verbose)
	if err != nil {
		return fmt.Errorf("Export modified from %s to %s failed, %s", start, end, err)
	}

	// NOTE: I am sorting the URI by decscending ID number so that the
	// newest articles are exported first
	sort.Sort(byURI(uris))

	count := len(uris)
	j := 0 // success count
	k := 0 // error count
	if verbose == true {
		log.Printf("(pid: %d) Exporting %d uris", pid, count)
	}
	i := 0
	for i = 0; i < count; i++ {
		uri := uris[i]
		rec, xmlSrc, err := api.GetEPrint(uri)
		if err != nil {
			if verbose == true {
				log.Printf("(pid: %d) Failed to get %s, %s\n", pid, uri, err)
			}
			k++
		} else {
			key := fmt.Sprintf("%d", rec.EPrintID)
			src, err = json.Marshal(rec)
			if err != nil {
				log.Printf("(pid: %d) Can't marshel key %s, %s", pid, key, err)
			} else {
				// NOTE: Check to see if we're doing an update or create
				if c.HasKey(key) == true {
					err = c.UpdateJSON(key, src)
				} else {
					err = c.CreateJSON(key, src)
				}
			}
			if err == nil {
				if len(saveKeys) > 0 {
					exportedKeys = append(exportedKeys, key)
				}
				// We've exported a record successfully, now update select lists
				j++
			} else {
				if verbose == true {
					log.Printf("(pid: %d) Failed to save eprint %s (%s) to %s, %s\n", pid, key, uri, api.Dataset, err)
				}
				k++
			}
			c.AttachFile(key, key+".xml", bytes.NewReader(xmlSrc))
		}
		if verbose == true && (i%EPrintsExportBatchSize) == 0 {
			log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i+1, count, j, k)
		}
	}
	if verbose == true {
		log.Printf("(pid: %d) %d/%d uri processed, %d exported, %d unexported", pid, i, count, j, k)
	}
	if len(saveKeys) > 0 {
		if err := ioutil.WriteFile(saveKeys, []byte(strings.Join(exportedKeys, "\n")), 0664); err != nil {
			return fmt.Errorf("Failed to export %s, %s", saveKeys, err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"
	"fmt"
	"log"
	"github.com/nu7hatch/gouuid"
	"github.com/vaughan0/go-ini"
)

var TriggerNotFoundMsg = "Trigger could not be found."

// A handler for a specific trigger.
type Trigger struct {
	// the path for which this trigger is triggered.
	path string
	// the URL that is to be called when this trigger i triggered
	outputurl string
}

// create a new trigger trigger for a specific path
func LoadHandler(outputurl, path string) Trigger {
	return Trigger{
		path: path,
		outputurl: outputurl,
	}
}

// log and trigger the specific trigger
//
// Does proper checks to make sure that the right method is from
// downstream.
func (tt Trigger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET"{
		// only supporting GET at the moment
		log.Println("[server]", r.Method, r.URL.Path, "404 (only GET allowed)")
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, TriggerNotFoundMsg)
		return
	}
	if r.URL.Path != tt.path {
		// if this isn't here we'll trigger for all path
		// prefixes.
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, TriggerNotFoundMsg)
		return
	}

	loguuid, err := uuid.NewV4()
	// used to correlate server and client log lines
	var id string
	if err != nil {
		id = "???"
	} else {
		id = loguuid.String()
	}

	log.Println("[server]", id, "GET", r.URL.Path, "200")
	fmt.Fprintf(w, "Triggered.")
	go func() {
		resp, err := http.Get(tt.outputurl)
		var result string
		if err != nil {
			result = "Err: " + err.Error()
		} else {
			result = resp.Status
		}
		log.Println("[client]", id, "GET", tt.outputurl, result)
	}()
}

// Load all handlers from the configuration file.
func LoadHandlers(file ini.File) {
	root_found := false

	for path, _ := range file {
		if path == "" {
			// ignoring default section
			continue
		}

		if path == "/" {
			root_found = true
		}

		outputurl, ok := file.Get(path, "url")
		if !ok {
			panic(fmt.Sprintf("'url' missing for: %s", path))
		}

		http.Handle(path, LoadHandler(outputurl, path))
	}

	if !root_found {
		// custom 404
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			log.Println("[server]", r.Method, r.URL.Path, "404")
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, TriggerNotFoundMsg)
		})
	}
}

const CONFIG_FILE_ENVIRON = "TRIGGER_TRIGGER_CONFIG"

func main() {
	inifilename := os.Getenv(CONFIG_FILE_ENVIRON)
	if inifilename == "" {
		fmt.Println("Please point", CONFIG_FILE_ENVIRON, "to config file.")
		os.Exit(1)
	}

	file, err := ini.LoadFile(inifilename)
	if err != nil {
		panic(fmt.Sprintf("Could not read config file: %s", inifilename))
	}

	LoadHandlers(file)

	listen, ok := file.Get("", "listen")
	if !ok {
		log.Print("[server] 'listen' not defined. Using fallback ':8080'")
		listen = ":8080"
	}

	log.Fatal(http.ListenAndServe(listen, nil))
}
<commit_msg>Processing HTTP requests through a channel<commit_after>package main

import (
	"net/http"
	"os"
	"fmt"
	"log"
	"github.com/nu7hatch/gouuid"
	"github.com/vaughan0/go-ini"
)

var TriggerNotFoundMsg = "Trigger could not be found."

type LogUUID string

// A handler for a specific trigger.
type Trigger struct {
	// the path for which this trigger is triggered.
	path string

	// channel for HTTP requests
	requestchan chan LogUUID
}

// create a new trigger trigger for a specific path
func LoadHandler(path string, section ini.Section) Trigger {
	outputurl, ok := section["url"]
	if !ok {
		panic(fmt.Sprintf("'url' missing for: %s", path))
	}

	t := Trigger{
		path: path,
		requestchan: make(chan LogUUID),
	}
	go func() {
		// Process HTTP requests
		for id := range t.requestchan {
			resp, err := http.Get(outputurl)
			var result string
			if err != nil {
				result = "Err: " + err.Error()
			} else {
				result = resp.Status
			}
			log.Println("[client]", id, "GET", outputurl, result)
		}
	}()
	return t
}

// log and trigger the specific trigger
//
// Does proper checks to make sure that the right method is from
// downstream.
func (tt Trigger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET"{
		// only supporting GET at the moment
		log.Println("[server]", r.Method, r.URL.Path, "404 (only GET allowed)")
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, TriggerNotFoundMsg)
		return
	}
	if r.URL.Path != tt.path {
		// if this isn't here we'll trigger for all path
		// prefixes.
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, TriggerNotFoundMsg)
		return
	}

	loguuid, err := uuid.NewV4()
	// used to correlate server and client log lines
	var id string
	if err != nil {
		id = "???"
	} else {
		id = loguuid.String()
	}
	tt.requestchan <- LogUUID(id)

	log.Println("[server]", id, "GET", r.URL.Path, "200")
	fmt.Fprintf(w, "Triggered.")
}

// Load all handlers from the configuration file.
func LoadHandlers(file ini.File) {
	root_found := false

	for path, settings := range file {
		if path == "" {
			// ignoring default section
			continue
		}

		if path == "/" {
			root_found = true
		}

		http.Handle(path, LoadHandler(path, settings))
	}

	if !root_found {
		// custom 404
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			log.Println("[server]", r.Method, r.URL.Path, "404")
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, TriggerNotFoundMsg)
		})
	}
}

const CONFIG_FILE_ENVIRON = "TRIGGER_TRIGGER_CONFIG"

func main() {
	inifilename := os.Getenv(CONFIG_FILE_ENVIRON)
	if inifilename == "" {
		fmt.Println("Please point", CONFIG_FILE_ENVIRON, "to config file.")
		os.Exit(1)
	}

	file, err := ini.LoadFile(inifilename)
	if err != nil {
		panic(fmt.Sprintf("Could not read config file: %s", inifilename))
	}

	LoadHandlers(file)

	listen, ok := file.Get("", "listen")
	if !ok {
		log.Print("[server] 'listen' not defined. Using fallback ':8080'")
		listen = ":8080"
	}

	log.Fatal(http.ListenAndServe(listen, nil))
}
<|endoftext|>


<commit_before>package qemu

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
	hasVsock   bool
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	qemuPidFile string
	qemuLogFile *QemuLogFile
	cpus        int
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath(QEMU_SYSTEM_EXE)
	if err != nil {
		return nil
	}

	var hasVsock bool
	_, err = exec.Command("/sbin/modprobe", "vhost_vsock").Output()
	if err == nil {
		hasVsock = true
	}

	return &QemuDriver{
		executable: cmd,
		hasVsock:   hasVsock,
	}
}

func (qd *QemuDriver) Name() string {
	return "qemu"
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	if _, err := os.Stat(QemuLogDir); os.IsNotExist(err) {
		os.Mkdir(QemuLogDir, 0755)
	}

	logFile := filepath.Join(QemuLogDir, homeDir[strings.Index(homeDir, "vm-"):len(homeDir)-1]+".log")
	if _, err := os.Create(logFile); err != nil {
		glog.Errorf("create qemu log file failed: %v", err)
	}
	qemuLogFile := &QemuLogFile{
		Name:   logFile,
		Offset: 0,
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: filepath.Join(homeDir, QmpSockName),
		qemuPidFile: filepath.Join(homeDir, QemuPidFile),
		qemuLogFile: qemuLogFile,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var log QemuLogFile
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case float64:
			proc, err = os.FindProcess(int(p.(float64)))
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	l, ok := persisted["log"]
	if !ok {
		return nil, errors.New("cannot read the qemu log filename info from persist info")
	}
	if bytes, err := json.Marshal(l); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	} else if err = json.Unmarshal(bytes, &log); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		qemuLogFile: &log,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"log":        *qc.qemuLogFile,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(ctx, qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.wdt <- "quit"
	<-qc.waitQmp
	qc.qemuLogFile.Close()
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, pause bool) error {
	commands := make([]*QmpCommand, 1)

	if pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.DiskDescriptor, result chan<- hypervisor.VmEvent) {
	name := blockInfo.Name
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(ctx, qc, name, sourceType, filename, format, id, result)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.DiskDescriptor, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(ctx, qc, id, callback, result)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo, result chan<- hypervisor.VmEvent) {
	newNetworkAddSession(ctx, qc, host.Id, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr, result)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	newNetworkDelSession(ctx, qc, n.DeviceName, callback, result)
}

func (qc *QemuContext) SetCpus(ctx *hypervisor.VmContext, cpus int) error {
	currcpus := qc.cpus

	if cpus < currcpus {
		return fmt.Errorf("can't reduce cpus number from %d to %d", currcpus, cpus)
	} else if cpus == currcpus {
		return nil
	}

	commands := make([]*QmpCommand, cpus-currcpus)
	for id := currcpus; id < cpus; id++ {
		commands[id-currcpus] = &QmpCommand{
			Execute: "cpu-add",
			Arguments: map[string]interface{}{
				"id": id,
			},
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			if err == nil {
				qc.cpus = cpus
			}
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int) error {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}
	return <-result
}

func (qc *QemuContext) Save(ctx *hypervisor.VmContext, path string) error {
	commands := make([]*QmpCommand, 2)

	commands[0] = &QmpCommand{
		Execute: "migrate-set-capabilities",
		Arguments: map[string]interface{}{
			"capabilities": []map[string]interface{}{
				{
					"capability": "bypass-shared-memory",
					"state":      true,
				},
			},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "migrate",
		Arguments: map[string]interface{}{
			"uri": fmt.Sprintf("exec:cat>%s", path),
		},
	}
	if !ctx.Boot.BootToBeTemplate {
		commands = commands[1:]
	}

	result := make(chan error, 1)
	// TODO: use query-migrate to query until completed
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}

	return <-result
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuDriver) SupportVmSocket() bool {
	return qc.hasVsock
}
<commit_msg>test qemu process with signal 0 when loading qemu<commit_after>package qemu

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/golang/glog"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/types"
)

//implement the hypervisor.HypervisorDriver interface
type QemuDriver struct {
	executable string
	hasVsock   bool
}

//implement the hypervisor.DriverContext interface
type QemuContext struct {
	driver      *QemuDriver
	qmp         chan QmpInteraction
	waitQmp     chan int
	wdt         chan string
	qmpSockName string
	qemuPidFile string
	qemuLogFile *QemuLogFile
	cpus        int
	process     *os.Process
}

func qemuContext(ctx *hypervisor.VmContext) *QemuContext {
	return ctx.DCtx.(*QemuContext)
}

func InitDriver() *QemuDriver {
	cmd, err := exec.LookPath(QEMU_SYSTEM_EXE)
	if err != nil {
		return nil
	}

	var hasVsock bool
	_, err = exec.Command("/sbin/modprobe", "vhost_vsock").Output()
	if err == nil {
		hasVsock = true
	}

	return &QemuDriver{
		executable: cmd,
		hasVsock:   hasVsock,
	}
}

func (qd *QemuDriver) Name() string {
	return "qemu"
}

func (qd *QemuDriver) InitContext(homeDir string) hypervisor.DriverContext {
	if _, err := os.Stat(QemuLogDir); os.IsNotExist(err) {
		os.Mkdir(QemuLogDir, 0755)
	}

	logFile := filepath.Join(QemuLogDir, homeDir[strings.Index(homeDir, "vm-"):len(homeDir)-1]+".log")
	if _, err := os.Create(logFile); err != nil {
		glog.Errorf("create qemu log file failed: %v", err)
	}
	qemuLogFile := &QemuLogFile{
		Name:   logFile,
		Offset: 0,
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: filepath.Join(homeDir, QmpSockName),
		qemuPidFile: filepath.Join(homeDir, QemuPidFile),
		qemuLogFile: qemuLogFile,
		process:     nil,
	}
}

func (qd *QemuDriver) LoadContext(persisted map[string]interface{}) (hypervisor.DriverContext, error) {
	if t, ok := persisted["hypervisor"]; !ok || t != "qemu" {
		return nil, errors.New("wrong driver type in persist info")
	}

	var sock string
	var log QemuLogFile
	var proc *os.Process = nil
	var err error

	s, ok := persisted["qmpSock"]
	if !ok {
		return nil, errors.New("cannot read the qmp socket info from persist info")
	} else {
		switch s.(type) {
		case string:
			sock = s.(string)
		default:
			return nil, errors.New("wrong sock name type in persist info")
		}
	}

	p, ok := persisted["pid"]
	if !ok {
		return nil, errors.New("cannot read the pid info from persist info")
	} else {
		switch p.(type) {
		case float64:
			proc, err = os.FindProcess(int(p.(float64)))
			if err != nil {
				return nil, err
			}
			// test if process has already exited
			if err = proc.Signal(syscall.Signal(0)); err != nil {
				return nil, fmt.Errorf("signal 0 on Qemu process(%d) failed: %v", int(p.(float64)), err)
			}
		default:
			return nil, errors.New("wrong pid field type in persist info")
		}
	}

	l, ok := persisted["log"]
	if !ok {
		return nil, errors.New("cannot read the qemu log filename info from persist info")
	}
	if bytes, err := json.Marshal(l); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	} else if err = json.Unmarshal(bytes, &log); err != nil {
		return nil, fmt.Errorf("wrong qemu log filename type in persist info: %v", err)
	}

	return &QemuContext{
		driver:      qd,
		qmp:         make(chan QmpInteraction, 128),
		wdt:         make(chan string, 16),
		waitQmp:     make(chan int, 1),
		qmpSockName: sock,
		qemuLogFile: &log,
		process:     proc,
	}, nil
}

func (qc *QemuContext) Launch(ctx *hypervisor.VmContext) {
	go launchQemu(qc, ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Associate(ctx *hypervisor.VmContext) {
	go associateQemu(ctx)
	go qmpHandler(ctx)
}

func (qc *QemuContext) Dump() (map[string]interface{}, error) {
	if qc.process == nil {
		return nil, errors.New("can not serialize qemu context: no process running")
	}

	return map[string]interface{}{
		"hypervisor": "qemu",
		"qmpSock":    qc.qmpSockName,
		"log":        *qc.qemuLogFile,
		"pid":        qc.process.Pid,
	}, nil
}

func (qc *QemuContext) Shutdown(ctx *hypervisor.VmContext) {
	qmpQemuQuit(ctx, qc)
}

func (qc *QemuContext) Kill(ctx *hypervisor.VmContext) {
	defer func() {
		err := recover()
		if glog.V(1) && err != nil {
			glog.Info("kill qemu, but channel has already been closed")
		}
	}()
	qc.wdt <- "kill"
}

func (qc *QemuContext) Stats(ctx *hypervisor.VmContext) (*types.PodStats, error) {
	return nil, nil
}

func (qc *QemuContext) Close() {
	qc.wdt <- "quit"
	<-qc.waitQmp
	qc.qemuLogFile.Close()
	close(qc.qmp)
	close(qc.wdt)
}

func (qc *QemuContext) Pause(ctx *hypervisor.VmContext, pause bool) error {
	commands := make([]*QmpCommand, 1)

	if pause {
		commands[0] = &QmpCommand{
			Execute: "stop",
		}
	} else {
		commands[0] = &QmpCommand{
			Execute: "cont",
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddDisk(ctx *hypervisor.VmContext, sourceType string, blockInfo *hypervisor.DiskDescriptor, result chan<- hypervisor.VmEvent) {
	name := blockInfo.Name
	filename := blockInfo.Filename
	format := blockInfo.Format
	id := blockInfo.ScsiId

	if format == "rbd" {
		if blockInfo.Options != nil {
			keyring := blockInfo.Options["keyring"]
			user := blockInfo.Options["user"]
			if keyring != "" && user != "" {
				filename += ":id=" + user + ":key=" + keyring
			}

			monitors := blockInfo.Options["monitors"]
			for i, m := range strings.Split(monitors, ";") {
				monitor := strings.Replace(m, ":", "\\:", -1)
				if i == 0 {
					filename += ":mon_host=" + monitor
					continue
				}
				filename += ";" + monitor
			}
		}
	}

	newDiskAddSession(ctx, qc, name, sourceType, filename, format, id, result)
}

func (qc *QemuContext) RemoveDisk(ctx *hypervisor.VmContext, blockInfo *hypervisor.DiskDescriptor, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	id := blockInfo.ScsiId

	newDiskDelSession(ctx, qc, id, callback, result)
}

func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo, result chan<- hypervisor.VmEvent) {
	newNetworkAddSession(ctx, qc, host.Id, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr, result)
}

func (qc *QemuContext) RemoveNic(ctx *hypervisor.VmContext, n *hypervisor.InterfaceCreated, callback hypervisor.VmEvent, result chan<- hypervisor.VmEvent) {
	newNetworkDelSession(ctx, qc, n.DeviceName, callback, result)
}

func (qc *QemuContext) SetCpus(ctx *hypervisor.VmContext, cpus int) error {
	currcpus := qc.cpus

	if cpus < currcpus {
		return fmt.Errorf("can't reduce cpus number from %d to %d", currcpus, cpus)
	} else if cpus == currcpus {
		return nil
	}

	commands := make([]*QmpCommand, cpus-currcpus)
	for id := currcpus; id < cpus; id++ {
		commands[id-currcpus] = &QmpCommand{
			Execute: "cpu-add",
			Arguments: map[string]interface{}{
				"id": id,
			},
		}
	}

	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond: func(err error) {
			if err == nil {
				qc.cpus = cpus
			}
			result <- err
		},
	}
	return <-result
}

func (qc *QemuContext) AddMem(ctx *hypervisor.VmContext, slot, size int) error {
	commands := make([]*QmpCommand, 2)
	commands[0] = &QmpCommand{
		Execute: "object-add",
		Arguments: map[string]interface{}{
			"qom-type": "memory-backend-ram",
			"id":       "mem" + strconv.Itoa(slot),
			"props":    map[string]interface{}{"size": int64(size) << 20},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "device_add",
		Arguments: map[string]interface{}{
			"driver": "pc-dimm",
			"id":     "dimm" + strconv.Itoa(slot),
			"memdev": "mem" + strconv.Itoa(slot),
		},
	}
	result := make(chan error, 1)
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}
	return <-result
}

func (qc *QemuContext) Save(ctx *hypervisor.VmContext, path string) error {
	commands := make([]*QmpCommand, 2)

	commands[0] = &QmpCommand{
		Execute: "migrate-set-capabilities",
		Arguments: map[string]interface{}{
			"capabilities": []map[string]interface{}{
				{
					"capability": "bypass-shared-memory",
					"state":      true,
				},
			},
		},
	}
	commands[1] = &QmpCommand{
		Execute: "migrate",
		Arguments: map[string]interface{}{
			"uri": fmt.Sprintf("exec:cat>%s", path),
		},
	}
	if !ctx.Boot.BootToBeTemplate {
		commands = commands[1:]
	}

	result := make(chan error, 1)
	// TODO: use query-migrate to query until completed
	qc.qmp <- &QmpSession{
		commands: commands,
		respond:  func(err error) { result <- err },
	}

	return <-result
}

func (qc *QemuDriver) SupportLazyMode() bool {
	return false
}

func (qc *QemuDriver) SupportVmSocket() bool {
	return qc.hasVsock
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/batch"
	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func init() {
	resource.AddTestSweepers("aws_batch_job_definition", &resource.Sweeper{
		Name: "aws_batch_job_definition",
		F:    testSweepBatchJobDefinitions,
		Dependencies: []string{
			"aws_batch_job_queue",
		},
	})
}

func testSweepBatchJobDefinitions(region string) error {
	client, err := sharedClientForRegion(region)
	if err != nil {
		return fmt.Errorf("error getting client: %w", err)
	}
	conn := client.(*AWSClient).batchconn
	var sweeperErrs *multierror.Error

	err = conn.DescribeJobDefinitionsPages(&batch.DescribeJobDefinitionsInput{}, func(page *batch.DescribeJobDefinitionsOutput, isLast bool) bool {
		if page == nil {
			return !isLast
		}

		for _, jobDefinition := range page.JobDefinitions {
			arn := aws.StringValue(jobDefinition.JobDefinitionArn)

			log.Printf("[INFO] Deleting Batch Job Definition: %s", arn)
			_, err := conn.DeregisterJobDefinition(&batch.DeregisterJobDefinitionInput{
				JobDefinition: aws.String(arn),
			})
			if err != nil {
				sweeperErr := fmt.Errorf("error deleting Batch Job Definition (%s): %w", arn, err)
				log.Printf("[ERROR] %s", sweeperErr)
				sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
				continue
			}
		}

		return !isLast
	})
	if testSweepSkipSweepError(err) {
		log.Printf("[WARN] Skipping Batch Job Definitions sweep for %s: %s", region, err)
		return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors
	}
	if err != nil {
		sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Batch Job Definitions: %w", err))
	}

	return sweeperErrs.ErrorOrNil()
}

func TestAccAWSBatchJobDefinition_basic(t *testing.T) {
	var jd batch.JobDefinition
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_batch_job_definition.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSBatch(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBatchJobDefinitionDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccBatchJobDefinitionConfigName(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &jd),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSBatchJobDefinition_ContainerProperties_Advanced(t *testing.T) {
	var jd batch.JobDefinition
	compare := batch.JobDefinition{
		Parameters: map[string]*string{
			"param1": aws.String("val1"),
			"param2": aws.String("val2"),
		},
		RetryStrategy: &batch.RetryStrategy{
			Attempts: aws.Int64(int64(1)),
		},
		Timeout: &batch.JobTimeout{
			AttemptDurationSeconds: aws.Int64(int64(60)),
		},
		ContainerProperties: &batch.ContainerProperties{
			Command: []*string{aws.String("ls"), aws.String("-la")},
			Environment: []*batch.KeyValuePair{
				{Name: aws.String("VARNAME"), Value: aws.String("VARVAL")},
			},
			Image:  aws.String("busybox"),
			Memory: aws.Int64(int64(512)),
			MountPoints: []*batch.MountPoint{
				{ContainerPath: aws.String("/tmp"), ReadOnly: aws.Bool(false), SourceVolume: aws.String("tmp")},
			},
			ResourceRequirements: []*batch.ResourceRequirement{},
			Ulimits: []*batch.Ulimit{
				{HardLimit: aws.Int64(int64(1024)), Name: aws.String("nofile"), SoftLimit: aws.Int64(int64(1024))},
			},
			Vcpus: aws.Int64(int64(1)),
			Volumes: []*batch.Volume{
				{
					Host: &batch.Host{SourcePath: aws.String("/tmp")},
					Name: aws.String("tmp"),
				},
			},
		},
	}
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_batch_job_definition.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSBatch(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBatchJobDefinitionDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccBatchJobDefinitionConfigContainerPropertiesAdvanced(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &jd),
					testAccCheckBatchJobDefinitionAttributes(&jd, &compare),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSBatchJobDefinition_updateForcesNewResource(t *testing.T) {
	var before, after batch.JobDefinition
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_batch_job_definition.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSBatch(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBatchJobDefinitionDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccBatchJobDefinitionConfigContainerPropertiesAdvanced(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &before),
					testAccCheckBatchJobDefinitionAttributes(&before, nil),
				),
			},
			{
				Config: testAccBatchJobDefinitionConfigContainerPropertiesAdvancedUpdate(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &after),
					testAccCheckJobDefinitionRecreated(t, &before, &after),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckBatchJobDefinitionExists(n string, jd *batch.JobDefinition) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Batch Job Queue ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).batchconn
		arn := rs.Primary.Attributes["arn"]
		def, err := getJobDefinition(conn, arn)
		if err != nil {
			return err
		}
		if def == nil {
			return fmt.Errorf("Not found: %s", n)
		}
		*jd = *def

		return nil
	}
}

func testAccCheckBatchJobDefinitionAttributes(jd *batch.JobDefinition, compare *batch.JobDefinition) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		for _, rs := range s.RootModule().Resources {
			if rs.Type != "aws_batch_job_definition" {
				continue
			}
			if *jd.JobDefinitionArn != rs.Primary.Attributes["arn"] {
				return fmt.Errorf("Bad Job Definition ARN\n\t expected: %s\n\tgot: %s\n", rs.Primary.Attributes["arn"], *jd.JobDefinitionArn)
			}
			if compare != nil {
				if compare.Parameters != nil && !reflect.DeepEqual(compare.Parameters, jd.Parameters) {
					return fmt.Errorf("Bad Job Definition Params\n\t expected: %v\n\tgot: %v\n", compare.Parameters, jd.Parameters)
				}
				if compare.RetryStrategy != nil && *compare.RetryStrategy.Attempts != *jd.RetryStrategy.Attempts {
					return fmt.Errorf("Bad Job Definition Retry Strategy\n\t expected: %d\n\tgot: %d\n", *compare.RetryStrategy.Attempts, *jd.RetryStrategy.Attempts)
				}
				if compare.ContainerProperties != nil && compare.ContainerProperties.Command != nil && !reflect.DeepEqual(compare.ContainerProperties, jd.ContainerProperties) {
					return fmt.Errorf("Bad Job Definition Container Properties\n\t expected: %s\n\tgot: %s\n", compare.ContainerProperties, jd.ContainerProperties)
				}
			}
		}
		return nil
	}
}

func testAccCheckJobDefinitionRecreated(t *testing.T,
	before, after *batch.JobDefinition) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if *before.Revision == *after.Revision {
			t.Fatalf("Expected change of JobDefinition Revisions, but both were %v", before.Revision)
		}
		return nil
	}
}

func testAccCheckBatchJobDefinitionDestroy(s *terraform.State) error {
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_batch_job_definition" {
			continue
		}
		conn := testAccProvider.Meta().(*AWSClient).batchconn
		js, err := getJobDefinition(conn, rs.Primary.Attributes["arn"])
		if err == nil && js != nil {
			if *js.Status == "ACTIVE" {
				return fmt.Errorf("Error: Job Definition still active")
			}
		}
		return nil
	}
	return nil
}

func testAccBatchJobDefinitionConfigContainerPropertiesAdvanced(rName string) string {
	return fmt.Sprintf(`
resource "aws_batch_job_definition" "test" {
	name = %[1]q
	type = "container"
	parameters = {
		param1 = "val1"
		param2 = "val2"
	}
	retry_strategy {
		attempts = 1
	}
	timeout {
		attempt_duration_seconds = 60
	}
	container_properties = <<CONTAINER_PROPERTIES
{
	"command": ["ls", "-la"],
	"image": "busybox",
	"memory": 512,
	"vcpus": 1,
	"volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
	"environment": [
		{"name": "VARNAME", "value": "VARVAL"}
	],
	"mountPoints": [
		{
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
	],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}
`, rName)
}

func testAccBatchJobDefinitionConfigContainerPropertiesAdvancedUpdate(rName string) string {
	return fmt.Sprintf(`
resource "aws_batch_job_definition" "test" {
	name = %[1]q
	type = "container"
	container_properties = <<CONTAINER_PROPERTIES
{
	"command": ["ls", "-la"],
	"image": "busybox",
	"memory": 1024,
	"vcpus": 1,
	"volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
	"environment": [
		{"name": "VARNAME", "value": "VARVAL"}
	],
	"mountPoints": [
		{
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
	],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}
`, rName)
}

func testAccBatchJobDefinitionConfigName(rName string) string {
	return fmt.Sprintf(`
resource "aws_batch_job_definition" "test" {
  container_properties = jsonencode({
    command = ["echo", "test"]
    image   = "busybox"
    memory  = 128
    vcpus   = 1
  })
  name = %[1]q
  type = "container"
}
`, rName)
}
<commit_msg>tests/resource/aws_batch_job_definition: Filter sweeper input on ACTIVE status (#13496)<commit_after>package aws

import (
	"fmt"
	"log"
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/batch"
	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func init() {
	resource.AddTestSweepers("aws_batch_job_definition", &resource.Sweeper{
		Name: "aws_batch_job_definition",
		F:    testSweepBatchJobDefinitions,
		Dependencies: []string{
			"aws_batch_job_queue",
		},
	})
}

func testSweepBatchJobDefinitions(region string) error {
	client, err := sharedClientForRegion(region)
	if err != nil {
		return fmt.Errorf("error getting client: %w", err)
	}
	conn := client.(*AWSClient).batchconn
	input := &batch.DescribeJobDefinitionsInput{
		Status: aws.String("ACTIVE"),
	}
	var sweeperErrs *multierror.Error

	err = conn.DescribeJobDefinitionsPages(input, func(page *batch.DescribeJobDefinitionsOutput, isLast bool) bool {
		if page == nil {
			return !isLast
		}

		for _, jobDefinition := range page.JobDefinitions {
			arn := aws.StringValue(jobDefinition.JobDefinitionArn)

			log.Printf("[INFO] Deleting Batch Job Definition: %s", arn)
			_, err := conn.DeregisterJobDefinition(&batch.DeregisterJobDefinitionInput{
				JobDefinition: aws.String(arn),
			})
			if err != nil {
				sweeperErr := fmt.Errorf("error deleting Batch Job Definition (%s): %w", arn, err)
				log.Printf("[ERROR] %s", sweeperErr)
				sweeperErrs = multierror.Append(sweeperErrs, sweeperErr)
				continue
			}
		}

		return !isLast
	})
	if testSweepSkipSweepError(err) {
		log.Printf("[WARN] Skipping Batch Job Definitions sweep for %s: %s", region, err)
		return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors
	}
	if err != nil {
		sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Batch Job Definitions: %w", err))
	}

	return sweeperErrs.ErrorOrNil()
}

func TestAccAWSBatchJobDefinition_basic(t *testing.T) {
	var jd batch.JobDefinition
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_batch_job_definition.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSBatch(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBatchJobDefinitionDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccBatchJobDefinitionConfigName(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &jd),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSBatchJobDefinition_ContainerProperties_Advanced(t *testing.T) {
	var jd batch.JobDefinition
	compare := batch.JobDefinition{
		Parameters: map[string]*string{
			"param1": aws.String("val1"),
			"param2": aws.String("val2"),
		},
		RetryStrategy: &batch.RetryStrategy{
			Attempts: aws.Int64(int64(1)),
		},
		Timeout: &batch.JobTimeout{
			AttemptDurationSeconds: aws.Int64(int64(60)),
		},
		ContainerProperties: &batch.ContainerProperties{
			Command: []*string{aws.String("ls"), aws.String("-la")},
			Environment: []*batch.KeyValuePair{
				{Name: aws.String("VARNAME"), Value: aws.String("VARVAL")},
			},
			Image:  aws.String("busybox"),
			Memory: aws.Int64(int64(512)),
			MountPoints: []*batch.MountPoint{
				{ContainerPath: aws.String("/tmp"), ReadOnly: aws.Bool(false), SourceVolume: aws.String("tmp")},
			},
			ResourceRequirements: []*batch.ResourceRequirement{},
			Ulimits: []*batch.Ulimit{
				{HardLimit: aws.Int64(int64(1024)), Name: aws.String("nofile"), SoftLimit: aws.Int64(int64(1024))},
			},
			Vcpus: aws.Int64(int64(1)),
			Volumes: []*batch.Volume{
				{
					Host: &batch.Host{SourcePath: aws.String("/tmp")},
					Name: aws.String("tmp"),
				},
			},
		},
	}
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_batch_job_definition.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSBatch(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBatchJobDefinitionDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccBatchJobDefinitionConfigContainerPropertiesAdvanced(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &jd),
					testAccCheckBatchJobDefinitionAttributes(&jd, &compare),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSBatchJobDefinition_updateForcesNewResource(t *testing.T) {
	var before, after batch.JobDefinition
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_batch_job_definition.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSBatch(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBatchJobDefinitionDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccBatchJobDefinitionConfigContainerPropertiesAdvanced(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &before),
					testAccCheckBatchJobDefinitionAttributes(&before, nil),
				),
			},
			{
				Config: testAccBatchJobDefinitionConfigContainerPropertiesAdvancedUpdate(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBatchJobDefinitionExists(resourceName, &after),
					testAccCheckJobDefinitionRecreated(t, &before, &after),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckBatchJobDefinitionExists(n string, jd *batch.JobDefinition) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Batch Job Queue ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).batchconn
		arn := rs.Primary.Attributes["arn"]
		def, err := getJobDefinition(conn, arn)
		if err != nil {
			return err
		}
		if def == nil {
			return fmt.Errorf("Not found: %s", n)
		}
		*jd = *def

		return nil
	}
}

func testAccCheckBatchJobDefinitionAttributes(jd *batch.JobDefinition, compare *batch.JobDefinition) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		for _, rs := range s.RootModule().Resources {
			if rs.Type != "aws_batch_job_definition" {
				continue
			}
			if *jd.JobDefinitionArn != rs.Primary.Attributes["arn"] {
				return fmt.Errorf("Bad Job Definition ARN\n\t expected: %s\n\tgot: %s\n", rs.Primary.Attributes["arn"], *jd.JobDefinitionArn)
			}
			if compare != nil {
				if compare.Parameters != nil && !reflect.DeepEqual(compare.Parameters, jd.Parameters) {
					return fmt.Errorf("Bad Job Definition Params\n\t expected: %v\n\tgot: %v\n", compare.Parameters, jd.Parameters)
				}
				if compare.RetryStrategy != nil && *compare.RetryStrategy.Attempts != *jd.RetryStrategy.Attempts {
					return fmt.Errorf("Bad Job Definition Retry Strategy\n\t expected: %d\n\tgot: %d\n", *compare.RetryStrategy.Attempts, *jd.RetryStrategy.Attempts)
				}
				if compare.ContainerProperties != nil && compare.ContainerProperties.Command != nil && !reflect.DeepEqual(compare.ContainerProperties, jd.ContainerProperties) {
					return fmt.Errorf("Bad Job Definition Container Properties\n\t expected: %s\n\tgot: %s\n", compare.ContainerProperties, jd.ContainerProperties)
				}
			}
		}
		return nil
	}
}

func testAccCheckJobDefinitionRecreated(t *testing.T,
	before, after *batch.JobDefinition) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if *before.Revision == *after.Revision {
			t.Fatalf("Expected change of JobDefinition Revisions, but both were %v", before.Revision)
		}
		return nil
	}
}

func testAccCheckBatchJobDefinitionDestroy(s *terraform.State) error {
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_batch_job_definition" {
			continue
		}
		conn := testAccProvider.Meta().(*AWSClient).batchconn
		js, err := getJobDefinition(conn, rs.Primary.Attributes["arn"])
		if err == nil && js != nil {
			if *js.Status == "ACTIVE" {
				return fmt.Errorf("Error: Job Definition still active")
			}
		}
		return nil
	}
	return nil
}

func testAccBatchJobDefinitionConfigContainerPropertiesAdvanced(rName string) string {
	return fmt.Sprintf(`
resource "aws_batch_job_definition" "test" {
	name = %[1]q
	type = "container"
	parameters = {
		param1 = "val1"
		param2 = "val2"
	}
	retry_strategy {
		attempts = 1
	}
	timeout {
		attempt_duration_seconds = 60
	}
	container_properties = <<CONTAINER_PROPERTIES
{
	"command": ["ls", "-la"],
	"image": "busybox",
	"memory": 512,
	"vcpus": 1,
	"volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
	"environment": [
		{"name": "VARNAME", "value": "VARVAL"}
	],
	"mountPoints": [
		{
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
	],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}
`, rName)
}

func testAccBatchJobDefinitionConfigContainerPropertiesAdvancedUpdate(rName string) string {
	return fmt.Sprintf(`
resource "aws_batch_job_definition" "test" {
	name = %[1]q
	type = "container"
	container_properties = <<CONTAINER_PROPERTIES
{
	"command": ["ls", "-la"],
	"image": "busybox",
	"memory": 1024,
	"vcpus": 1,
	"volumes": [
      {
        "host": {
          "sourcePath": "/tmp"
        },
        "name": "tmp"
      }
    ],
	"environment": [
		{"name": "VARNAME", "value": "VARVAL"}
	],
	"mountPoints": [
		{
          "sourceVolume": "tmp",
          "containerPath": "/tmp",
          "readOnly": false
        }
	],
    "ulimits": [
      {
        "hardLimit": 1024,
        "name": "nofile",
        "softLimit": 1024
      }
    ]
}
CONTAINER_PROPERTIES
}
`, rName)
}

func testAccBatchJobDefinitionConfigName(rName string) string {
	return fmt.Sprintf(`
resource "aws_batch_job_definition" "test" {
  container_properties = jsonencode({
    command = ["echo", "test"]
    image   = "busybox"
    memory  = 128
    vcpus   = 1
  })
  name = %[1]q
  type = "container"
}
`, rName)
}
<|endoftext|>


<commit_before>package params

const (
	// ClientIdentifier is client identifier to advertise over the network
	ClientIdentifier = "StatusIM"

	// DataDir is default data directory used by statusd executable
	DataDir = "statusd-data"

	// KeyStoreDir is default directory where private keys are stored, relative to DataDir
	KeyStoreDir = "keystore"

	// IPCFile is filename of exposed IPC RPC Server
	IPCFile = "geth.ipc"

	// RPCEnabledDefault is the default state of whether the http rpc server is supposed
	// to be started along with a node.
	RPCEnabledDefault = false

	// HTTPHost is host interface for the HTTP RPC server
	HTTPHost = "localhost"

	// HTTPPort is HTTP-RPC port (replaced in unit tests)
	HTTPPort = 8545

	// ListenAddr is an IP address and port of this node (e.g. 127.0.0.1:30303).
	ListenAddr = ":0"

	// APIModules is a list of modules to expose via any type of RPC (HTTP, IPC, in-proc)
	APIModules = "db,eth,net,web3,shh,personal,admin,debug"

	// WSHost is a host interface for the websocket RPC server
	WSHost = "localhost"

	// SendTransactionMethodName defines the name for a giving transaction.
	SendTransactionMethodName = "eth_sendTransaction"

	// WSPort is a WS-RPC port (replaced in unit tests)
	WSPort = 8546

	// MaxPeers is the maximum number of global peers
	MaxPeers = 25

	// MaxPendingPeers is the maximum number of peers that can be pending in the
	// handshake phase, counted separately for inbound and outbound connections.
	MaxPendingPeers = 0

	// DefaultGas default amount of gas used for transactions
	DefaultGas = 180000

	// DefaultFileDescriptorLimit is fd limit that database can use
	DefaultFileDescriptorLimit = uint64(2048)

	// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
	DatabaseCache = 16

	// LogFile defines where to write logs to
	LogFile = ""

	// LogLevel defines the minimum log level to report
	LogLevel = "ERROR"

	// LogLevelSuccinct defines the log level when only errors are reported.
	// Useful when the default INFO level becomes too verbose.
	LogLevelSuccinct = "ERROR"

	// LogToStderr defines whether logged info should also be output to os.Stderr
	LogToStderr = true

	// WhisperDataDir is directory where Whisper data is stored, relative to DataDir
	WhisperDataDir = "wnode"

	// WhisperMinimumPoW amount of work for Whisper message to be added to sending queue
	WhisperMinimumPoW = 0.001

	// WhisperTTL is time to live for messages, in seconds
	WhisperTTL = 120

	// FirebaseNotificationTriggerURL is URL where FCM notification requests are sent to
	FirebaseNotificationTriggerURL = "https://fcm.googleapis.com/fcm/send"

	// MainnetEthereumNetworkURL is URL where the upstream ethereum network is loaded to
	// allow us avoid syncing node.
	MainnetEthereumNetworkURL = "https://mainnet.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

	// RopstenEthereumNetworkURL is URL where the upstream ethereum network is loaded to
	// allow us avoid syncing node.
	RopstenEthereumNetworkURL = "https://ropsten.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

	// RinkebyEthereumNetworkURL is URL where the upstream ethereum network is loaded to
	// allow us avoid syncing node.
	RinkebyEthereumNetworkURL = "https://rinkeby.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

	// MainNetworkID is id of the main network
	MainNetworkID = 1

	// RopstenNetworkID is id of a test network (on PoW)
	RopstenNetworkID = 3

	// RinkebyNetworkID is id of a test network (on PoA)
	RinkebyNetworkID = 4

	// StatusChainNetworkID is id of a test network (private chain)
	StatusChainNetworkID = 777
)
<commit_msg>Remove admin,debug,db api from exposed modules<commit_after>package params

const (
	// ClientIdentifier is client identifier to advertise over the network
	ClientIdentifier = "StatusIM"

	// DataDir is default data directory used by statusd executable
	DataDir = "statusd-data"

	// KeyStoreDir is default directory where private keys are stored, relative to DataDir
	KeyStoreDir = "keystore"

	// IPCFile is filename of exposed IPC RPC Server
	IPCFile = "geth.ipc"

	// RPCEnabledDefault is the default state of whether the http rpc server is supposed
	// to be started along with a node.
	RPCEnabledDefault = false

	// HTTPHost is host interface for the HTTP RPC server
	HTTPHost = "localhost"

	// HTTPPort is HTTP-RPC port (replaced in unit tests)
	HTTPPort = 8545

	// ListenAddr is an IP address and port of this node (e.g. 127.0.0.1:30303).
	ListenAddr = ":0"

	// APIModules is a list of modules to expose via any type of RPC (HTTP, IPC, in-proc)
	APIModules = "eth,net,web3,shh,personal"

	// WSHost is a host interface for the websocket RPC server
	WSHost = "localhost"

	// SendTransactionMethodName defines the name for a giving transaction.
	SendTransactionMethodName = "eth_sendTransaction"

	// WSPort is a WS-RPC port (replaced in unit tests)
	WSPort = 8546

	// MaxPeers is the maximum number of global peers
	MaxPeers = 25

	// MaxPendingPeers is the maximum number of peers that can be pending in the
	// handshake phase, counted separately for inbound and outbound connections.
	MaxPendingPeers = 0

	// DefaultGas default amount of gas used for transactions
	DefaultGas = 180000

	// DefaultFileDescriptorLimit is fd limit that database can use
	DefaultFileDescriptorLimit = uint64(2048)

	// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
	DatabaseCache = 16

	// LogFile defines where to write logs to
	LogFile = ""

	// LogLevel defines the minimum log level to report
	LogLevel = "ERROR"

	// LogLevelSuccinct defines the log level when only errors are reported.
	// Useful when the default INFO level becomes too verbose.
	LogLevelSuccinct = "ERROR"

	// LogToStderr defines whether logged info should also be output to os.Stderr
	LogToStderr = true

	// WhisperDataDir is directory where Whisper data is stored, relative to DataDir
	WhisperDataDir = "wnode"

	// WhisperMinimumPoW amount of work for Whisper message to be added to sending queue
	WhisperMinimumPoW = 0.001

	// WhisperTTL is time to live for messages, in seconds
	WhisperTTL = 120

	// FirebaseNotificationTriggerURL is URL where FCM notification requests are sent to
	FirebaseNotificationTriggerURL = "https://fcm.googleapis.com/fcm/send"

	// MainnetEthereumNetworkURL is URL where the upstream ethereum network is loaded to
	// allow us avoid syncing node.
	MainnetEthereumNetworkURL = "https://mainnet.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

	// RopstenEthereumNetworkURL is URL where the upstream ethereum network is loaded to
	// allow us avoid syncing node.
	RopstenEthereumNetworkURL = "https://ropsten.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

	// RinkebyEthereumNetworkURL is URL where the upstream ethereum network is loaded to
	// allow us avoid syncing node.
	RinkebyEthereumNetworkURL = "https://rinkeby.infura.io/nKmXgiFgc2KqtoQ8BCGJ"

	// MainNetworkID is id of the main network
	MainNetworkID = 1

	// RopstenNetworkID is id of a test network (on PoW)
	RopstenNetworkID = 3

	// RinkebyNetworkID is id of a test network (on PoA)
	RinkebyNetworkID = 4

	// StatusChainNetworkID is id of a test network (private chain)
	StatusChainNetworkID = 777
)
<|endoftext|>


<commit_before>package elementary

// #include <Elementary.h>
import "C"

import (
	"unsafe"

	"github.com/zenhack/go.efl/evas"
)

type Object evas.Object

func (o *Object) SmartCallbackAdd(event string, callback func(evInfo unsafe.Pointer)) {
	(*evas.Object)(o).SmartCallbackAdd(event, callback)
}

func (o *Object) Show() {
	(*evas.Object)(o).Show()
}

func (o *Object) TextSet(text string) {
	cText := C.CString(text)
	// XXX: elm_object_text set is defined as a macro, and for whatever reason cgo
	// isn't picking it up (getting an error to the effect that the symbol isn't
	// defined). instead, we're just calling the underlying function ourselves,
	// but it would be nice to not need to.
	//
	// C.elm_object_text_set((*C.Evas_Object)(o), cText)
	C.elm_object_part_text_set((*C.Evas_Object)(o), nil, cText)
}
<commit_msg>Added a note about a memory leak.<commit_after>package elementary

// #include <Elementary.h>
import "C"

import (
	"unsafe"

	"github.com/zenhack/go.efl/evas"
)

type Object evas.Object

func (o *Object) SmartCallbackAdd(event string, callback func(evInfo unsafe.Pointer)) {
	(*evas.Object)(o).SmartCallbackAdd(event, callback)
}

func (o *Object) Show() {
	(*evas.Object)(o).Show()
}

func (o *Object) TextSet(text string) {
	cText := C.CString(text)
	// XXX: elm_object_text set is defined as a macro, and for whatever reason cgo
	// isn't picking it up (getting an error to the effect that the symbol isn't
	// defined). instead, we're just calling the underlying function ourselves,
	// but it would be nice to not need to.
	//
	// C.elm_object_text_set((*C.Evas_Object)(o), cText)
	C.elm_object_part_text_set((*C.Evas_Object)(o), nil, cText)

	// FIXME: we're never freeing cText - I'm not totally sure what the memory-ownership
	// rules are here; will have to investigate and then fix this.
}
<|endoftext|>


<commit_before>// Copyright 2017 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// gen-accessors generates accessor methods for structs with pointer fields.
//
// It is meant to be used by the go-github authors in conjunction with the
// go generate tool before sending a commit to GitHub.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"
	"text/template"
	"time"
)

const (
	fileSuffix = "-accessors.go"
)

var (
	verbose = flag.Bool("v", false, "Print verbose log messages")

	sourceTmpl = template.Must(template.New("source").Parse(source))

	// blacklistStructMethod lists "struct.method" combos to skip.
	blacklistStructMethod = map[string]bool{
		"RepositoryContent.GetContent":    true,
		"Client.GetBaseURL":               true,
		"Client.GetUploadURL":             true,
		"ErrorResponse.GetResponse":       true,
		"RateLimitError.GetResponse":      true,
		"AbuseRateLimitError.GetResponse": true,
	}
	// blacklistStruct lists structs to skip.
	blacklistStruct = map[string]bool{
		"Client": true,
	}
)

func logf(fmt string, args ...interface{}) {
	if *verbose {
		log.Printf(fmt, args...)
	}
}

func main() {
	flag.Parse()
	fset := token.NewFileSet()

	pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0)
	if err != nil {
		log.Fatal(err)
		return
	}

	for pkgName, pkg := range pkgs {
		t := &templateData{
			filename: pkgName + fileSuffix,
			Year:     time.Now().Year(),
			Package:  pkgName,
			Imports:  map[string]string{},
		}
		for filename, f := range pkg.Files {
			logf("Processing %v...", filename)
			if err := t.processAST(f); err != nil {
				log.Fatal(err)
			}
		}
		if err := t.dump(); err != nil {
			log.Fatal(err)
		}
	}
	logf("Done.")
}

func (t *templateData) processAST(f *ast.File) error {
	for _, decl := range f.Decls {
		gd, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}
		for _, spec := range gd.Specs {
			ts, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}
			// Skip unexported identifiers.
			if !ts.Name.IsExported() {
				logf("Struct %v is unexported; skipping.", ts.Name)
				continue
			}
			// Check if the struct is blacklisted.
			if blacklistStruct[ts.Name.Name] {
				logf("Struct %v is blacklisted; skipping.", ts.Name)
				continue
			}
			st, ok := ts.Type.(*ast.StructType)
			if !ok {
				continue
			}
			for _, field := range st.Fields.List {
				se, ok := field.Type.(*ast.StarExpr)
				if len(field.Names) == 0 || !ok {
					continue
				}

				fieldName := field.Names[0]
				// Skip unexported identifiers.
				if !fieldName.IsExported() {
					logf("Field %v is unexported; skipping.", fieldName)
					continue
				}
				// Check if "struct.method" is blacklisted.
				if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] {
					logf("Method %v is blacklisted; skipping.", key)
					continue
				}

				switch x := se.X.(type) {
				case *ast.ArrayType:
					t.addArrayType(x, ts.Name.String(), fieldName.String())
				case *ast.Ident:
					t.addIdent(x, ts.Name.String(), fieldName.String())
				case *ast.MapType:
					t.addMapType(x, ts.Name.String(), fieldName.String())
				case *ast.SelectorExpr:
					t.addSelectorExpr(x, ts.Name.String(), fieldName.String())
				default:
					logf("processAST: type %q, field %q, unknown %T: %+v", ts.Name, fieldName, x, x)
				}
			}
		}
	}
	return nil
}

func sourceFilter(fi os.FileInfo) bool {
	return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix)
}

func (t *templateData) dump() error {
	if len(t.Getters) == 0 {
		logf("No getters for %v; skipping.", t.filename)
		return nil
	}

	// Sort getters by ReceiverType.FieldName.
	sort.Sort(byName(t.Getters))

	var buf bytes.Buffer
	if err := sourceTmpl.Execute(&buf, t); err != nil {
		return err
	}
	clean, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	logf("Writing %v...", t.filename)
	return ioutil.WriteFile(t.filename, clean, 0644)
}

func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter {
	return &getter{
		sortVal:      strings.ToLower(receiverType) + "." + strings.ToLower(fieldName),
		ReceiverVar:  strings.ToLower(receiverType[:1]),
		ReceiverType: receiverType,
		FieldName:    fieldName,
		FieldType:    fieldType,
		ZeroValue:    zeroValue,
		NamedStruct:  namedStruct,
	}
}

func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) {
	var eltType string
	switch elt := x.Elt.(type) {
	case *ast.Ident:
		eltType = elt.String()
	default:
		logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt)
		return
	}

	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false))
}

func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) {
	var zeroValue string
	var namedStruct = false
	switch x.String() {
	case "int":
		zeroValue = "0"
	case "string":
		zeroValue = `""`
	case "bool":
		zeroValue = "false"
	case "Timestamp":
		zeroValue = "Timestamp{}"
	default:
		zeroValue = "nil"
		namedStruct = true
	}

	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct))
}

func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) {
	var keyType string
	switch key := x.Key.(type) {
	case *ast.Ident:
		keyType = key.String()
	default:
		logf("addMapType: type %q, field %q: unknown key type: %T %+v; skipping.", receiverType, fieldName, key, key)
		return
	}

	var valueType string
	switch value := x.Value.(type) {
	case *ast.Ident:
		valueType = value.String()
	default:
		logf("addMapType: type %q, field %q: unknown value type: %T %+v; skipping.", receiverType, fieldName, value, value)
		return
	}

	fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType)
	zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType)
	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false))
}

func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) {
	if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field.
		return
	}

	var xX string
	if xx, ok := x.X.(*ast.Ident); ok {
		xX = xx.String()
	}

	switch xX {
	case "time", "json":
		if xX == "json" {
			t.Imports["encoding/json"] = "encoding/json"
		} else {
			t.Imports[xX] = xX
		}
		fieldType := fmt.Sprintf("%v.%v", xX, x.Sel.Name)
		zeroValue := fmt.Sprintf("%v.%v{}", xX, x.Sel.Name)
		if xX == "time" && x.Sel.Name == "Duration" {
			zeroValue = "0"
		}
		t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false))
	default:
		logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x)
	}
}

type templateData struct {
	filename string
	Year     int
	Package  string
	Imports  map[string]string
	Getters  []*getter
}

type getter struct {
	sortVal      string // Lower-case version of "ReceiverType.FieldName".
	ReceiverVar  string // The one-letter variable name to match the ReceiverType.
	ReceiverType string
	FieldName    string
	FieldType    string
	ZeroValue    string
	NamedStruct  bool // Getter for named struct.
}

type byName []*getter

func (b byName) Len() int           { return len(b) }
func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal }
func (b byName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Code generated by gen-accessors; DO NOT EDIT.

package {{.Package}}
{{with .Imports}}
import (
  {{- range . -}}
  "{{.}}"
  {{end -}}
)
{{end}}
{{range .Getters}}
{{if .NamedStruct}}
// Get{{.FieldName}} returns the {{.FieldName}} field.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} {
  if {{.ReceiverVar}} == nil {
    return {{.ZeroValue}}
  }
  return {{.ReceiverVar}}.{{.FieldName}}
}
{{else}}
// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} {
  if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil {
    return {{.ZeroValue}}
  }
  return *{{.ReceiverVar}}.{{.FieldName}}
}
{{end}}
{{end}}
`
<commit_msg>Hardcode 2017 as copyright year in github-accessors.go. (#825)<commit_after>// Copyright 2017 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// gen-accessors generates accessor methods for structs with pointer fields.
//
// It is meant to be used by the go-github authors in conjunction with the
// go generate tool before sending a commit to GitHub.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"
	"text/template"
)

const (
	fileSuffix = "-accessors.go"
)

var (
	verbose = flag.Bool("v", false, "Print verbose log messages")

	sourceTmpl = template.Must(template.New("source").Parse(source))

	// blacklistStructMethod lists "struct.method" combos to skip.
	blacklistStructMethod = map[string]bool{
		"RepositoryContent.GetContent":    true,
		"Client.GetBaseURL":               true,
		"Client.GetUploadURL":             true,
		"ErrorResponse.GetResponse":       true,
		"RateLimitError.GetResponse":      true,
		"AbuseRateLimitError.GetResponse": true,
	}
	// blacklistStruct lists structs to skip.
	blacklistStruct = map[string]bool{
		"Client": true,
	}
)

func logf(fmt string, args ...interface{}) {
	if *verbose {
		log.Printf(fmt, args...)
	}
}

func main() {
	flag.Parse()
	fset := token.NewFileSet()

	pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0)
	if err != nil {
		log.Fatal(err)
		return
	}

	for pkgName, pkg := range pkgs {
		t := &templateData{
			filename: pkgName + fileSuffix,
			Year:     2017,
			Package:  pkgName,
			Imports:  map[string]string{},
		}
		for filename, f := range pkg.Files {
			logf("Processing %v...", filename)
			if err := t.processAST(f); err != nil {
				log.Fatal(err)
			}
		}
		if err := t.dump(); err != nil {
			log.Fatal(err)
		}
	}
	logf("Done.")
}

func (t *templateData) processAST(f *ast.File) error {
	for _, decl := range f.Decls {
		gd, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}
		for _, spec := range gd.Specs {
			ts, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}
			// Skip unexported identifiers.
			if !ts.Name.IsExported() {
				logf("Struct %v is unexported; skipping.", ts.Name)
				continue
			}
			// Check if the struct is blacklisted.
			if blacklistStruct[ts.Name.Name] {
				logf("Struct %v is blacklisted; skipping.", ts.Name)
				continue
			}
			st, ok := ts.Type.(*ast.StructType)
			if !ok {
				continue
			}
			for _, field := range st.Fields.List {
				se, ok := field.Type.(*ast.StarExpr)
				if len(field.Names) == 0 || !ok {
					continue
				}

				fieldName := field.Names[0]
				// Skip unexported identifiers.
				if !fieldName.IsExported() {
					logf("Field %v is unexported; skipping.", fieldName)
					continue
				}
				// Check if "struct.method" is blacklisted.
				if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] {
					logf("Method %v is blacklisted; skipping.", key)
					continue
				}

				switch x := se.X.(type) {
				case *ast.ArrayType:
					t.addArrayType(x, ts.Name.String(), fieldName.String())
				case *ast.Ident:
					t.addIdent(x, ts.Name.String(), fieldName.String())
				case *ast.MapType:
					t.addMapType(x, ts.Name.String(), fieldName.String())
				case *ast.SelectorExpr:
					t.addSelectorExpr(x, ts.Name.String(), fieldName.String())
				default:
					logf("processAST: type %q, field %q, unknown %T: %+v", ts.Name, fieldName, x, x)
				}
			}
		}
	}
	return nil
}

func sourceFilter(fi os.FileInfo) bool {
	return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix)
}

func (t *templateData) dump() error {
	if len(t.Getters) == 0 {
		logf("No getters for %v; skipping.", t.filename)
		return nil
	}

	// Sort getters by ReceiverType.FieldName.
	sort.Sort(byName(t.Getters))

	var buf bytes.Buffer
	if err := sourceTmpl.Execute(&buf, t); err != nil {
		return err
	}
	clean, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	logf("Writing %v...", t.filename)
	return ioutil.WriteFile(t.filename, clean, 0644)
}

func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter {
	return &getter{
		sortVal:      strings.ToLower(receiverType) + "." + strings.ToLower(fieldName),
		ReceiverVar:  strings.ToLower(receiverType[:1]),
		ReceiverType: receiverType,
		FieldName:    fieldName,
		FieldType:    fieldType,
		ZeroValue:    zeroValue,
		NamedStruct:  namedStruct,
	}
}

func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) {
	var eltType string
	switch elt := x.Elt.(type) {
	case *ast.Ident:
		eltType = elt.String()
	default:
		logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt)
		return
	}

	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false))
}

func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) {
	var zeroValue string
	var namedStruct = false
	switch x.String() {
	case "int":
		zeroValue = "0"
	case "string":
		zeroValue = `""`
	case "bool":
		zeroValue = "false"
	case "Timestamp":
		zeroValue = "Timestamp{}"
	default:
		zeroValue = "nil"
		namedStruct = true
	}

	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct))
}

func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) {
	var keyType string
	switch key := x.Key.(type) {
	case *ast.Ident:
		keyType = key.String()
	default:
		logf("addMapType: type %q, field %q: unknown key type: %T %+v; skipping.", receiverType, fieldName, key, key)
		return
	}

	var valueType string
	switch value := x.Value.(type) {
	case *ast.Ident:
		valueType = value.String()
	default:
		logf("addMapType: type %q, field %q: unknown value type: %T %+v; skipping.", receiverType, fieldName, value, value)
		return
	}

	fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType)
	zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType)
	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false))
}

func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) {
	if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field.
		return
	}

	var xX string
	if xx, ok := x.X.(*ast.Ident); ok {
		xX = xx.String()
	}

	switch xX {
	case "time", "json":
		if xX == "json" {
			t.Imports["encoding/json"] = "encoding/json"
		} else {
			t.Imports[xX] = xX
		}
		fieldType := fmt.Sprintf("%v.%v", xX, x.Sel.Name)
		zeroValue := fmt.Sprintf("%v.%v{}", xX, x.Sel.Name)
		if xX == "time" && x.Sel.Name == "Duration" {
			zeroValue = "0"
		}
		t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false))
	default:
		logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x)
	}
}

type templateData struct {
	filename string
	Year     int
	Package  string
	Imports  map[string]string
	Getters  []*getter
}

type getter struct {
	sortVal      string // Lower-case version of "ReceiverType.FieldName".
	ReceiverVar  string // The one-letter variable name to match the ReceiverType.
	ReceiverType string
	FieldName    string
	FieldType    string
	ZeroValue    string
	NamedStruct  bool // Getter for named struct.
}

type byName []*getter

func (b byName) Len() int           { return len(b) }
func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal }
func (b byName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Code generated by gen-accessors; DO NOT EDIT.

package {{.Package}}
{{with .Imports}}
import (
  {{- range . -}}
  "{{.}}"
  {{end -}}
)
{{end}}
{{range .Getters}}
{{if .NamedStruct}}
// Get{{.FieldName}} returns the {{.FieldName}} field.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} {
  if {{.ReceiverVar}} == nil {
    return {{.ZeroValue}}
  }
  return {{.ReceiverVar}}.{{.FieldName}}
}
{{else}}
// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} {
  if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil {
    return {{.ZeroValue}}
  }
  return *{{.ReceiverVar}}.{{.FieldName}}
}
{{end}}
{{end}}
`
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// make sure that even if a file imports runtime,
// it cannot get at the low-level runtime definitions
// known to the compiler.  for normal packages
// the compiler doesn't even record the lower case
// functions in its symbol table, but some functions
// in runtime are hard-coded into the compiler.

package main

import "runtime"

func main() {
	runtime.printbool(true);	// ERROR "cannot refer|undefined identifier"
}
<commit_msg>New gccgo error message; match both compilers with one string.<commit_after>// errchk $G $D/$F.go

// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// make sure that even if a file imports runtime,
// it cannot get at the low-level runtime definitions
// known to the compiler.  for normal packages
// the compiler doesn't even record the lower case
// functions in its symbol table, but some functions
// in runtime are hard-coded into the compiler.

package main

import "runtime"

func main() {
	runtime.printbool(true);	// ERROR "unexported"
}
<|endoftext|>