<commit_before>package goharproxy

import (
	"time"
	"net/http"
	"net/url"
	"strings"
	"log"
	"io/ioutil"
)

var startingEntrySize int = 1000

type Har struct {
	HarLog HarLog	`json:"harLog"`
}

type HarLog struct {
	Version string			`json:"version"`
	Creator string			`json:"creator"`
	Browser string			`json:"browser"`
	Pages   []HarPage		`json:"pages"`
	Entries []HarEntry		`json:"entries"`
}

func newHarLog() *HarLog {
	harLog := HarLog {
		Version : "1.2",
		Creator : "GoHarProxy 0.1",
		Browser : "",
		Pages 	: make([]HarPage, 0, 10),
		Entries : makeNewEntries(),
	}
	return &harLog
}

func (harLog *HarLog) addEntry(entry ...HarEntry) {
	entries := harLog.Entries
	m := len(entries)
	n := m + len(entry)
	if n > cap(entries) { // if necessary, reallocate
		// allocate double what's needed, for future growth.
		newEntries := make([]HarEntry, (n+1)*2)
		copy(newEntries, entries)
		entries = newEntries
	}
	entries = entries[0:n]
	copy(entries[m:n], entry)
	harLog.Entries = entries
}

func makeNewEntries() []HarEntry {
	return make([]HarEntry, 0, startingEntrySize)
}

type HarPage struct {
	Id              string			`json:"id"`
	StartedDateTime time.Time		`json:"startedDateTime"`
	Title           string			`json:"title"`
	PageTimings     HarPageTimings	`json:"pageTimings"`
}

type HarEntry struct {
	PageRef         string			`json:"pageRef"`
	StartedDateTime time.Time		`json:"startedDateTime"`
	Time            int64			`json:"time"`
	Request         *HarRequest		`json:"request"`
	Response        *HarResponse	`json:"response"`
	Timings         HarTimings		`json:"timings"`
	ServerIpAddress string			`json:"serverIpAddress"`
	Connection      string			`json:"connection"`
}

type HarRequest struct {
	Method         string				`json:"method"`
	Url            string				`json:"url"`
	HttpVersion    string				`json:"httpVersion"`
	Cookies        []HarCookie			`json:"cookies"`
	Headers        []HarNameValuePair	`json:"headers"`
	QueryString    []HarNameValuePair	`json:"queryString"`
	PostData       *HarPostData			`json:"postData"`
	BodySize       int64				`json:"bodySize"`
	HeadersSize    int64				`json:"headersSize"`
}

var captureContent bool = false

func parseRequest(req *http.Request) *HarRequest {
	if req == nil {
		return nil
	}
	harRequest := HarRequest {
		Method 		: req.Method,
		Url    		: req.URL.String(),
		HttpVersion : req.Proto,
		Cookies 	: parseCookies(req.Cookies()),
		Headers		: parseStringArrMap(req.Header),
		QueryString : parseStringArrMap((req.URL.Query())),
		BodySize	: req.ContentLength,
		HeadersSize : calcHeaderSize(req.Header),
	}

	if captureContent && (req.Method == "POST" || req.Method == "PUT") {
		harRequest.PostData = parsePostData(req)
	}

	return &harRequest
}

func calcHeaderSize(header http.Header) int64 {
	headerSize := 0
	for headerName, headerValues := range header {
		headerSize += len(headerName) + 2
		for _, v := range headerValues {
			headerSize += len(v)
		}
	}
	return int64(headerSize)
}

func parsePostData(req *http.Request) *HarPostData {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing request to %v: %v\n", req.URL, e)
		}
	}()

	harPostData := new(HarPostData)
	contentType := req.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in request")
	}
	harPostData.MimeType = contentType[0]

	if len(req.PostForm) > 0 {
		index := 0
		params := make([]HarPostDataParam, len(req.PostForm))
		for k, v := range req.PostForm {
			param := HarPostDataParam {
				Name  : k,
				Value : strings.Join(v, ","),
			}
			params[index] = param
			index++
		}
		harPostData.Params = params
	} else {
		str, _ := ioutil.ReadAll(req.Body)
		harPostData.Text = string(str)
	}
	return harPostData
}

func parseStringArrMap(stringArrMap map[string][]string) []HarNameValuePair {
	index := 0
	harQueryString := make([]HarNameValuePair, len(stringArrMap))
	for k, v := range stringArrMap {
		escapedKey, _ 	 := url.QueryUnescape(k)
		escapedValues, _ := url.QueryUnescape(strings.Join(v, ","))
		harNameValuePair := HarNameValuePair {
			Name  : escapedKey,
			Value : escapedValues,
		}
		harQueryString[index] = harNameValuePair
		index++
	}
	return harQueryString
}

func parseCookies(cookies []*http.Cookie) []HarCookie {
	harCookies := make([]HarCookie, len(cookies))
	for i, cookie := range cookies {
		harCookie := HarCookie {
			Name 	 : cookie.Name,
			Domain 	 : cookie.Domain,
			Expires  : cookie.Expires,
			HttpOnly : cookie.HttpOnly,
			Path 	 : cookie.Path,
			Secure 	 : cookie.Secure,
			Value 	 : cookie.Value,
	}
		harCookies[i] = harCookie
	}
	return harCookies
}

type HarResponse struct {
	Status             int					`json:"status"`
	StatusText         string				`json:"statusText"`
	HttpVersion        string				`json:"httpVersion`
	Cookies            []HarCookie			`json:"cookies"`
	Headers            []HarNameValuePair	`json:"headers"`
	Content            *HarContent			`json:"content"`
	RedirectUrl        string				`json:"redirectUrl"`
	BodySize           int64				`json:"bodySize"`
	HeadersSize        int64				`json:"headersSize"`
}

func parseResponse(resp *http.Response) *HarResponse {
	if resp == nil {
		return nil
	}

	harResponse := HarResponse {
		Status			: resp.StatusCode,
		StatusText		: resp.Status,
		HttpVersion		: resp.Proto,
		Cookies			: parseCookies(resp.Cookies()),
		Headers			: parseStringArrMap(resp.Header),
		RedirectUrl		: "",
		BodySize		: resp.ContentLength,
		HeadersSize		: calcHeaderSize(resp.Header),
	}

	if captureContent {
		harResponse.Content = parseContent(resp)
	}

	return &harResponse
}

func parseContent(resp *http.Response) *HarContent{
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing response to %v: %v\n", resp.Request.URL, e)
		}
	}()

	harContent := new(HarContent)
	contentType := resp.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in response")
	}
	harContent.MimeType = contentType[0]
	if (resp.ContentLength == 0) {
		log.Println("Empty content")
		return nil
	}

	body, _ := ioutil.ReadAll(resp.Body)
	harContent.Text = string(body)
	return harContent
}

type HarCookie struct {
	Name     string			`json:"name"`
	Value    string			`json:"value"`
	Path     string			`json:"path"`
	Domain   string			`json:"domain"`
	Expires  time.Time		`json:"expires"`
	HttpOnly bool			`json:"httpOnly"`
	Secure   bool			`json:"secure"`
}

type HarNameValuePair struct {
	Name  string		`json:"name"`
	Value string		`json:"value"`
}

type HarPostData struct {
	MimeType string					`json:"mimeType"`
	Params   []HarPostDataParam		`json:"params"`
	Text     string					`json:"text"`
}

type HarPostDataParam struct {
	Name        string		`json:"name"`
	Value       string		`json:"value"`
	FileName    string		`json:"fileName"`
	ContentType string		`json:"contentType`
}

type HarContent struct {
	Size        int64		`json:"size"`
	Compression int64		`json:"compression"`
	MimeType    string		`json:"mimeType"`
	Text        string		`json:"text"`
	Encoding    string		`json:"encoding"`
}

type HarPageTimings struct {
	OnContentLoad int64		`json:"onContentLoad"`
	OnLoad        int64		`json:"onLoad"`
}

type HarTimings struct {
	Blocked int64
	Dns     int64
	Connect int64
	Send    int64
	Wait    int64
	Receive int64
	Ssl     int64
}

<commit_msg>Handle content lenght <= 0<commit_after>package goharproxy

import (
	"time"
	"net/http"
	"net/url"
	"strings"
	"log"
	"io/ioutil"
)

var startingEntrySize int = 1000

type Har struct {
	HarLog HarLog	`json:"harLog"`
}

type HarLog struct {
	Version string			`json:"version"`
	Creator string			`json:"creator"`
	Browser string			`json:"browser"`
	Pages   []HarPage		`json:"pages"`
	Entries []HarEntry		`json:"entries"`
}

func newHarLog() *HarLog {
	harLog := HarLog {
		Version : "1.2",
		Creator : "GoHarProxy 0.1",
		Browser : "",
		Pages 	: make([]HarPage, 0, 10),
		Entries : makeNewEntries(),
	}
	return &harLog
}

func (harLog *HarLog) addEntry(entry ...HarEntry) {
	entries := harLog.Entries
	m := len(entries)
	n := m + len(entry)
	if n > cap(entries) { // if necessary, reallocate
		// allocate double what's needed, for future growth.
		newEntries := make([]HarEntry, (n+1)*2)
		copy(newEntries, entries)
		entries = newEntries
	}
	entries = entries[0:n]
	copy(entries[m:n], entry)
	harLog.Entries = entries
}

func makeNewEntries() []HarEntry {
	return make([]HarEntry, 0, startingEntrySize)
}

type HarPage struct {
	Id              string			`json:"id"`
	StartedDateTime time.Time		`json:"startedDateTime"`
	Title           string			`json:"title"`
	PageTimings     HarPageTimings	`json:"pageTimings"`
}

type HarEntry struct {
	PageRef         string			`json:"pageRef"`
	StartedDateTime time.Time		`json:"startedDateTime"`
	Time            int64			`json:"time"`
	Request         *HarRequest		`json:"request"`
	Response        *HarResponse	`json:"response"`
	Timings         HarTimings		`json:"timings"`
	ServerIpAddress string			`json:"serverIpAddress"`
	Connection      string			`json:"connection"`
}

type HarRequest struct {
	Method         string				`json:"method"`
	Url            string				`json:"url"`
	HttpVersion    string				`json:"httpVersion"`
	Cookies        []HarCookie			`json:"cookies"`
	Headers        []HarNameValuePair	`json:"headers"`
	QueryString    []HarNameValuePair	`json:"queryString"`
	PostData       *HarPostData			`json:"postData"`
	BodySize       int64				`json:"bodySize"`
	HeadersSize    int64				`json:"headersSize"`
}

var captureContent bool = false

func parseRequest(req *http.Request) *HarRequest {
	if req == nil {
		return nil
	}
	harRequest := HarRequest {
		Method 		: req.Method,
		Url    		: req.URL.String(),
		HttpVersion : req.Proto,
		Cookies 	: parseCookies(req.Cookies()),
		Headers		: parseStringArrMap(req.Header),
		QueryString : parseStringArrMap((req.URL.Query())),
		BodySize	: req.ContentLength,
		HeadersSize : calcHeaderSize(req.Header),
	}

	if captureContent && (req.Method == "POST" || req.Method == "PUT") {
		harRequest.PostData = parsePostData(req)
	}

	return &harRequest
}

func calcHeaderSize(header http.Header) int64 {
	headerSize := 0
	for headerName, headerValues := range header {
		headerSize += len(headerName) + 2
		for _, v := range headerValues {
			headerSize += len(v)
		}
	}
	return int64(headerSize)
}

func parsePostData(req *http.Request) *HarPostData {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing request to %v: %v\n", req.URL, e)
		}
	}()

	harPostData := new(HarPostData)
	contentType := req.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in request")
	}
	harPostData.MimeType = contentType[0]

	if len(req.PostForm) > 0 {
		index := 0
		params := make([]HarPostDataParam, len(req.PostForm))
		for k, v := range req.PostForm {
			param := HarPostDataParam {
				Name  : k,
				Value : strings.Join(v, ","),
			}
			params[index] = param
			index++
		}
		harPostData.Params = params
	} else {
		str, _ := ioutil.ReadAll(req.Body)
		harPostData.Text = string(str)
	}
	return harPostData
}

func parseStringArrMap(stringArrMap map[string][]string) []HarNameValuePair {
	index := 0
	harQueryString := make([]HarNameValuePair, len(stringArrMap))
	for k, v := range stringArrMap {
		escapedKey, _ 	 := url.QueryUnescape(k)
		escapedValues, _ := url.QueryUnescape(strings.Join(v, ","))
		harNameValuePair := HarNameValuePair {
			Name  : escapedKey,
			Value : escapedValues,
		}
		harQueryString[index] = harNameValuePair
		index++
	}
	return harQueryString
}

func parseCookies(cookies []*http.Cookie) []HarCookie {
	harCookies := make([]HarCookie, len(cookies))
	for i, cookie := range cookies {
		harCookie := HarCookie {
			Name 	 : cookie.Name,
			Domain 	 : cookie.Domain,
			Expires  : cookie.Expires,
			HttpOnly : cookie.HttpOnly,
			Path 	 : cookie.Path,
			Secure 	 : cookie.Secure,
			Value 	 : cookie.Value,
	}
		harCookies[i] = harCookie
	}
	return harCookies
}

type HarResponse struct {
	Status             int					`json:"status"`
	StatusText         string				`json:"statusText"`
	HttpVersion        string				`json:"httpVersion`
	Cookies            []HarCookie			`json:"cookies"`
	Headers            []HarNameValuePair	`json:"headers"`
	Content            *HarContent			`json:"content"`
	RedirectUrl        string				`json:"redirectUrl"`
	BodySize           int64				`json:"bodySize"`
	HeadersSize        int64				`json:"headersSize"`
}

func parseResponse(resp *http.Response) *HarResponse {
	if resp == nil {
		return nil
	}

	harResponse := HarResponse {
		Status			: resp.StatusCode,
		StatusText		: resp.Status,
		HttpVersion		: resp.Proto,
		Cookies			: parseCookies(resp.Cookies()),
		Headers			: parseStringArrMap(resp.Header),
		RedirectUrl		: "",
		BodySize		: resp.ContentLength,
		HeadersSize		: calcHeaderSize(resp.Header),
	}

	if captureContent {
		harResponse.Content = parseContent(resp)
	}

	return &harResponse
}

func parseContent(resp *http.Response) *HarContent{
	defer func() {
		if e := recover(); e != nil {
			log.Printf("Error parsing response to %v: %v\n", resp.Request.URL, e)
		}
	}()

	harContent := new(HarContent)
	contentType := resp.Header["Content-Type"]
	if contentType == nil {
		panic("Missing content type in response")
	}
	harContent.MimeType = contentType[0]
	if (resp.ContentLength <= 0) {
		log.Println("Empty content")
		return nil
	}

	body, _ := ioutil.ReadAll(resp.Body)
	harContent.Text = string(body)
	return harContent
}

type HarCookie struct {
	Name     string			`json:"name"`
	Value    string			`json:"value"`
	Path     string			`json:"path"`
	Domain   string			`json:"domain"`
	Expires  time.Time		`json:"expires"`
	HttpOnly bool			`json:"httpOnly"`
	Secure   bool			`json:"secure"`
}

type HarNameValuePair struct {
	Name  string		`json:"name"`
	Value string		`json:"value"`
}

type HarPostData struct {
	MimeType string					`json:"mimeType"`
	Params   []HarPostDataParam		`json:"params"`
	Text     string					`json:"text"`
}

type HarPostDataParam struct {
	Name        string		`json:"name"`
	Value       string		`json:"value"`
	FileName    string		`json:"fileName"`
	ContentType string		`json:"contentType`
}

type HarContent struct {
	Size        int64		`json:"size"`
	Compression int64		`json:"compression"`
	MimeType    string		`json:"mimeType"`
	Text        string		`json:"text"`
	Encoding    string		`json:"encoding"`
}

type HarPageTimings struct {
	OnContentLoad int64		`json:"onContentLoad"`
	OnLoad        int64		`json:"onLoad"`
}

type HarTimings struct {
	Blocked int64
	Dns     int64
	Connect int64
	Send    int64
	Wait    int64
	Receive int64
	Ssl     int64
}

<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/html"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"strings"
)

var userAgents = []string{
	"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:11.0) Gecko/20100101 Firefox/11.0",
	"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100 101 Firefox/22.0",
	"Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0",
	"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5",
	"Mozilla/5.0 (Windows; Windows NT 6.1) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5",
}

func getResult(url string) (string, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("User-Agent", userAgents[rand.Intn(len(userAgents))])

	resp, err := client.Do(req)
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		body, err := ioutil.ReadAll(resp.Body)
		return string(body), err
	}
	return "An error occurred", err
}

func normalizeLink(link string) string {
	r, _ := regexp.Compile("&[A-Za-z0-9]+=[A-Za-z0-9-_]+")
	link = r.ReplaceAllString(link, "")
	r, _ = regexp.Compile("/url\\?q=")
	link = r.ReplaceAllString(link, "")
	return fmt.Sprint(link, "?answertab=votes")
}

func extractLinks(numberToExtract int, htmlDoc string) []string {
	doc, err := html.Parse(strings.NewReader(htmlDoc))
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	var links []string
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			for _, a := range n.Attr {
				if a.Key == "href" {
					matched, _ := regexp.MatchString("^(/url\\?q=)?http://(meta.)?stackoverflow.com", a.Val)
					if matched {
						links = append(links, normalizeLink(a.Val))
						break
					}
				}
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)
	return links
}

func searchUrl(https *bool, query []string) string {
	body := "google.com/search?q=site:stackoverflow.com"

	var pre string
	if *https {
		pre = "https://encrypted."
	} else {
		pre = "http://"
	}

	return fmt.Sprint(pre, body, "%20", strings.Join(query, "%20"))
}

func main() {
	https := flag.Bool("https", false, "Use https")
	numAnswers := flag.Int("answers", 1, "Number of answers to retrieve")
	// onlyLinks := flag.Bool("links", false, "Only display answer links, not the result text")

	flag.Parse()

	if len(flag.Args()) == 0 {
		flag.PrintDefaults()
		fmt.Println("Supply a question")
		os.Exit(2)
	}

	var url string = searchUrl(https, flag.Args())

	fmt.Println("url: ", url)
	fmt.Println("answers: ", *numAnswers)

	body, err := getResult(url)

	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	// fmt.Println(body)

	links := extractLinks(numAnswers, body)

	for i := 0; i < len(links); i++ {
		fmt.Println(links[i])
	}
}
<commit_msg>links flag and stop grabbing links past the answers flag count<commit_after>package main

import (
	"code.google.com/p/go.net/html"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"strings"
)

var userAgents = []string{
	"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:11.0) Gecko/20100101 Firefox/11.0",
	"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100 101 Firefox/22.0",
	"Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0",
	"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5",
	"Mozilla/5.0 (Windows; Windows NT 6.1) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5",
}

func getResult(url string) (string, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("User-Agent", userAgents[rand.Intn(len(userAgents))])

	resp, err := client.Do(req)
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		body, err := ioutil.ReadAll(resp.Body)
		return string(body), err
	}
	return "An error occurred", err
}

func normalizeLink(link string) string {
	r, _ := regexp.Compile("&[A-Za-z0-9]+=[A-Za-z0-9-_]+")
	link = r.ReplaceAllString(link, "")
	r, _ = regexp.Compile("/url\\?q=")
	link = r.ReplaceAllString(link, "")
	return fmt.Sprint(link, "?answertab=votes")
}

func extractLinks(numberToExtract int, htmlDoc string) []string {
	doc, err := html.Parse(strings.NewReader(htmlDoc))
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	var links []string
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			for _, a := range n.Attr {
				if a.Key == "href" {
					matched, _ := regexp.MatchString("^(/url\\?q=)?http://(meta.)?stackoverflow.com", a.Val)
					if matched {
						links = append(links, normalizeLink(a.Val))
						break
					}
				}
			}
		}
		for c := n.FirstChild; c != nil && len(links) < numberToExtract; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)
	return links
}

func searchUrl(https *bool, query []string) string {
	body := "google.com/search?q=site:stackoverflow.com"

	var pre string
	if *https {
		pre = "https://encrypted."
	} else {
		pre = "http://"
	}

	return fmt.Sprint(pre, body, "%20", strings.Join(query, "%20"))
}

func main() {
	https := flag.Bool("https", false, "Use https")
	numAnswers := flag.Int("answers", 1, "Number of answers to retrieve")
	onlyLinks := flag.Bool("links", false, "Only display answer links, not the result text")

	flag.Parse()

	if len(flag.Args()) == 0 {
		flag.PrintDefaults()
		fmt.Println("Supply a question")
		os.Exit(2)
	}

	url := searchUrl(https, flag.Args())

	body, err := getResult(url)

	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	links := extractLinks(*numAnswers, body)

	if *onlyLinks {
		for i := 0; i < len(links); i++ {
			fmt.Println(links[i])
		}
		os.Exit(0)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 node

import (
	"fmt"
	"strings"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"

	"k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	appsv1listers "k8s.io/client-go/listers/apps/v1"
	utilpod "k8s.io/kubernetes/pkg/api/v1/pod"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/kubelet/util/format"
	nodepkg "k8s.io/kubernetes/pkg/util/node"

	"k8s.io/klog"
)

// DeletePods will delete all pods from master running on given node,
// and return true if any pods were deleted, or were found pending
// deletion.
func DeletePods(kubeClient clientset.Interface, pods []*v1.Pod, recorder record.EventRecorder, nodeName, nodeUID string, daemonStore appsv1listers.DaemonSetLister) (bool, error) {
	remaining := false
	var updateErrList []error

	if len(pods) > 0 {
		RecordNodeEvent(recorder, nodeName, nodeUID, v1.EventTypeNormal, "DeletingAllPods", fmt.Sprintf("Deleting all Pods from Node %v.", nodeName))
	}

	for i := range pods {
		// Defensive check, also needed for tests.
		if pods[i].Spec.NodeName != nodeName {
			continue
		}

		// Pod will be modified, so making copy is required.
		pod := pods[i].DeepCopy()
		// Set reason and message in the pod object.
		if _, err := SetPodTerminationReason(kubeClient, pod, nodeName); err != nil {
			if apierrors.IsConflict(err) {
				updateErrList = append(updateErrList,
					fmt.Errorf("update status failed for pod %q: %v", format.Pod(pod), err))
				continue
			}
		}
		// if the pod has already been marked for deletion, we still return true that there are remaining pods.
		if pod.DeletionGracePeriodSeconds != nil {
			remaining = true
			continue
		}
		// if the pod is managed by a daemonset, ignore it
		if _, err := daemonStore.GetPodDaemonSets(pod); err == nil {
			// No error means at least one daemonset was found
			continue
		}

		klog.V(2).Infof("Starting deletion of pod %v/%v", pod.Namespace, pod.Name)
		recorder.Eventf(pod, v1.EventTypeNormal, "NodeControllerEviction", "Marking for deletion Pod %s from Node %s", pod.Name, nodeName)
		if err := kubeClient.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil {
			if apierrors.IsNotFound(err) {
				// NotFound error means that pod was already deleted.
				// There is nothing left to do with this pod.
				continue
			}
			return false, err
		}
		remaining = true
	}

	if len(updateErrList) > 0 {
		return false, utilerrors.NewAggregate(updateErrList)
	}
	return remaining, nil
}

// SetPodTerminationReason attempts to set a reason and message in the
// pod status, updates it in the apiserver, and returns an error if it
// encounters one.
func SetPodTerminationReason(kubeClient clientset.Interface, pod *v1.Pod, nodeName string) (*v1.Pod, error) {
	if pod.Status.Reason == nodepkg.NodeUnreachablePodReason {
		return pod, nil
	}

	pod.Status.Reason = nodepkg.NodeUnreachablePodReason
	pod.Status.Message = fmt.Sprintf(nodepkg.NodeUnreachablePodMessage, nodeName, pod.Name)

	var updatedPod *v1.Pod
	var err error
	if updatedPod, err = kubeClient.CoreV1().Pods(pod.Namespace).UpdateStatus(pod); err != nil {
		return nil, err
	}
	return updatedPod, nil
}

// MarkPodsNotReady updates ready status of given pods running on
// given node from master return true if success
func MarkPodsNotReady(kubeClient clientset.Interface, pods []*v1.Pod, nodeName string) error {
	klog.V(2).Infof("Update ready status of pods on node [%v]", nodeName)

	errMsg := []string{}
	for i := range pods {
		// Defensive check, also needed for tests.
		if pods[i].Spec.NodeName != nodeName {
			continue
		}

		// Pod will be modified, so making copy is required.
		pod := pods[i].DeepCopy()
		for _, cond := range pod.Status.Conditions {
			if cond.Type == v1.PodReady {
				cond.Status = v1.ConditionFalse
				if !utilpod.UpdatePodCondition(&pod.Status, &cond) {
					break
				}
				klog.V(2).Infof("Updating ready status of pod %v to false", pod.Name)
				_, err := kubeClient.CoreV1().Pods(pod.Namespace).UpdateStatus(pod)
				if err != nil {
					if apierrors.IsNotFound(err) {
						// NotFound error means that pod was already deleted.
						// There is nothing left to do with this pod.
						continue
					}
					klog.Warningf("Failed to update status for pod %q: %v", format.Pod(pod), err)
					errMsg = append(errMsg, fmt.Sprintf("%v", err))
				}
				break
			}
		}
	}
	if len(errMsg) == 0 {
		return nil
	}
	return fmt.Errorf("%v", strings.Join(errMsg, "; "))
}

// RecordNodeEvent records a event related to a node.
func RecordNodeEvent(recorder record.EventRecorder, nodeName, nodeUID, eventtype, reason, event string) {
	ref := &v1.ObjectReference{
		Kind:      "Node",
		Name:      nodeName,
		UID:       types.UID(nodeUID),
		Namespace: "",
	}
	klog.V(2).Infof("Recording %s event message for node %s", event, nodeName)
	recorder.Eventf(ref, eventtype, reason, "Node %s event: %s", nodeName, event)
}

// RecordNodeStatusChange records a event related to a node status change. (Common to lifecycle and ipam)
func RecordNodeStatusChange(recorder record.EventRecorder, node *v1.Node, newStatus string) {
	ref := &v1.ObjectReference{
		Kind:      "Node",
		Name:      node.Name,
		UID:       node.UID,
		Namespace: "",
	}
	klog.V(2).Infof("Recording status change %s event message for node %s", newStatus, node.Name)
	// TODO: This requires a transaction, either both node status is updated
	// and event is recorded or neither should happen, see issue #6055.
	recorder.Eventf(ref, v1.EventTypeNormal, newStatus, "Node %s status is now: %s", node.Name, newStatus)
}

// SwapNodeControllerTaint returns true in case of success and false
// otherwise.
func SwapNodeControllerTaint(kubeClient clientset.Interface, taintsToAdd, taintsToRemove []*v1.Taint, node *v1.Node) bool {
	for _, taintToAdd := range taintsToAdd {
		now := metav1.Now()
		taintToAdd.TimeAdded = &now
	}

	err := controller.AddOrUpdateTaintOnNode(kubeClient, node.Name, taintsToAdd...)
	if err != nil {
		utilruntime.HandleError(
			fmt.Errorf(
				"unable to taint %+v unresponsive Node %q: %v",
				taintsToAdd,
				node.Name,
				err))
		return false
	}
	klog.V(4).Infof("Added %+v Taint to Node %v", taintsToAdd, node.Name)

	err = controller.RemoveTaintOffNode(kubeClient, node.Name, node, taintsToRemove...)
	if err != nil {
		utilruntime.HandleError(
			fmt.Errorf(
				"unable to remove %+v unneeded taint from unresponsive Node %q: %v",
				taintsToRemove,
				node.Name,
				err))
		return false
	}
	klog.V(4).Infof("Made sure that Node %+v has no %v Taint", node.Name, taintsToRemove)

	return true
}

// AddOrUpdateLabelsOnNode updates the labels on the node and returns true on
// success and false on failure.
func AddOrUpdateLabelsOnNode(kubeClient clientset.Interface, labelsToUpdate map[string]string, node *v1.Node) bool {
	err := controller.AddOrUpdateLabelsOnNode(kubeClient, node.Name, labelsToUpdate)
	if err != nil {
		utilruntime.HandleError(
			fmt.Errorf(
				"unable to update labels %+v for Node %q: %v",
				labelsToUpdate,
				node.Name,
				err))
		return false
	}
	klog.V(4).Infof("Updated labels %+v to Node %v", labelsToUpdate, node.Name)
	return true
}

// CreateAddNodeHandler creates an add node handler.
func CreateAddNodeHandler(f func(node *v1.Node) error) func(obj interface{}) {
	return func(originalObj interface{}) {
		node := originalObj.(*v1.Node).DeepCopy()
		if err := f(node); err != nil {
			utilruntime.HandleError(fmt.Errorf("Error while processing Node Add: %v", err))
		}
	}
}

// CreateUpdateNodeHandler creates a node update handler. (Common to lifecycle and ipam)
func CreateUpdateNodeHandler(f func(oldNode, newNode *v1.Node) error) func(oldObj, newObj interface{}) {
	return func(origOldObj, origNewObj interface{}) {
		node := origNewObj.(*v1.Node).DeepCopy()
		prevNode := origOldObj.(*v1.Node).DeepCopy()

		if err := f(prevNode, node); err != nil {
			utilruntime.HandleError(fmt.Errorf("Error while processing Node Add/Delete: %v", err))
		}
	}
}

// CreateDeleteNodeHandler creates a delete node handler. (Common to lifecycle and ipam)
func CreateDeleteNodeHandler(f func(node *v1.Node) error) func(obj interface{}) {
	return func(originalObj interface{}) {
		originalNode, isNode := originalObj.(*v1.Node)
		// We can get DeletedFinalStateUnknown instead of *v1.Node here and
		// we need to handle that correctly. #34692
		if !isNode {
			deletedState, ok := originalObj.(cache.DeletedFinalStateUnknown)
			if !ok {
				klog.Errorf("Received unexpected object: %v", originalObj)
				return
			}
			originalNode, ok = deletedState.Obj.(*v1.Node)
			if !ok {
				klog.Errorf("DeletedFinalStateUnknown contained non-Node object: %v", deletedState.Obj)
				return
			}
		}
		node := originalNode.DeepCopy()
		if err := f(node); err != nil {
			utilruntime.HandleError(fmt.Errorf("Error while processing Node Add/Delete: %v", err))
		}
	}
}

// GetNodeCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func GetNodeCondition(status *v1.NodeStatus, conditionType v1.NodeConditionType) (int, *v1.NodeCondition) {
	if status == nil {
		return -1, nil
	}
	for i := range status.Conditions {
		if status.Conditions[i].Type == conditionType {
			return i, &status.Conditions[i]
		}
	}
	return -1, nil
}
<commit_msg>Add apiVersion to involvedObject<commit_after>/*
Copyright 2016 The Kubernetes 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 node

import (
	"fmt"
	"strings"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"

	"k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	appsv1listers "k8s.io/client-go/listers/apps/v1"
	utilpod "k8s.io/kubernetes/pkg/api/v1/pod"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/kubelet/util/format"
	nodepkg "k8s.io/kubernetes/pkg/util/node"

	"k8s.io/klog"
)

// DeletePods will delete all pods from master running on given node,
// and return true if any pods were deleted, or were found pending
// deletion.
func DeletePods(kubeClient clientset.Interface, pods []*v1.Pod, recorder record.EventRecorder, nodeName, nodeUID string, daemonStore appsv1listers.DaemonSetLister) (bool, error) {
	remaining := false
	var updateErrList []error

	if len(pods) > 0 {
		RecordNodeEvent(recorder, nodeName, nodeUID, v1.EventTypeNormal, "DeletingAllPods", fmt.Sprintf("Deleting all Pods from Node %v.", nodeName))
	}

	for i := range pods {
		// Defensive check, also needed for tests.
		if pods[i].Spec.NodeName != nodeName {
			continue
		}

		// Pod will be modified, so making copy is required.
		pod := pods[i].DeepCopy()
		// Set reason and message in the pod object.
		if _, err := SetPodTerminationReason(kubeClient, pod, nodeName); err != nil {
			if apierrors.IsConflict(err) {
				updateErrList = append(updateErrList,
					fmt.Errorf("update status failed for pod %q: %v", format.Pod(pod), err))
				continue
			}
		}
		// if the pod has already been marked for deletion, we still return true that there are remaining pods.
		if pod.DeletionGracePeriodSeconds != nil {
			remaining = true
			continue
		}
		// if the pod is managed by a daemonset, ignore it
		if _, err := daemonStore.GetPodDaemonSets(pod); err == nil {
			// No error means at least one daemonset was found
			continue
		}

		klog.V(2).Infof("Starting deletion of pod %v/%v", pod.Namespace, pod.Name)
		recorder.Eventf(pod, v1.EventTypeNormal, "NodeControllerEviction", "Marking for deletion Pod %s from Node %s", pod.Name, nodeName)
		if err := kubeClient.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil {
			if apierrors.IsNotFound(err) {
				// NotFound error means that pod was already deleted.
				// There is nothing left to do with this pod.
				continue
			}
			return false, err
		}
		remaining = true
	}

	if len(updateErrList) > 0 {
		return false, utilerrors.NewAggregate(updateErrList)
	}
	return remaining, nil
}

// SetPodTerminationReason attempts to set a reason and message in the
// pod status, updates it in the apiserver, and returns an error if it
// encounters one.
func SetPodTerminationReason(kubeClient clientset.Interface, pod *v1.Pod, nodeName string) (*v1.Pod, error) {
	if pod.Status.Reason == nodepkg.NodeUnreachablePodReason {
		return pod, nil
	}

	pod.Status.Reason = nodepkg.NodeUnreachablePodReason
	pod.Status.Message = fmt.Sprintf(nodepkg.NodeUnreachablePodMessage, nodeName, pod.Name)

	var updatedPod *v1.Pod
	var err error
	if updatedPod, err = kubeClient.CoreV1().Pods(pod.Namespace).UpdateStatus(pod); err != nil {
		return nil, err
	}
	return updatedPod, nil
}

// MarkPodsNotReady updates ready status of given pods running on
// given node from master return true if success
func MarkPodsNotReady(kubeClient clientset.Interface, pods []*v1.Pod, nodeName string) error {
	klog.V(2).Infof("Update ready status of pods on node [%v]", nodeName)

	errMsg := []string{}
	for i := range pods {
		// Defensive check, also needed for tests.
		if pods[i].Spec.NodeName != nodeName {
			continue
		}

		// Pod will be modified, so making copy is required.
		pod := pods[i].DeepCopy()
		for _, cond := range pod.Status.Conditions {
			if cond.Type == v1.PodReady {
				cond.Status = v1.ConditionFalse
				if !utilpod.UpdatePodCondition(&pod.Status, &cond) {
					break
				}
				klog.V(2).Infof("Updating ready status of pod %v to false", pod.Name)
				_, err := kubeClient.CoreV1().Pods(pod.Namespace).UpdateStatus(pod)
				if err != nil {
					if apierrors.IsNotFound(err) {
						// NotFound error means that pod was already deleted.
						// There is nothing left to do with this pod.
						continue
					}
					klog.Warningf("Failed to update status for pod %q: %v", format.Pod(pod), err)
					errMsg = append(errMsg, fmt.Sprintf("%v", err))
				}
				break
			}
		}
	}
	if len(errMsg) == 0 {
		return nil
	}
	return fmt.Errorf("%v", strings.Join(errMsg, "; "))
}

// RecordNodeEvent records a event related to a node.
func RecordNodeEvent(recorder record.EventRecorder, nodeName, nodeUID, eventtype, reason, event string) {
	ref := &v1.ObjectReference{
		APIVersion: "v1",
		Kind:       "Node",
		Name:       nodeName,
		UID:        types.UID(nodeUID),
		Namespace:  "",
	}
	klog.V(2).Infof("Recording %s event message for node %s", event, nodeName)
	recorder.Eventf(ref, eventtype, reason, "Node %s event: %s", nodeName, event)
}

// RecordNodeStatusChange records a event related to a node status change. (Common to lifecycle and ipam)
func RecordNodeStatusChange(recorder record.EventRecorder, node *v1.Node, newStatus string) {
	ref := &v1.ObjectReference{
		APIVersion: "v1",
		Kind:       "Node",
		Name:       node.Name,
		UID:        node.UID,
		Namespace:  "",
	}
	klog.V(2).Infof("Recording status change %s event message for node %s", newStatus, node.Name)
	// TODO: This requires a transaction, either both node status is updated
	// and event is recorded or neither should happen, see issue #6055.
	recorder.Eventf(ref, v1.EventTypeNormal, newStatus, "Node %s status is now: %s", node.Name, newStatus)
}

// SwapNodeControllerTaint returns true in case of success and false
// otherwise.
func SwapNodeControllerTaint(kubeClient clientset.Interface, taintsToAdd, taintsToRemove []*v1.Taint, node *v1.Node) bool {
	for _, taintToAdd := range taintsToAdd {
		now := metav1.Now()
		taintToAdd.TimeAdded = &now
	}

	err := controller.AddOrUpdateTaintOnNode(kubeClient, node.Name, taintsToAdd...)
	if err != nil {
		utilruntime.HandleError(
			fmt.Errorf(
				"unable to taint %+v unresponsive Node %q: %v",
				taintsToAdd,
				node.Name,
				err))
		return false
	}
	klog.V(4).Infof("Added %+v Taint to Node %v", taintsToAdd, node.Name)

	err = controller.RemoveTaintOffNode(kubeClient, node.Name, node, taintsToRemove...)
	if err != nil {
		utilruntime.HandleError(
			fmt.Errorf(
				"unable to remove %+v unneeded taint from unresponsive Node %q: %v",
				taintsToRemove,
				node.Name,
				err))
		return false
	}
	klog.V(4).Infof("Made sure that Node %+v has no %v Taint", node.Name, taintsToRemove)

	return true
}

// AddOrUpdateLabelsOnNode updates the labels on the node and returns true on
// success and false on failure.
func AddOrUpdateLabelsOnNode(kubeClient clientset.Interface, labelsToUpdate map[string]string, node *v1.Node) bool {
	err := controller.AddOrUpdateLabelsOnNode(kubeClient, node.Name, labelsToUpdate)
	if err != nil {
		utilruntime.HandleError(
			fmt.Errorf(
				"unable to update labels %+v for Node %q: %v",
				labelsToUpdate,
				node.Name,
				err))
		return false
	}
	klog.V(4).Infof("Updated labels %+v to Node %v", labelsToUpdate, node.Name)
	return true
}

// CreateAddNodeHandler creates an add node handler.
func CreateAddNodeHandler(f func(node *v1.Node) error) func(obj interface{}) {
	return func(originalObj interface{}) {
		node := originalObj.(*v1.Node).DeepCopy()
		if err := f(node); err != nil {
			utilruntime.HandleError(fmt.Errorf("Error while processing Node Add: %v", err))
		}
	}
}

// CreateUpdateNodeHandler creates a node update handler. (Common to lifecycle and ipam)
func CreateUpdateNodeHandler(f func(oldNode, newNode *v1.Node) error) func(oldObj, newObj interface{}) {
	return func(origOldObj, origNewObj interface{}) {
		node := origNewObj.(*v1.Node).DeepCopy()
		prevNode := origOldObj.(*v1.Node).DeepCopy()

		if err := f(prevNode, node); err != nil {
			utilruntime.HandleError(fmt.Errorf("Error while processing Node Add/Delete: %v", err))
		}
	}
}

// CreateDeleteNodeHandler creates a delete node handler. (Common to lifecycle and ipam)
func CreateDeleteNodeHandler(f func(node *v1.Node) error) func(obj interface{}) {
	return func(originalObj interface{}) {
		originalNode, isNode := originalObj.(*v1.Node)
		// We can get DeletedFinalStateUnknown instead of *v1.Node here and
		// we need to handle that correctly. #34692
		if !isNode {
			deletedState, ok := originalObj.(cache.DeletedFinalStateUnknown)
			if !ok {
				klog.Errorf("Received unexpected object: %v", originalObj)
				return
			}
			originalNode, ok = deletedState.Obj.(*v1.Node)
			if !ok {
				klog.Errorf("DeletedFinalStateUnknown contained non-Node object: %v", deletedState.Obj)
				return
			}
		}
		node := originalNode.DeepCopy()
		if err := f(node); err != nil {
			utilruntime.HandleError(fmt.Errorf("Error while processing Node Add/Delete: %v", err))
		}
	}
}

// GetNodeCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func GetNodeCondition(status *v1.NodeStatus, conditionType v1.NodeConditionType) (int, *v1.NodeCondition) {
	if status == nil {
		return -1, nil
	}
	for i := range status.Conditions {
		if status.Conditions[i].Type == conditionType {
			return i, &status.Conditions[i]
		}
	}
	return -1, nil
}
<|endoftext|>


<commit_before>package appdatabase

import (
	"database/sql"

	"github.com/status-im/status-go/appdatabase/migrations"
	migrationsprevnodecfg "github.com/status-im/status-go/appdatabase/migrationsprevnodecfg"
	"github.com/status-im/status-go/nodecfg"
	"github.com/status-im/status-go/sqlite"
)

// InitializeDB creates db file at a given path and applies migrations.
func InitializeDB(path, password string) (*sql.DB, error) {
	db, err := sqlite.OpenDB(path, password)
	if err != nil {
		return nil, err
	}

	err = migrationsprevnodecfg.Migrate(db)
	if err != nil {
		return nil, err
	}

	// NodeConfig migration cannot be done with SQL
	err = nodecfg.MigrateNodeConfig(db)
	if err != nil {
		return nil, err
	}

	err = migrations.Migrate(db)
	if err != nil {
		return nil, err
	}
	return db, nil
}

// DecryptDatabase creates an unencrypted copy of the database and copies it
// over to the given directory
func DecryptDatabase(oldPath, newPath, password string) error {
	return sqlite.DecryptDB(oldPath, newPath, password)
}

// EncryptDatabase creates an encrypted copy of the database and copies it to the
// user path
func EncryptDatabase(oldPath, newPath, password string) error {
	return sqlite.EncryptDB(oldPath, newPath, password)
}

func ChangeDatabasePassword(path, password, newPassword string) error {
	return sqlite.ChangeEncryptionKey(path, password, newPassword)
}
<commit_msg>fix: file not found error when logging in due to not finding migrations files (#2504)<commit_after>package appdatabase

import (
	"database/sql"

	"github.com/status-im/status-go/appdatabase/migrations"
	migrationsprevnodecfg "github.com/status-im/status-go/appdatabase/migrationsprevnodecfg"
	"github.com/status-im/status-go/nodecfg"
	"github.com/status-im/status-go/sqlite"
)

const nodeCfgMigrationDate = 1640111208

// InitializeDB creates db file at a given path and applies migrations.
func InitializeDB(path, password string) (*sql.DB, error) {
	db, err := sqlite.OpenDB(path, password)
	if err != nil {
		return nil, err
	}

	// Check if the migration table exists
	row := db.QueryRow("SELECT exists(SELECT name FROM sqlite_master WHERE type='table' AND name='status_go_schema_migrations')")
	migrationTableExists := false
	err = row.Scan(&migrationTableExists)
	if err != nil && err != sql.ErrNoRows {
		return nil, err
	}

	var lastMigration uint64 = 0
	if migrationTableExists {
		row = db.QueryRow("SELECT version FROM status_go_schema_migrations")
		err = row.Scan(&lastMigration)
		if err != nil && err != sql.ErrNoRows {
			return nil, err
		}
	}

	if !migrationTableExists || (lastMigration > 0 && lastMigration < nodeCfgMigrationDate) {
		// If it's the first time migration's being run, or latest migration happened before migrating the nodecfg table
		err = migrationsprevnodecfg.Migrate(db)
		if err != nil {
			return nil, err
		}

		// NodeConfig migration cannot be done with SQL
		err = nodecfg.MigrateNodeConfig(db)
		if err != nil {
			return nil, err
		}
	}

	err = migrations.Migrate(db)
	if err != nil {
		return nil, err
	}
	return db, nil
}

// DecryptDatabase creates an unencrypted copy of the database and copies it
// over to the given directory
func DecryptDatabase(oldPath, newPath, password string) error {
	return sqlite.DecryptDB(oldPath, newPath, password)
}

// EncryptDatabase creates an encrypted copy of the database and copies it to the
// user path
func EncryptDatabase(oldPath, newPath, password string) error {
	return sqlite.EncryptDB(oldPath, newPath, password)
}

func ChangeDatabasePassword(path, password, newPassword string) error {
	return sqlite.ChangeEncryptionKey(path, password, newPassword)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"time"
)

type Job struct {
	ID          int               `json:"id"`
	Script      string            `json:"script"`
	Args        []string          `json:"args,omitempty"`
	Files       map[string]string `json:"files,omitempty"`
	CallbackURL string            `json:"callback_url,omitempty"`
	WorkingDir  string            `json:"-"`
	ScriptDir   string            `json:"-"`
	StoreDir    string            `json:"-"`
	Output      string            `json:"output"`
	ExecLog     string            `json:"exec_log"`
	Status      string            `json:"status"`
	StartTime   time.Time         `json:"start_time"`
	FinishTime  time.Time         `json:"end_time"`
	Duration    string            `json:"duration"`
}

func (j *Job) CleanArgs() ([]string, error) {
	// TODO find a way to clean the arguments
	return j.Args, nil
}

func (j *Job) SaveFiles(dir string) error {
	var err error
	var file string
	for name, data := range j.Files {

		// Clean bad input
		name = strings.Replace(name, "..", "", -1)
		name = strings.Replace(name, "/", "", -1)

		file = path.Join(dir, name)
		log.Printf("Writing %s to disk\n", file)
		err = ioutil.WriteFile(file, []byte(data), 0644)
		if err != nil {
			return err
		}
	}
	return nil
}

func (j *Job) Log() error {
	conn := redisDB.Get()
	defer conn.Close()

	data, err := json.Marshal(j)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Printf("Adding job %d to log for %s to Redis\n", j.ID, j.Script)
	_, err = conn.Do("ZADD", j.Script, j.ID, data)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Printf("Trimming log for %s to the %d most recent\n", j.Script, LOGLIMIT)
	_, err = conn.Do("ZREMRANGEBYRANK", j.Script, 0, -LOGLIMIT-1)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func (j *Job) Callback() error {
	data, err := json.Marshal(j)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Printf("Sending status back to %s\n", j.CallbackURL)
	buf := bytes.NewBuffer(data)
	_, err = http.Post(j.CallbackURL, "application/json", buf)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func (j *Job) Execute(ch chan error) {
	j.StartTime = time.Now()

	// Intialize script path and arguments
	s := path.Join(j.ScriptDir, j.Script)
	args, err := j.CleanArgs()
	if err != nil {
		j.ExecLog = fmt.Sprintf("%s", err)
		ch <- err
		return
	}

	// Set environment variables
	os.Setenv("QMD_STORE", path.Clean(j.StoreDir))

	tmpPath := path.Join(j.WorkingDir, "tmp", strconv.Itoa(j.ID))
	os.MkdirAll(tmpPath, 0777)
	os.Setenv("QMD_TMP", tmpPath)
	defer j.RemoveTmpdir(tmpPath)

	err = j.SaveFiles(tmpPath)
	if err != nil {
		j.ExecLog = fmt.Sprintf("%s", err)
		ch <- err
		return
	}

	outPath := path.Join(tmpPath, "qmd.out")
	os.Setenv("QMD_OUT", outPath)

	// Setup and execute cmd
	cmd := exec.Command(s, args...)
	cmd.Dir = path.Clean(j.WorkingDir)

	log.Printf("Executing command: %s\n", s)
	out, err := cmd.Output()
	j.FinishTime = time.Now()
	j.Duration = j.FinishTime.Sub(j.StartTime).String()

	if err != nil {
		j.ExecLog = fmt.Sprintf("%s", err)
		ch <- err
		return
	}

	data, err := ioutil.ReadFile(outPath)
	if os.IsNotExist(err) {
		j.Output = string(data)
		err = nil
	}

	j.ExecLog = string(out)

	ch <- err
	return
}

func (j *Job) RemoveTmpdir(tmpPath string) {
	if !config.KeepTemp {
		log.Println("Deleting all files and dirs in", tmpPath)
		err := os.RemoveAll(tmpPath)
		if err != nil {
			log.Println("Failed to delete all files and dirs in", tmpPath)
			log.Println(err)
		}
	}
}
<commit_msg>WorkingDir is tmp.. so we can trim that<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"time"
)

type Job struct {
	ID          int               `json:"id"`
	Script      string            `json:"script"`
	Args        []string          `json:"args,omitempty"`
	Files       map[string]string `json:"files,omitempty"`
	CallbackURL string            `json:"callback_url,omitempty"`
	WorkingDir  string            `json:"-"`
	ScriptDir   string            `json:"-"`
	StoreDir    string            `json:"-"`
	Output      string            `json:"output"`
	ExecLog     string            `json:"exec_log"`
	Status      string            `json:"status"`
	StartTime   time.Time         `json:"start_time"`
	FinishTime  time.Time         `json:"end_time"`
	Duration    string            `json:"duration"`
}

func (j *Job) CleanArgs() ([]string, error) {
	// TODO find a way to clean the arguments
	return j.Args, nil
}

func (j *Job) SaveFiles(dir string) error {
	var err error
	var file string
	for name, data := range j.Files {

		// Clean bad input
		name = strings.Replace(name, "..", "", -1)
		name = strings.Replace(name, "/", "", -1)

		file = path.Join(dir, name)
		log.Printf("Writing %s to disk\n", file)
		err = ioutil.WriteFile(file, []byte(data), 0644)
		if err != nil {
			return err
		}
	}
	return nil
}

func (j *Job) Log() error {
	conn := redisDB.Get()
	defer conn.Close()

	data, err := json.Marshal(j)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Printf("Adding job %d to log for %s to Redis\n", j.ID, j.Script)
	_, err = conn.Do("ZADD", j.Script, j.ID, data)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Printf("Trimming log for %s to the %d most recent\n", j.Script, LOGLIMIT)
	_, err = conn.Do("ZREMRANGEBYRANK", j.Script, 0, -LOGLIMIT-1)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func (j *Job) Callback() error {
	data, err := json.Marshal(j)
	if err != nil {
		log.Println(err)
		return err
	}

	log.Printf("Sending status back to %s\n", j.CallbackURL)
	buf := bytes.NewBuffer(data)
	_, err = http.Post(j.CallbackURL, "application/json", buf)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func (j *Job) Execute(ch chan error) {
	j.StartTime = time.Now()

	// Intialize script path and arguments
	s := path.Join(j.ScriptDir, j.Script)
	args, err := j.CleanArgs()
	if err != nil {
		j.ExecLog = fmt.Sprintf("%s", err)
		ch <- err
		return
	}

	// Set environment variables
	os.Setenv("QMD_STORE", path.Clean(j.StoreDir))

	tmpPath := path.Join(j.WorkingDir, strconv.Itoa(j.ID))
	os.MkdirAll(tmpPath, 0777)
	os.Setenv("QMD_TMP", tmpPath)
	defer j.RemoveTmpdir(tmpPath)

	err = j.SaveFiles(tmpPath)
	if err != nil {
		j.ExecLog = fmt.Sprintf("%s", err)
		ch <- err
		return
	}

	outPath := path.Join(tmpPath, "qmd.out")
	os.Setenv("QMD_OUT", outPath)

	// Setup and execute cmd
	cmd := exec.Command(s, args...)
	cmd.Dir = path.Clean(j.WorkingDir)

	log.Printf("Executing command: %s\n", s)
	out, err := cmd.Output()
	j.FinishTime = time.Now()
	j.Duration = j.FinishTime.Sub(j.StartTime).String()

	if err != nil {
		j.ExecLog = fmt.Sprintf("%s", err)
		ch <- err
		return
	}

	data, err := ioutil.ReadFile(outPath)
	if os.IsNotExist(err) {
		j.Output = string(data)
		err = nil
	}

	j.ExecLog = string(out)

	ch <- err
	return
}

func (j *Job) RemoveTmpdir(tmpPath string) {
	if !config.KeepTemp {
		log.Println("Deleting all files and dirs in", tmpPath)
		err := os.RemoveAll(tmpPath)
		if err != nil {
			log.Println("Failed to delete all files and dirs in", tmpPath)
			log.Println(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"sort"
	"strings"

	"github.com/calmh/zsnapper/zfs"
	"gopkg.in/yaml.v2"
)

type Job struct {
	Family    string   // "test"
	Datasets  []string // "data/foto"
	Schedule  string   // "0 */5 * * * *"
	Keep      int      // 12
	Recursive bool
}

func LoadJobs(r io.Reader) ([]Job, error) {
	bs, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var configs []Job
	if err := yaml.Unmarshal(bs, &configs); err != nil {
		return nil, err
	}

	return configs, nil
}

func (j Job) Run() {
	name := j.Family + "-" + timestamp()

	expandedSets := expandLines(j.Datasets)
	all := datasets()
	filteredDatasets := selectLines(expandedSets, all)

	for _, dataset := range filteredDatasets {
		if err := j.snapshot(dataset, name); err != nil {
			fmt.Println("Create snapshot:", err)
		}
		if err := j.clean(dataset); err != nil {
			fmt.Println("Clean snapshots:", err)
		}
	}
}

func (j Job) String() string {
	return fmt.Sprintf(`%v@%s, at "%s" (keep %d, recursive %v)`, j.Datasets, j.Family, j.Schedule, j.Keep, j.Recursive)
}

func (j Job) snapshot(dataset, name string) error {
	mutexFor(dataset).Lock()
	defer mutexFor(dataset).Unlock()

	ds, err := zfs.GetDataset(dataset)
	if err != nil {
		return err
	}

	ss, err := ds.Snapshot(name, j.Recursive)
	if err != nil {
		return err
	}

	if verbose {
		fmt.Println("Created", ss.Name)
	}

	return nil
}

func (j Job) clean(dataset string) error {
	mutexFor(dataset).Lock()
	defer mutexFor(dataset).Unlock()

	ds, err := zfs.GetDataset(dataset)
	if err != nil {
		return err
	}

	snaps, err := ds.Snapshots()
	if err != nil {
		return err
	}

	prefix := dataset + "@" + j.Family + "-"
	var matching []*zfs.Dataset
	for _, snap := range snaps {
		if strings.HasPrefix(snap.Name, prefix) {
			matching = append(matching, snap)
		}
	}

	sort.Sort(datasetList(matching))

	if len(matching) <= j.Keep {
		return nil
	}

	var flags zfs.DestroyFlag
	if j.Recursive {
		flags |= zfs.DestroyRecursive
	}
	for _, snap := range matching[:len(matching)-j.Keep] {
		if err := snap.Destroy(flags); err != nil {
			return err
		}
		if verbose {
			fmt.Println("Destroyed", snap.Name)
		}
	}

	return nil
}
<commit_msg>Try harder to clean up old snapshots<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"sort"
	"strings"

	"github.com/calmh/zsnapper/zfs"
	"gopkg.in/yaml.v2"
)

type Job struct {
	Family    string   // "test"
	Datasets  []string // "data/foto"
	Schedule  string   // "0 */5 * * * *"
	Keep      int      // 12
	Recursive bool
}

func LoadJobs(r io.Reader) ([]Job, error) {
	bs, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var configs []Job
	if err := yaml.Unmarshal(bs, &configs); err != nil {
		return nil, err
	}

	return configs, nil
}

func (j Job) Run() {
	name := j.Family + "-" + timestamp()

	expandedSets := expandLines(j.Datasets)
	all := datasets()
	filteredDatasets := selectLines(expandedSets, all)

	for _, dataset := range filteredDatasets {
		if err := j.snapshot(dataset, name); err != nil {
			fmt.Println("Create snapshot:", err)
		}
		if err := j.clean(dataset); err != nil {
			fmt.Println("Clean snapshots:", err)
		}
	}
}

func (j Job) String() string {
	return fmt.Sprintf(`%v@%s, at "%s" (keep %d, recursive %v)`, j.Datasets, j.Family, j.Schedule, j.Keep, j.Recursive)
}

func (j Job) snapshot(dataset, name string) error {
	mutexFor(dataset).Lock()
	defer mutexFor(dataset).Unlock()

	ds, err := zfs.GetDataset(dataset)
	if err != nil {
		return err
	}

	ss, err := ds.Snapshot(name, j.Recursive)
	if err != nil {
		return err
	}

	if verbose {
		fmt.Println("Created", ss.Name)
	}

	return nil
}

func (j Job) clean(dataset string) error {
	mutexFor(dataset).Lock()
	defer mutexFor(dataset).Unlock()

	ds, err := zfs.GetDataset(dataset)
	if err != nil {
		return err
	}

	snaps, err := ds.Snapshots()
	if err != nil {
		return err
	}

	prefix := dataset + "@" + j.Family + "-"
	var matching []*zfs.Dataset
	for _, snap := range snaps {
		if strings.HasPrefix(snap.Name, prefix) {
			matching = append(matching, snap)
		}
	}

	sort.Sort(datasetList(matching))

	if len(matching) <= j.Keep {
		return nil
	}

	var flags zfs.DestroyFlag
	if j.Recursive {
		flags |= zfs.DestroyRecursive
	}
	for _, snap := range matching[:len(matching)-j.Keep] {
		if err := snap.Destroy(flags); err != nil {
			fmt.Printf("Destroy %s: %v\n", snap.Name, err)
		} else if verbose {
			fmt.Println("Destroyed", snap.Name)
		}
	}

	return nil
}
<|endoftext|>


<commit_before><commit_msg>some work on config stuff<commit_after><|endoftext|>


<commit_before>/****************************************************************************
 * This file is part of Builder.
 *
 * Copyright (C) 2015 Pier Luigi Fiorini
 *
 * Author(s):
 *    Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 *
 * $BEGIN_LICENSE:AGPL3+$
 *
 * 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 <http://www.gnu.org/licenses/>.
 *
 * $END_LICENSE$
 ***************************************************************************/

package builder

import (
	"sync"
	"time"
)

// Represents a job.
type Job struct {
	// Identifier.
	Id uint64 `json:"id"`
	// Type.
	Type JobTargetType `json:"type"`
	// Target name.
	Target string `json:"target"`
	// Architecture.
	Architecture string `json:"arch"`
	// When the job has started.
	Started time.Time `json:"started"`
	// When the job has finished.
	Finished time.Time `json:"finished"`
	// Status.
	Status JobStatus `json:"status"`
	// Build steps.
	Steps []*Step `json:"steps"`
	// Mutex that serialize access to this job.
	Mutex sync.Mutex `json:"-"`
}

// Step represents the step of a job.
type Step struct {
	// Name.
	Name string `json:"name"`
	// When the step was started.
	Started time.Time `json:"started"`
	// When the step has finished.
	Finished time.Time `json:"finished"`
	// Summary.
	Summary map[string][]string `json:"summary"`
	// Output.
	Log string `json:"log"`
	// Additional logs.
	Logs map[string][]byte `json:"logs,omitempty"`
}

// Job target type enumeration.
type JobTargetType uint32

const (
	JOB_TARGET_TYPE_PACKAGE = iota
	JOB_TARGET_TYPE_IMAGE
)

// Job status enumeration.
type JobStatus uint32

const (
	JOB_STATUS_JUST_CREATED = iota
	JOB_STATUS_WAITING
	JOB_STATUS_PROCESSING
	JOB_STATUS_SUCCESSFUL
	JOB_STATUS_FAILED
	JOB_STATUS_CRASHED
)

// Map job status to description.
var JobStatusDescriptionMap = map[JobStatus]string{
	JOB_STATUS_JUST_CREATED: "JustCreated",
	JOB_STATUS_WAITING:      "Waiting",
	JOB_STATUS_PROCESSING:   "Processing",
	JOB_STATUS_SUCCESSFUL:   "Successful",
	JOB_STATUS_FAILED:       "Failed",
	JOB_STATUS_CRASHED:      "Crashed",
}
<commit_msg>Omit empty build step summary when marshaling<commit_after>/****************************************************************************
 * This file is part of Builder.
 *
 * Copyright (C) 2015 Pier Luigi Fiorini
 *
 * Author(s):
 *    Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 *
 * $BEGIN_LICENSE:AGPL3+$
 *
 * 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 <http://www.gnu.org/licenses/>.
 *
 * $END_LICENSE$
 ***************************************************************************/

package builder

import (
	"sync"
	"time"
)

// Represents a job.
type Job struct {
	// Identifier.
	Id uint64 `json:"id"`
	// Type.
	Type JobTargetType `json:"type"`
	// Target name.
	Target string `json:"target"`
	// Architecture.
	Architecture string `json:"arch"`
	// When the job has started.
	Started time.Time `json:"started"`
	// When the job has finished.
	Finished time.Time `json:"finished"`
	// Status.
	Status JobStatus `json:"status"`
	// Build steps.
	Steps []*Step `json:"steps"`
	// Mutex that serialize access to this job.
	Mutex sync.Mutex `json:"-"`
}

// Step represents the step of a job.
type Step struct {
	// Name.
	Name string `json:"name"`
	// When the step was started.
	Started time.Time `json:"started"`
	// When the step has finished.
	Finished time.Time `json:"finished"`
	// Summary.
	Summary map[string][]string `json:"summary,omitempty"`
	// Output.
	Log string `json:"log"`
	// Additional logs.
	Logs map[string][]byte `json:"logs,omitempty"`
}

// Job target type enumeration.
type JobTargetType uint32

const (
	JOB_TARGET_TYPE_PACKAGE = iota
	JOB_TARGET_TYPE_IMAGE
)

// Job status enumeration.
type JobStatus uint32

const (
	JOB_STATUS_JUST_CREATED = iota
	JOB_STATUS_WAITING
	JOB_STATUS_PROCESSING
	JOB_STATUS_SUCCESSFUL
	JOB_STATUS_FAILED
	JOB_STATUS_CRASHED
)

// Map job status to description.
var JobStatusDescriptionMap = map[JobStatus]string{
	JOB_STATUS_JUST_CREATED: "JustCreated",
	JOB_STATUS_WAITING:      "Waiting",
	JOB_STATUS_PROCESSING:   "Processing",
	JOB_STATUS_SUCCESSFUL:   "Successful",
	JOB_STATUS_FAILED:       "Failed",
	JOB_STATUS_CRASHED:      "Crashed",
}
<|endoftext|>


<commit_before>package rdbtools

import (
	"fmt"
	"time"
)

type KeyObject struct {
	ExpiryTime time.Time
	Key        interface{}
}

func NewKeyObject(key interface{}, expiryTime int64) KeyObject {
	k := KeyObject{
		Key: key,
	}
	if expiryTime >= 0 {
		k.ExpiryTime = time.Unix(expiryTime/1000, 0)
	}

	return k
}

func (k KeyObject) Expired() bool {
	return k.ExpiryTime.Before(time.Now())
}

func (k KeyObject) String() string {
	if !k.ExpiryTime.IsZero() {
		return fmt.Sprintf("KeyObject{ExpiryTime: %s, Key: %s}", k.ExpiryTime.UTC(), DataToString(k.Key))
	}

	return fmt.Sprintf("%s", DataToString(k.Key))
}
<commit_msg>Store the expiry time in UTC directly<commit_after>package rdbtools

import (
	"fmt"
	"time"
)

type KeyObject struct {
	ExpiryTime time.Time
	Key        interface{}
}

func NewKeyObject(key interface{}, expiryTime int64) KeyObject {
	k := KeyObject{
		Key: key,
	}
	if expiryTime >= 0 {
		k.ExpiryTime = time.Unix(expiryTime/1000, 0).UTC()
	}

	return k
}

func (k KeyObject) Expired() bool {
	return k.ExpiryTime.Before(time.Now())
}

func (k KeyObject) String() string {
	if !k.ExpiryTime.IsZero() {
		return fmt.Sprintf("KeyObject{ExpiryTime: %s, Key: %s}", k.ExpiryTime, DataToString(k.Key))
	}

	return fmt.Sprintf("%s", DataToString(k.Key))
}
<|endoftext|>


<commit_before>//go:generate go run ./internal/generate -f -o kml22gx.gen.go -n gx: xsd/kml22gx.xsd
//go:generate go run ./internal/generate -f -o ogckml22.gen.go xsd/ogckml22.xsd

// Package kml provides convenience methods for creating and writing KML documents.
//
// See https://developers.google.com/kml/
//
// Goals
//
//   - Convenient API for creating both simple and complex KML documents.
//   - 1:1 mapping between functions and KML elements.
//
// Non-goals
//
//   - Protection against generating invalid documents.
//   - Concealment of KML complexity.
//   - Fine-grained control over generated XML.
package kml

import (
	"encoding/xml"
	"fmt"
	"image/color"
	"io"
	"strconv"
	"time"
)

// An Element represents an abstract KML element.
type Element interface {
	xml.Marshaler
	Write(io.Writer) error
	WriteIndent(io.Writer, string, string) error
}

// A SimpleElement is an Element with a single value.
type SimpleElement struct {
	xml.StartElement
	Value string
}

// A CompoundElement is an Element with children.
type CompoundElement struct {
	xml.StartElement
	Children []Element
}

// A SharedElement is an element with an id.
type SharedElement struct {
	CompoundElement
	Id string
}

// MarshalXML marshals se to e. start is ignored.
func (se *SimpleElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	return e.EncodeElement(xml.CharData(se.Value), se.StartElement)
}

// SetBool sets se's value from a bool.
func (se *SimpleElement) SetBool(value bool) {
	se.Value = formatBool(value)
}

// SetColor sets se's value from a color.Color.
func (se *SimpleElement) SetColor(value color.Color) {
	se.Value = formatColor(value)
}

// SetFloat set se's value from a float64.
func (se *SimpleElement) SetFloat(value float64) {
	se.Value = formatFloat(value)
}

// SetInt sets se's value from an int.
func (se *SimpleElement) SetInt(value int) {
	se.Value = formatInt(value)
}

// SetString sets se's value from a string.
func (se *SimpleElement) SetString(value string) {
	se.Value = value
}

// SetTime sets se'ss value from a time.Time.
func (se *SimpleElement) SetTime(value time.Time) {
	se.Value = formatTime(value)
}

// Write writes an XML header and se to w.
func (se *SimpleElement) Write(w io.Writer) error {
	return write(w, "", "", se)
}

// WriteIndent writes an XML header and se to w.
func (se *SimpleElement) WriteIndent(w io.Writer, prefix, indent string) error {
	return write(w, prefix, indent, se)
}

// Add adds children to ce.
func (ce *CompoundElement) Add(children ...Element) *CompoundElement {
	ce.Children = append(ce.Children, children...)
	return ce
}

// SetChildren sets ce's children.
func (ce *CompoundElement) SetChildren(children []Element) {
	ce.Children = children
}

// MarshalXML marshals ce to e. start is ignored.
func (ce *CompoundElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if err := e.EncodeToken(ce.StartElement); err != nil {
		return err
	}
	for _, c := range ce.Children {
		if err := e.EncodeElement(c, ce.StartElement); err != nil {
			return err
		}
	}
	return e.EncodeToken(ce.End())
}

// Write writes an XML header and ce to w.
func (ce *CompoundElement) Write(w io.Writer) error {
	return write(w, "", "", ce)
}

// WriteIndent writes an XML header and ce to w.
func (ce *CompoundElement) WriteIndent(w io.Writer, prefix, indent string) error {
	return write(w, prefix, indent, ce)
}

// ID returns se's id.
func (se *SharedElement) ID() string {
	return se.Id
}

// SetID sets se's id.
func (se *SharedElement) SetID(id string) {
	se.Id = id
}

// URL returns se's URL.
func (se *SharedElement) URL() string {
	return "#" + se.ID()
}

func formatBool(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func formatColor(value color.Color) string {
	r, g, b, a := value.RGBA()
	return fmt.Sprintf("%02x%02x%02x%02x", a/256, b/256, g/256, r/256)
}

func formatInt(value int) string {
	return strconv.Itoa(value)
}

func formatFloat(value float64) string {
	return strconv.FormatFloat(value, 'f', -1, 64)
}

func formatTime(value time.Time) string {
	return value.Format(time.RFC3339)
}

func write(w io.Writer, prefix, indent string, m xml.Marshaler) error {
	if _, err := w.Write([]byte(xml.Header)); err != nil {
		return err
	}
	e := xml.NewEncoder(w)
	e.Indent(prefix, indent)
	return e.Encode(m)
}

func newSEBool(name string, value bool) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatBool(value),
	}
}

func newSEColor(name string, value color.Color) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatColor(value),
	}
}

func newSEElement(name string, value Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Children:     []Element{value},
	}
}

func newSEFloat(name string, value float64) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatFloat(value),
	}
}

func newSEInt(name string, value int) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatInt(value),
	}
}

func newSEVec2(name string, value Vec2) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: name},
			Attr: []xml.Attr{
				{Name: xml.Name{Local: "x"}, Value: strconv.FormatFloat(value.X, 'f', -1, 64)},
				{Name: xml.Name{Local: "y"}, Value: strconv.FormatFloat(value.Y, 'f', -1, 64)},
				{Name: xml.Name{Local: "xunits"}, Value: string(value.XUnits)},
				{Name: xml.Name{Local: "yunits"}, Value: string(value.YUnits)},
			},
		},
	}
}

func newSEString(name, value string) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        value,
	}
}

func newSETime(name string, value time.Time) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatTime(value),
	}
}

func newCE(name string, children []Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Children:     children,
	}
}

func newSharedE(name, id string, children []Element) *SharedElement {
	var attr []xml.Attr
	if id != "" {
		attr = append(attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: id})
	}
	return &SharedElement{
		CompoundElement: CompoundElement{
			StartElement: xml.StartElement{
				Name: xml.Name{Local: name},
				Attr: attr,
			},
			Children: children,
		},
		Id: id,
	}
}
<commit_msg>Remove redundant methods<commit_after>//go:generate go run ./internal/generate -f -o kml22gx.gen.go -n gx: xsd/kml22gx.xsd
//go:generate go run ./internal/generate -f -o ogckml22.gen.go xsd/ogckml22.xsd

// Package kml provides convenience methods for creating and writing KML documents.
//
// See https://developers.google.com/kml/
//
// Goals
//
//   - Convenient API for creating both simple and complex KML documents.
//   - 1:1 mapping between functions and KML elements.
//
// Non-goals
//
//   - Protection against generating invalid documents.
//   - Concealment of KML complexity.
//   - Fine-grained control over generated XML.
package kml

import (
	"encoding/xml"
	"fmt"
	"image/color"
	"io"
	"strconv"
	"time"
)

// An Element represents an abstract KML element.
type Element interface {
	xml.Marshaler
	Write(io.Writer) error
	WriteIndent(io.Writer, string, string) error
}

// A SimpleElement is an Element with a single value.
type SimpleElement struct {
	xml.StartElement
	Value string
}

// A CompoundElement is an Element with children.
type CompoundElement struct {
	xml.StartElement
	Children []Element
}

// A SharedElement is an element with an id.
type SharedElement struct {
	CompoundElement
	Id string
}

// MarshalXML marshals se to e. start is ignored.
func (se *SimpleElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	return e.EncodeElement(xml.CharData(se.Value), se.StartElement)
}

// SetBool sets se's value from a bool.
func (se *SimpleElement) SetBool(value bool) {
	se.Value = formatBool(value)
}

// SetColor sets se's value from a color.Color.
func (se *SimpleElement) SetColor(value color.Color) {
	se.Value = formatColor(value)
}

// SetFloat set se's value from a float64.
func (se *SimpleElement) SetFloat(value float64) {
	se.Value = formatFloat(value)
}

// SetInt sets se's value from an int.
func (se *SimpleElement) SetInt(value int) {
	se.Value = formatInt(value)
}

// SetString sets se's value from a string.
func (se *SimpleElement) SetString(value string) {
	se.Value = value
}

// SetTime sets se'ss value from a time.Time.
func (se *SimpleElement) SetTime(value time.Time) {
	se.Value = formatTime(value)
}

// Write writes an XML header and se to w.
func (se *SimpleElement) Write(w io.Writer) error {
	return write(w, "", "", se)
}

// WriteIndent writes an XML header and se to w.
func (se *SimpleElement) WriteIndent(w io.Writer, prefix, indent string) error {
	return write(w, prefix, indent, se)
}

// Add adds children to ce.
func (ce *CompoundElement) Add(children ...Element) *CompoundElement {
	ce.Children = append(ce.Children, children...)
	return ce
}

// SetChildren sets ce's children.
func (ce *CompoundElement) SetChildren(children []Element) {
	ce.Children = children
}

// MarshalXML marshals ce to e. start is ignored.
func (ce *CompoundElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if err := e.EncodeToken(ce.StartElement); err != nil {
		return err
	}
	for _, c := range ce.Children {
		if err := e.EncodeElement(c, ce.StartElement); err != nil {
			return err
		}
	}
	return e.EncodeToken(ce.End())
}

// Write writes an XML header and ce to w.
func (ce *CompoundElement) Write(w io.Writer) error {
	return write(w, "", "", ce)
}

// WriteIndent writes an XML header and ce to w.
func (ce *CompoundElement) WriteIndent(w io.Writer, prefix, indent string) error {
	return write(w, prefix, indent, ce)
}

// URL returns se's URL.
func (se *SharedElement) URL() string {
	return "#" + se.Id
}

func formatBool(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func formatColor(value color.Color) string {
	r, g, b, a := value.RGBA()
	return fmt.Sprintf("%02x%02x%02x%02x", a/256, b/256, g/256, r/256)
}

func formatInt(value int) string {
	return strconv.Itoa(value)
}

func formatFloat(value float64) string {
	return strconv.FormatFloat(value, 'f', -1, 64)
}

func formatTime(value time.Time) string {
	return value.Format(time.RFC3339)
}

func write(w io.Writer, prefix, indent string, m xml.Marshaler) error {
	if _, err := w.Write([]byte(xml.Header)); err != nil {
		return err
	}
	e := xml.NewEncoder(w)
	e.Indent(prefix, indent)
	return e.Encode(m)
}

func newSEBool(name string, value bool) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatBool(value),
	}
}

func newSEColor(name string, value color.Color) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatColor(value),
	}
}

func newSEElement(name string, value Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Children:     []Element{value},
	}
}

func newSEFloat(name string, value float64) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatFloat(value),
	}
}

func newSEInt(name string, value int) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatInt(value),
	}
}

func newSEVec2(name string, value Vec2) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: name},
			Attr: []xml.Attr{
				{Name: xml.Name{Local: "x"}, Value: strconv.FormatFloat(value.X, 'f', -1, 64)},
				{Name: xml.Name{Local: "y"}, Value: strconv.FormatFloat(value.Y, 'f', -1, 64)},
				{Name: xml.Name{Local: "xunits"}, Value: string(value.XUnits)},
				{Name: xml.Name{Local: "yunits"}, Value: string(value.YUnits)},
			},
		},
	}
}

func newSEString(name, value string) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        value,
	}
}

func newSETime(name string, value time.Time) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Value:        formatTime(value),
	}
}

func newCE(name string, children []Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		Children:     children,
	}
}

func newSharedE(name, id string, children []Element) *SharedElement {
	var attr []xml.Attr
	if id != "" {
		attr = append(attr, xml.Attr{Name: xml.Name{Local: "id"}, Value: id})
	}
	return &SharedElement{
		CompoundElement: CompoundElement{
			StartElement: xml.StartElement{
				Name: xml.Name{Local: name},
				Attr: attr,
			},
			Children: children,
		},
		Id: id,
	}
}
<|endoftext|>


<commit_before>package kms

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

// KillingMeSoftly interface contains all functions needed for operation and method chaining
type KillingMeSoftly interface {
	Done()
}

type killingMeSoftly struct {
	wg *sync.WaitGroup
}

// Logger is the default instance of the log package
var (
	once         sync.Once
	killMeSoftly *killingMeSoftly
	notify       chan struct{}
	done         chan struct{}
)

func init() {
	once.Do(func() {
		killMeSoftly = &killingMeSoftly{
			wg: &sync.WaitGroup{},
		}

		notify = make(chan struct{})
		done = make(chan struct{})
	})
}

// ShutdownInitiated returns a notification channel for the package which will be
// closed/notified once a termination signal is recieved.
//
// usefull when other code, such as a custom TCP connection listener needs to be
// notified to stop listening for new connections.
func ShutdownInitiated() chan struct{} {
	return notify
}

// ShutdownComplete returns a notification channel for the package which will be
// closed/notified once termination is imminent.
func ShutdownComplete() chan struct{} {
	return done
}

// Wait signifies that your application is busy performing an operation.
//
// best to chain using defer kms.Wait().Done()
func Wait() KillingMeSoftly {
	killMeSoftly.wg.Add(1)
	return killMeSoftly
}

// Done signifies that your application is done performing an operation. it is different from
// the chained version as it does not need to be connected the the wait object.
func Done() {
	killMeSoftly.wg.Done()
}

// Done signifies that your application is done performing an operation.
//
// best to chain using defer kms.Wait().Done()
func (k *killingMeSoftly) Done() {
	k.wg.Done()
}

// Listen sets up signals to listen for interupt or kill signals
// in an attempt to wait for all operations to complete before letting
// the process die.
func Listen(block bool) {

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		sig := <-s

		close(notify)

		// listen for another signal, if another happens.. force shutdown
		go func() {
			sig := <-s
			fmt.Printf("recieved additional %s, hard shutdown initiated\n", sig)
			os.Exit(1)
		}()

		log.Printf("signal %s recieved, attempting soft shutdown...\n", sig)
		killMeSoftly.wg.Wait()
		log.Println("soft shutdown complete, ending process")
		close(done)
	}()

	if block {
		<-done
	}
}

// ListenTimeout sets up signals to listen for interupt or kill signals
// in an attempt to wait for all operations to complete before letting
// the process die.
//
// the wait duration is how long to wait before forcefully shutting everything down.
func ListenTimeout(block bool, wait time.Duration) {

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		sig := <-s

		close(notify)

		log.Printf("signal %s recieved, attempting soft shutdown after %s...\n", sig, wait)

		go func() {
			select {

			case <-time.After(wait):
				fmt.Println("timeout reached, hard shutdown initiated")
				os.Exit(1)
			case sig := <-s:
				fmt.Printf("recieved additional %s, hard shutdown initiated\n", sig)
				os.Exit(1)
			case <-done:
			}

		}()

		killMeSoftly.wg.Wait()
		log.Println("soft shutdown complete, ending process")
		close(done)
	}()

	if block {
		<-done
	}
}
<commit_msg>Add SIHUP to list of signals being listened for<commit_after>package kms

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

// KillingMeSoftly interface contains all functions needed for operation and method chaining
type KillingMeSoftly interface {
	Done()
}

type killingMeSoftly struct {
	wg *sync.WaitGroup
}

// Logger is the default instance of the log package
var (
	once         sync.Once
	killMeSoftly *killingMeSoftly
	notify       chan struct{}
	done         chan struct{}
)

func init() {
	once.Do(func() {
		killMeSoftly = &killingMeSoftly{
			wg: &sync.WaitGroup{},
		}

		notify = make(chan struct{})
		done = make(chan struct{})
	})
}

// ShutdownInitiated returns a notification channel for the package which will be
// closed/notified once a termination signal is recieved.
//
// usefull when other code, such as a custom TCP connection listener needs to be
// notified to stop listening for new connections.
func ShutdownInitiated() chan struct{} {
	return notify
}

// ShutdownComplete returns a notification channel for the package which will be
// closed/notified once termination is imminent.
func ShutdownComplete() chan struct{} {
	return done
}

// Wait signifies that your application is busy performing an operation.
//
// best to chain using defer kms.Wait().Done()
func Wait() KillingMeSoftly {
	killMeSoftly.wg.Add(1)
	return killMeSoftly
}

// Done signifies that your application is done performing an operation. it is different from
// the chained version as it does not need to be connected the the wait object.
func Done() {
	killMeSoftly.wg.Done()
}

// Done signifies that your application is done performing an operation.
//
// best to chain using defer kms.Wait().Done()
func (k *killingMeSoftly) Done() {
	k.wg.Done()
}

// Listen sets up signals to listen for interupt or kill signals
// in an attempt to wait for all operations to complete before letting
// the process die.
func Listen(block bool) {

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	go func() {
		sig := <-s

		close(notify)

		// listen for another signal, if another happens.. force shutdown
		go func() {
			sig := <-s
			fmt.Printf("recieved additional %s, hard shutdown initiated\n", sig)
			os.Exit(1)
		}()

		log.Printf("signal %s recieved, attempting soft shutdown...\n", sig)
		killMeSoftly.wg.Wait()
		log.Println("soft shutdown complete, ending process")
		close(done)
	}()

	if block {
		<-done
	}
}

// ListenTimeout sets up signals to listen for interupt or kill signals
// in an attempt to wait for all operations to complete before letting
// the process die.
//
// the wait duration is how long to wait before forcefully shutting everything down.
func ListenTimeout(block bool, wait time.Duration) {

	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	go func() {
		sig := <-s

		close(notify)

		log.Printf("signal %s recieved, attempting soft shutdown after %s...\n", sig, wait)

		go func() {
			select {

			case <-time.After(wait):
				fmt.Println("timeout reached, hard shutdown initiated")
				os.Exit(1)
			case sig := <-s:
				fmt.Printf("recieved additional %s, hard shutdown initiated\n", sig)
				os.Exit(1)
			case <-done:
			}

		}()

		killMeSoftly.wg.Wait()
		log.Println("soft shutdown complete, ending process")
		close(done)
	}()

	if block {
		<-done
	}
}
<|endoftext|>


<commit_before><commit_msg>Improve efficiency of UpdateAvailable<commit_after><|endoftext|>


<commit_before>package profitbricks

import (
	"context"
	"net/http"
)

// Lan object
type Lan struct {
	ID         string        `json:"id,omitempty"`
	PBType     string        `json:"type,omitempty"`
	Href       string        `json:"href,omitempty"`
	Metadata   *Metadata     `json:"metadata,omitempty"`
	Properties LanProperties `json:"properties,omitempty"`
	Entities   *LanEntities  `json:"entities,omitempty"`
	Response   string        `json:"Response,omitempty"`
	Headers    *http.Header  `json:"headers,omitempty"`
	StatusCode int           `json:"statuscode,omitempty"`
}

// LanProperties object
type LanProperties struct {
	Name       string        `json:"name,omitempty"`
	Public     bool          `json:"public,omitempty"`
	IPFailover *[]IPFailover `json:"ipFailover,omitempty"`
	PCC        string        `json:"pcc,omitmepty"`
}

// LanEntities object
type LanEntities struct {
	Nics *LanNics `json:"nics,omitempty"`
}

// IPFailover object
type IPFailover struct {
	NicUUID string `json:"nicUuid,omitempty"`
	IP      string `json:"ip,omitempty"`
}

// LanNics object
type LanNics struct {
	ID     string `json:"id,omitempty"`
	PBType string `json:"type,omitempty"`
	Href   string `json:"href,omitempty"`
	Items  []Nic  `json:"items,omitempty"`
}

// Lans object
type Lans struct {
	ID         string       `json:"id,omitempty"`
	PBType     string       `json:"type,omitempty"`
	Href       string       `json:"href,omitempty"`
	Items      []Lan        `json:"items,omitempty"`
	Response   string       `json:"Response,omitempty"`
	Headers    *http.Header `json:"headers,omitempty"`
	StatusCode int          `json:"statuscode,omitempty"`
}

// ListLans returns a Collection for lans in the Datacenter
func (c *Client) ListLans(dcid string) (*Lans, error) {
	url := lansPath(dcid)
	ret := &Lans{}
	err := c.Get(url, ret, http.StatusOK)
	return ret, err
}

// CreateLan creates a lan in the datacenter
// from a jason []byte and returns a Instance struct
func (c *Client) CreateLan(dcid string, request Lan) (*Lan, error) {
	url := lansPath(dcid)
	ret := &Lan{}
	err := c.Post(url, request, ret, http.StatusAccepted)
	return ret, err
}

// CreateLanAndWait creates a lan, waits for the request to finish and returns a refreshed lan
// Note that an error does not necessarily means that the resource has not been created.
// If err & res are not nil, a resource with res.ID exists, but an error occurred either while waiting for
// the request or when refreshing the resource.
func (c *Client) CreateLanAndWait(ctx context.Context, dcid string, request Lan) (res *Lan, err error) {
	res, err = c.CreateLan(dcid, request)
	if err != nil {
		return
	}
	if err = c.WaitTillProvisionedOrCanceled(ctx, res.Headers.Get("location")); err != nil {
		return
	}
	var lan *Lan
	if lan, err = c.GetLan(dcid, res.ID); err != nil {
		return
	} else {
		return lan, err
	}
}

// GetLan pulls data for the lan where id = lanid returns an Instance struct
func (c *Client) GetLan(dcid, lanid string) (*Lan, error) {
	url := lanPath(dcid, lanid)
	ret := &Lan{}
	err := c.Get(url, ret, http.StatusOK)
	return ret, err
}

// UpdateLan does a partial update to a lan using json from []byte json returns a Instance struct
func (c *Client) UpdateLan(dcid string, lanid string, obj LanProperties) (*Lan, error) {
	url := lanPath(dcid, lanid)
	ret := &Lan{}
	err := c.Patch(url, obj, ret, http.StatusAccepted)
	return ret, err
}

// UpdateLanAndWait creates a lan, waits for the request to finish and returns a refreshed lan
// Note that an error does not necessarily means that the resource has not been updated.
// If err & res are not nil, a resource with res.ID exists, but an error occurred either while waiting for
// the request or when refreshing the resource.
func (c *Client) UpdateLanAndWait(ctx context.Context, dcid, lanid string, props LanProperties) (res *Lan, err error) {
	res, err = c.UpdateLan(dcid, lanid, props)
	if err != nil {
		return
	}
	if err = c.WaitTillProvisionedOrCanceled(ctx, res.Headers.Get("location")); err != nil {
		return
	}
	var lan *Lan
	if lan, err = c.GetLan(dcid, res.ID); err != nil {
		return
	} else {
		return lan, err
	}
}

// DeleteLan deletes a lan where id == lanid
func (c *Client) DeleteLan(dcid, lanid string) (*http.Header, error) {
	url := lanPath(dcid, lanid)
	ret := &http.Header{}
	err := c.Delete(url, ret, http.StatusAccepted)
	return ret, err
}

// DeleteLanAndWait deletes given lan and waits for the request to finish
func (c *Client) DeleteLanAndWait(ctx context.Context, dcid, lanid string) error {
	rsp, err := c.DeleteLan(dcid, lanid)
	if err != nil {
		return err
	}
	return c.WaitTillProvisionedOrCanceled(ctx, rsp.Get("location"))
}
<commit_msg>Updates: LAN pcc property<commit_after>package profitbricks

import (
	"context"
	"net/http"
)

// Lan object
type Lan struct {
	ID         string        `json:"id,omitempty"`
	PBType     string        `json:"type,omitempty"`
	Href       string        `json:"href,omitempty"`
	Metadata   *Metadata     `json:"metadata,omitempty"`
	Properties LanProperties `json:"properties,omitempty"`
	Entities   *LanEntities  `json:"entities,omitempty"`
	Response   string        `json:"Response,omitempty"`
	Headers    *http.Header  `json:"headers,omitempty"`
	StatusCode int           `json:"statuscode,omitempty"`
}

// LanProperties object
type LanProperties struct {
	Name       string        `json:"name,omitempty"`
	Public     bool          `json:"public,omitempty"`
	IPFailover *[]IPFailover `json:"ipFailover,omitempty"`
	PCC        string        `json:"pcc,omitempty"`
}

// LanEntities object
type LanEntities struct {
	Nics *LanNics `json:"nics,omitempty"`
}

// IPFailover object
type IPFailover struct {
	NicUUID string `json:"nicUuid,omitempty"`
	IP      string `json:"ip,omitempty"`
}

// LanNics object
type LanNics struct {
	ID     string `json:"id,omitempty"`
	PBType string `json:"type,omitempty"`
	Href   string `json:"href,omitempty"`
	Items  []Nic  `json:"items,omitempty"`
}

// Lans object
type Lans struct {
	ID         string       `json:"id,omitempty"`
	PBType     string       `json:"type,omitempty"`
	Href       string       `json:"href,omitempty"`
	Items      []Lan        `json:"items,omitempty"`
	Response   string       `json:"Response,omitempty"`
	Headers    *http.Header `json:"headers,omitempty"`
	StatusCode int          `json:"statuscode,omitempty"`
}

// ListLans returns a Collection for lans in the Datacenter
func (c *Client) ListLans(dcid string) (*Lans, error) {
	url := lansPath(dcid)
	ret := &Lans{}
	err := c.Get(url, ret, http.StatusOK)
	return ret, err
}

// CreateLan creates a lan in the datacenter
// from a jason []byte and returns a Instance struct
func (c *Client) CreateLan(dcid string, request Lan) (*Lan, error) {
	url := lansPath(dcid)
	ret := &Lan{}
	err := c.Post(url, request, ret, http.StatusAccepted)
	return ret, err
}

// CreateLanAndWait creates a lan, waits for the request to finish and returns a refreshed lan
// Note that an error does not necessarily means that the resource has not been created.
// If err & res are not nil, a resource with res.ID exists, but an error occurred either while waiting for
// the request or when refreshing the resource.
func (c *Client) CreateLanAndWait(ctx context.Context, dcid string, request Lan) (res *Lan, err error) {
	res, err = c.CreateLan(dcid, request)
	if err != nil {
		return
	}
	if err = c.WaitTillProvisionedOrCanceled(ctx, res.Headers.Get("location")); err != nil {
		return
	}
	var lan *Lan
	if lan, err = c.GetLan(dcid, res.ID); err != nil {
		return
	} else {
		return lan, err
	}
}

// GetLan pulls data for the lan where id = lanid returns an Instance struct
func (c *Client) GetLan(dcid, lanid string) (*Lan, error) {
	url := lanPath(dcid, lanid)
	ret := &Lan{}
	err := c.Get(url, ret, http.StatusOK)
	return ret, err
}

// UpdateLan does a partial update to a lan using json from []byte json returns a Instance struct
func (c *Client) UpdateLan(dcid string, lanid string, obj LanProperties) (*Lan, error) {
	url := lanPath(dcid, lanid)
	ret := &Lan{}
	err := c.Patch(url, obj, ret, http.StatusAccepted)
	return ret, err
}

// UpdateLanAndWait creates a lan, waits for the request to finish and returns a refreshed lan
// Note that an error does not necessarily means that the resource has not been updated.
// If err & res are not nil, a resource with res.ID exists, but an error occurred either while waiting for
// the request or when refreshing the resource.
func (c *Client) UpdateLanAndWait(ctx context.Context, dcid, lanid string, props LanProperties) (res *Lan, err error) {
	res, err = c.UpdateLan(dcid, lanid, props)
	if err != nil {
		return
	}
	if err = c.WaitTillProvisionedOrCanceled(ctx, res.Headers.Get("location")); err != nil {
		return
	}
	var lan *Lan
	if lan, err = c.GetLan(dcid, res.ID); err != nil {
		return
	} else {
		return lan, err
	}
}

// DeleteLan deletes a lan where id == lanid
func (c *Client) DeleteLan(dcid, lanid string) (*http.Header, error) {
	url := lanPath(dcid, lanid)
	ret := &http.Header{}
	err := c.Delete(url, ret, http.StatusAccepted)
	return ret, err
}

// DeleteLanAndWait deletes given lan and waits for the request to finish
func (c *Client) DeleteLanAndWait(ctx context.Context, dcid, lanid string) error {
	rsp, err := c.DeleteLan(dcid, lanid)
	if err != nil {
		return err
	}
	return c.WaitTillProvisionedOrCanceled(ctx, rsp.Get("location"))
}
<|endoftext|>