<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"
	"time"

	"github.com/gorilla/mux"
	"github.com/weaveworks/mesh"

	"github.com/weaveworks/go-checkpoint"
	"github.com/weaveworks/weave/ipam"
	"github.com/weaveworks/weave/nameserver"
	"github.com/weaveworks/weave/net/address"
	weave "github.com/weaveworks/weave/router"
)

var rootTemplate = template.New("root").Funcs(map[string]interface{}{
	"countDNSEntries": func(entries []nameserver.EntryStatus) int {
		count := 0
		for _, entry := range entries {
			if entry.Tombstone == 0 {
				count++
			}
		}
		return count
	},
	"printList": func(list []string) string {
		if len(list) == 0 {
			return "none"
		}
		return strings.Join(list, ", ")
	},
	"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
		var buffer bytes.Buffer

		type stats struct {
			ips       uint32
			nickname  string
			reachable bool
		}

		peerStats := make(map[string]*stats)

		for _, entry := range status.Entries {
			s, found := peerStats[entry.Peer]
			if !found {
				s = &stats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
				peerStats[entry.Peer] = s
			}
			s.ips += entry.Size
		}

		printOwned := func(name string, nickName string, info string, ips uint32) {
			percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)

			displayName := name + "(" + nickName + ")"
			fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
				displayName, ips, percentageRanges, info)
		}

		// print the local info first
		if ourStats := peerStats[router.Name]; ourStats != nil {
			printOwned(router.Name, ourStats.nickname, "", ourStats.ips)
		}

		// and then the rest
		for peer, stats := range peerStats {
			if peer != router.Name {
				reachableStr := ""
				if !stats.reachable {
					reachableStr = "- unreachable!"
				}
				printOwned(peer, stats.nickname, reachableStr, stats.ips)
			}
		}

		return buffer.String()
	},
	"allIPAMOwnersUnreachable": func(status ipam.Status) bool {
		for _, entry := range status.Entries {
			if entry.Size > 0 && entry.IsKnownPeer {
				return false
			}
		}
		return true
	},
	"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
		counts := make(map[string]int)
		for _, conn := range conns {
			counts[conn.State]++
		}
		return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
	},
	"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
		counts := make(map[string]int)
		for _, peer := range peers {
			for _, conn := range peer.Connections {
				if conn.Established {
					counts["established"]++
				} else {
					counts["pending"]++
				}
			}
		}
		return printCounts(counts, []string{"established", "pending"})
	},
	"printState": func(enabled bool) string {
		if enabled {
			return "enabled"
		}
		return "disabled"
	},
	"trimSuffix": strings.TrimSuffix,
})

// Print counts in a specified order
func printCounts(counts map[string]int, keys []string) string {
	var stringCounts []string
	for _, key := range keys {
		if count, ok := counts[key]; ok {
			stringCounts = append(stringCounts, fmt.Sprintf("%d %s", count, key))
		}
	}
	return strings.Join(stringCounts, ", ")
}

// Strip escaped newlines from template
func escape(template string) string {
	return strings.Replace(template, "\\\n", "", -1)
}

// Define a named template panicking on error
func defTemplate(name string, text string) *template.Template {
	return template.Must(rootTemplate.New(name).Parse(escape(text)))
}

var statusTemplate = defTemplate("status", `\
        Version: {{.Version}} ({{.VersionCheck}})

        Service: router
       Protocol: {{.Router.Protocol}} \
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
{{.Router.ProtocolMaxVersion}}\
{{else}}\
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
{{end}}
           Name: {{.Router.Name}}({{.Router.NickName}})
     Encryption: {{printState .Router.Encryption}}
  PeerDiscovery: {{printState .Router.PeerDiscovery}}
        Targets: {{len .Router.Targets}}
    Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
          Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
 TrustedSubnets: {{printList .Router.TrustedSubnets}}
{{if .IPAM}}\

        Service: ipam
{{if .IPAM.Entries}}\
{{if allIPAMOwnersUnreachable .IPAM}}\
         Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
{{else if len .IPAM.PendingAllocates}}\
         Status: waiting for IP range grant from peers
{{else}}\
         Status: ready
{{end}}\
{{else if .IPAM.Paxos}}\
{{if .IPAM.Paxos.Elector}}\
         Status: awaiting consensus (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{else}}\
         Status: priming
{{end}}\
{{else}}\
         Status: idle
{{end}}\
          Range: {{.IPAM.Range}}
  DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{end}}\
{{if .DNS}}\

        Service: dns
         Domain: {{.DNS.Domain}}
       Upstream: {{printList .DNS.Upstream}}
            TTL: {{.DNS.TTL}}
        Entries: {{countDNSEntries .DNS.Entries}}
{{end}}\
`)

var targetsTemplate = defTemplate("targetsTemplate", `\
{{range .Router.Targets}}{{.}}
{{end}}\
`)

var connectionsTemplate = defTemplate("connectionsTemplate", `\
{{range .Router.Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} {{printf "%-11v" .State}} {{.Info}}
{{end}}\
`)

var peersTemplate = defTemplate("peers", `\
{{range .Router.Peers}}\
{{.Name}}({{.NickName}})
{{range .Connections}}\
   {{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-37v" $nameNickName}} \
{{if .Established}}established{{else}}pending{{end}}
{{end}}\
{{end}}\
`)

var dnsEntriesTemplate = defTemplate("dnsEntries", `\
{{$domain := printf ".%v" .DNS.Domain}}\
{{range .DNS.Entries}}\
{{if eq .Tombstone 0}}\
{{$hostname := trimSuffix .Hostname $domain}}\
{{printf "%-12v" $hostname}} {{printf "%-15v" .Address}} {{printf "%12.12v" .ContainerID}} {{.Origin}}
{{end}}\
{{end}}\
`)

var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}}`)

type VersionCheck struct {
	Enabled     bool
	NewVersion  string
	NextCheckAt time.Time
}

func versionCheck() *VersionCheck {
	v := &VersionCheck{}
	if checkpoint.IsCheckDisabled() {
		return v
	}

	v.Enabled = true
	v.NewVersion = newVersion.Load().(string)
	v.NextCheckAt = checker.NextCheckAt()

	return v
}

func (v *VersionCheck) String() string {
	switch {
	case !v.Enabled:
		return "version check update disabled"
	case v.NewVersion != "":
		return fmt.Sprintf("version %s available - please upgrade!",
			v.NewVersion)
	default:
		return fmt.Sprintf("up to date; next check at %s",
			v.NextCheckAt.Format("2006/01/02 15:04:05"))
	}
}

type WeaveStatus struct {
	Version      string
	VersionCheck *VersionCheck              `json:"VersionCheck,omitempty"`
	Router       *weave.NetworkRouterStatus `json:"Router,omitempty"`
	IPAM         *ipam.Status               `json:"IPAM,omitempty"`
	DNS          *nameserver.Status         `json:"DNS,omitempty"`
}

func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
	status := func() WeaveStatus {
		return WeaveStatus{
			version,
			versionCheck(),
			weave.NewNetworkRouterStatus(router),
			ipam.NewStatus(allocator, defaultSubnet),
			nameserver.NewStatus(ns, dnsserver)}
	}
	muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			json, err := json.MarshalIndent(status(), "", "    ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				Log.Error("Error during report marshalling: ", err)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(json)
		})

	muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			funcs := template.FuncMap{
				"json": func(v interface{}) string {
					a, _ := json.Marshal(v)
					return string(a)
				},
			}
			formatTemplate, err := template.New("format").Funcs(funcs).Parse(mux.Vars(r)["format"])
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			if err := formatTemplate.Execute(w, status()); err != nil {
				http.Error(w, "error during template execution", http.StatusInternalServerError)
				Log.Error(err)
			}
		})

	defHandler := func(path string, template *template.Template) {
		muxRouter.Methods("GET").Path(path).HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				if err := template.Execute(w, status()); err != nil {
					http.Error(w, "error during template execution", http.StatusInternalServerError)
					Log.Error(err)
				}
			})
	}

	defHandler("/status", statusTemplate)
	defHandler("/status/targets", targetsTemplate)
	defHandler("/status/connections", connectionsTemplate)
	defHandler("/status/peers", peersTemplate)
	defHandler("/status/dns", dnsEntriesTemplate)
	defHandler("/status/ipam", ipamTemplate)
}
<commit_msg>Extract countDNSEntries function<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"
	"time"

	"github.com/gorilla/mux"
	"github.com/weaveworks/mesh"

	"github.com/weaveworks/go-checkpoint"
	"github.com/weaveworks/weave/ipam"
	"github.com/weaveworks/weave/nameserver"
	"github.com/weaveworks/weave/net/address"
	weave "github.com/weaveworks/weave/router"
)

var rootTemplate = template.New("root").Funcs(map[string]interface{}{
	"countDNSEntries": countDNSEntries,
	"printList": func(list []string) string {
		if len(list) == 0 {
			return "none"
		}
		return strings.Join(list, ", ")
	},
	"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
		var buffer bytes.Buffer

		type stats struct {
			ips       uint32
			nickname  string
			reachable bool
		}

		peerStats := make(map[string]*stats)

		for _, entry := range status.Entries {
			s, found := peerStats[entry.Peer]
			if !found {
				s = &stats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
				peerStats[entry.Peer] = s
			}
			s.ips += entry.Size
		}

		printOwned := func(name string, nickName string, info string, ips uint32) {
			percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)

			displayName := name + "(" + nickName + ")"
			fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
				displayName, ips, percentageRanges, info)
		}

		// print the local info first
		if ourStats := peerStats[router.Name]; ourStats != nil {
			printOwned(router.Name, ourStats.nickname, "", ourStats.ips)
		}

		// and then the rest
		for peer, stats := range peerStats {
			if peer != router.Name {
				reachableStr := ""
				if !stats.reachable {
					reachableStr = "- unreachable!"
				}
				printOwned(peer, stats.nickname, reachableStr, stats.ips)
			}
		}

		return buffer.String()
	},
	"allIPAMOwnersUnreachable": func(status ipam.Status) bool {
		for _, entry := range status.Entries {
			if entry.Size > 0 && entry.IsKnownPeer {
				return false
			}
		}
		return true
	},
	"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
		counts := make(map[string]int)
		for _, conn := range conns {
			counts[conn.State]++
		}
		return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
	},
	"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
		counts := make(map[string]int)
		for _, peer := range peers {
			for _, conn := range peer.Connections {
				if conn.Established {
					counts["established"]++
				} else {
					counts["pending"]++
				}
			}
		}
		return printCounts(counts, []string{"established", "pending"})
	},
	"printState": func(enabled bool) string {
		if enabled {
			return "enabled"
		}
		return "disabled"
	},
	"trimSuffix": strings.TrimSuffix,
})

func countDNSEntries(entries []nameserver.EntryStatus) int {
	count := 0
	for _, entry := range entries {
		if entry.Tombstone == 0 {
			count++
		}
	}
	return count
}

// Print counts in a specified order
func printCounts(counts map[string]int, keys []string) string {
	var stringCounts []string
	for _, key := range keys {
		if count, ok := counts[key]; ok {
			stringCounts = append(stringCounts, fmt.Sprintf("%d %s", count, key))
		}
	}
	return strings.Join(stringCounts, ", ")
}

// Strip escaped newlines from template
func escape(template string) string {
	return strings.Replace(template, "\\\n", "", -1)
}

// Define a named template panicking on error
func defTemplate(name string, text string) *template.Template {
	return template.Must(rootTemplate.New(name).Parse(escape(text)))
}

var statusTemplate = defTemplate("status", `\
        Version: {{.Version}} ({{.VersionCheck}})

        Service: router
       Protocol: {{.Router.Protocol}} \
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
{{.Router.ProtocolMaxVersion}}\
{{else}}\
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
{{end}}
           Name: {{.Router.Name}}({{.Router.NickName}})
     Encryption: {{printState .Router.Encryption}}
  PeerDiscovery: {{printState .Router.PeerDiscovery}}
        Targets: {{len .Router.Targets}}
    Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
          Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
 TrustedSubnets: {{printList .Router.TrustedSubnets}}
{{if .IPAM}}\

        Service: ipam
{{if .IPAM.Entries}}\
{{if allIPAMOwnersUnreachable .IPAM}}\
         Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
{{else if len .IPAM.PendingAllocates}}\
         Status: waiting for IP range grant from peers
{{else}}\
         Status: ready
{{end}}\
{{else if .IPAM.Paxos}}\
{{if .IPAM.Paxos.Elector}}\
         Status: awaiting consensus (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{else}}\
         Status: priming
{{end}}\
{{else}}\
         Status: idle
{{end}}\
          Range: {{.IPAM.Range}}
  DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{end}}\
{{if .DNS}}\

        Service: dns
         Domain: {{.DNS.Domain}}
       Upstream: {{printList .DNS.Upstream}}
            TTL: {{.DNS.TTL}}
        Entries: {{countDNSEntries .DNS.Entries}}
{{end}}\
`)

var targetsTemplate = defTemplate("targetsTemplate", `\
{{range .Router.Targets}}{{.}}
{{end}}\
`)

var connectionsTemplate = defTemplate("connectionsTemplate", `\
{{range .Router.Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} {{printf "%-11v" .State}} {{.Info}}
{{end}}\
`)

var peersTemplate = defTemplate("peers", `\
{{range .Router.Peers}}\
{{.Name}}({{.NickName}})
{{range .Connections}}\
   {{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-37v" $nameNickName}} \
{{if .Established}}established{{else}}pending{{end}}
{{end}}\
{{end}}\
`)

var dnsEntriesTemplate = defTemplate("dnsEntries", `\
{{$domain := printf ".%v" .DNS.Domain}}\
{{range .DNS.Entries}}\
{{if eq .Tombstone 0}}\
{{$hostname := trimSuffix .Hostname $domain}}\
{{printf "%-12v" $hostname}} {{printf "%-15v" .Address}} {{printf "%12.12v" .ContainerID}} {{.Origin}}
{{end}}\
{{end}}\
`)

var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}}`)

type VersionCheck struct {
	Enabled     bool
	NewVersion  string
	NextCheckAt time.Time
}

func versionCheck() *VersionCheck {
	v := &VersionCheck{}
	if checkpoint.IsCheckDisabled() {
		return v
	}

	v.Enabled = true
	v.NewVersion = newVersion.Load().(string)
	v.NextCheckAt = checker.NextCheckAt()

	return v
}

func (v *VersionCheck) String() string {
	switch {
	case !v.Enabled:
		return "version check update disabled"
	case v.NewVersion != "":
		return fmt.Sprintf("version %s available - please upgrade!",
			v.NewVersion)
	default:
		return fmt.Sprintf("up to date; next check at %s",
			v.NextCheckAt.Format("2006/01/02 15:04:05"))
	}
}

type WeaveStatus struct {
	Version      string
	VersionCheck *VersionCheck              `json:"VersionCheck,omitempty"`
	Router       *weave.NetworkRouterStatus `json:"Router,omitempty"`
	IPAM         *ipam.Status               `json:"IPAM,omitempty"`
	DNS          *nameserver.Status         `json:"DNS,omitempty"`
}

func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
	status := func() WeaveStatus {
		return WeaveStatus{
			version,
			versionCheck(),
			weave.NewNetworkRouterStatus(router),
			ipam.NewStatus(allocator, defaultSubnet),
			nameserver.NewStatus(ns, dnsserver)}
	}
	muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			json, err := json.MarshalIndent(status(), "", "    ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				Log.Error("Error during report marshalling: ", err)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			w.Write(json)
		})

	muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			funcs := template.FuncMap{
				"json": func(v interface{}) string {
					a, _ := json.Marshal(v)
					return string(a)
				},
			}
			formatTemplate, err := template.New("format").Funcs(funcs).Parse(mux.Vars(r)["format"])
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			if err := formatTemplate.Execute(w, status()); err != nil {
				http.Error(w, "error during template execution", http.StatusInternalServerError)
				Log.Error(err)
			}
		})

	defHandler := func(path string, template *template.Template) {
		muxRouter.Methods("GET").Path(path).HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				if err := template.Execute(w, status()); err != nil {
					http.Error(w, "error during template execution", http.StatusInternalServerError)
					Log.Error(err)
				}
			})
	}

	defHandler("/status", statusTemplate)
	defHandler("/status/targets", targetsTemplate)
	defHandler("/status/connections", connectionsTemplate)
	defHandler("/status/peers", peersTemplate)
	defHandler("/status/dns", dnsEntriesTemplate)
	defHandler("/status/ipam", ipamTemplate)
}
<|endoftext|>


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

// Package static exports a map of static file content that supports the godoc
// user interface. The map should be used with the mapfs package, see
// code.google.com/p/godoc/vfs/mapfs.
package static
<commit_msg>go.tools/godoc/static: fix import path doc typo<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package static exports a map of static file content that supports the godoc
// user interface. The map should be used with the mapfs package, see
// code.google.com/p/go.tools/godoc/vfs/mapfs.
package static
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package gossip

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"regexp"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
)

// callback holds regexp pattern match and GossipCallback method.
type callback struct {
	pattern *regexp.Regexp
	method  Callback
}

// infoStore objects manage maps of Info objects. They maintain a
// sequence number generator which they use to allocate new info
// objects.
//
// infoStores can be queried for incremental updates occurring since a
// specified map of peer node high water timestamps.
//
// infoStores can be combined using deltas from peer nodes.
//
// infoStores are not thread safe.
type infoStore struct {
	Infos     infoMap             `json:"infos,omitempty"` // Map from key to info
	NodeID    roachpb.NodeID      `json:"-"`               // Owning node's ID
	NodeAddr  util.UnresolvedAddr `json:"-"`               // Address of node owning this info store: "host:port"
	nodes     map[int32]*Node     // Per-node information for gossip peers
	callbacks []*callback

	callbackMu     sync.Mutex // Serializes callbacks
	callbackWorkMu sync.Mutex // Protects callbackWork
	callbackWork   []func()
}

var monoTime struct {
	sync.Mutex
	last int64
}

var errNotFresh = errors.New("info not fresh")

// monotonicUnixNano returns a monotonically increasing value for
// nanoseconds in Unix time. Since equal times are ignored with
// updates to infos, we're careful to avoid incorrectly ignoring a
// newly created value in the event one is created within the same
// nanosecond. Really unlikely except for the case of unittests, but
// better safe than sorry.
func monotonicUnixNano() int64 {
	monoTime.Lock()
	defer monoTime.Unlock()

	now := time.Now().UnixNano()
	if now <= monoTime.last {
		now = monoTime.last + 1
	}
	monoTime.last = now
	return now
}

// String returns a string representation of an infostore.
func (is *infoStore) String() string {
	buf := bytes.Buffer{}
	if infoCount := len(is.Infos); infoCount > 0 {
		fmt.Fprintf(&buf, "infostore with %d info(s): ", infoCount)
	} else {
		return "infostore (empty)"
	}

	prepend := ""

	if err := is.visitInfos(func(key string, i *Info) error {
		fmt.Fprintf(&buf, "%sinfo %q: %+v", prepend, key, i.Value)
		prepend = ", "
		return nil
	}); err != nil {
		log.Errorf("failed to properly construct string representation of infoStore: %s", err)
	}
	return buf.String()
}

// newInfoStore allocates and returns a new infoStore.
func newInfoStore(nodeID roachpb.NodeID, nodeAddr util.UnresolvedAddr) *infoStore {
	return &infoStore{
		Infos:    make(infoMap),
		NodeID:   nodeID,
		NodeAddr: nodeAddr,
		nodes:    map[int32]*Node{},
	}
}

// newInfo allocates and returns a new info object using specified key,
// value, and time-to-live.
func (is *infoStore) newInfo(val []byte, ttl time.Duration) *Info {
	if is.NodeID == 0 {
		panic("gossip infostore's NodeID is 0")
	}
	now := monotonicUnixNano()
	ttlStamp := now + int64(ttl)
	if ttl == 0 {
		ttlStamp = math.MaxInt64
	}
	v := roachpb.MakeValueFromBytesAndTimestamp(val, roachpb.Timestamp{WallTime: now})
	return &Info{
		Value:    v,
		TTLStamp: ttlStamp,
		NodeID:   is.NodeID,
	}
}

// getInfo returns the Info at key. Returns nil when key is not present
// in the infoStore.
func (is *infoStore) getInfo(key string) *Info {
	if info, ok := is.Infos[key]; ok {
		// Check TTL and discard if too old.
		if info.expired(time.Now().UnixNano()) {
			delete(is.Infos, key)
		} else {
			return info
		}
	}
	return nil
}

// addInfo adds or updates an info in the infos map.
//
// Returns nil if info was added; error otherwise.
func (is *infoStore) addInfo(key string, i *Info) error {
	if i.NodeID == 0 {
		panic("gossip info's NodeID is 0")
	}
	// Only replace an existing info if new timestamp is greater, or if
	// timestamps are equal, but new hops is smaller.
	if existingInfo, ok := is.Infos[key]; ok {
		iNanos := i.Value.Timestamp.WallTime
		existingNanos := existingInfo.Value.Timestamp.WallTime
		if iNanos < existingNanos || (iNanos == existingNanos && i.Hops >= existingInfo.Hops) {
			return errNotFresh
		}
	}
	if i.OrigStamp == 0 {
		i.Value.InitChecksum([]byte(key))
		i.OrigStamp = monotonicUnixNano()
		if n, ok := is.nodes[int32(i.NodeID)]; ok && n.HighWaterStamp >= i.OrigStamp {
			panic(util.Errorf("high water stamp %d >= %d", n.HighWaterStamp, i.OrigStamp))
		}
	}
	// Update info map.
	is.Infos[key] = i
	// Update the high water timestamp & min hops for the originating node.
	if nID := int32(i.NodeID); nID != 0 {
		n, ok := is.nodes[nID]
		if !ok {
			is.nodes[nID] = &Node{i.OrigStamp, i.Hops}
		} else {
			if n.HighWaterStamp < i.OrigStamp {
				n.HighWaterStamp = i.OrigStamp
			}
			if n.MinHops > i.Hops {
				n.MinHops = i.Hops
			}
		}
	}
	is.processCallbacks(key, i.Value)
	return nil
}

// getNodes returns a copy of the nodes map of gossip peer info
// maintained by this infostore.
func (is *infoStore) getNodes() map[int32]*Node {
	copy := make(map[int32]*Node, len(is.nodes))
	for k, v := range is.nodes {
		nodeCopy := *v
		copy[k] = &nodeCopy
	}
	return copy
}

// registerCallback registers a callback for a key pattern to be
// invoked whenever new info for a gossip key matching pattern is
// received. The callback method is invoked with the info key which
// matched pattern. Returns a function to unregister the callback.
// Note: the callback may fire after being unregistered.
func (is *infoStore) registerCallback(pattern string, method Callback) func() {
	re := regexp.MustCompile(pattern)
	cb := &callback{pattern: re, method: method}
	is.callbacks = append(is.callbacks, cb)
	if err := is.visitInfos(func(key string, i *Info) error {
		if re.MatchString(key) {
			is.runCallbacks(key, i.Value, method)
		}
		return nil
	}); err != nil {
		panic(err)
	}

	return func() {
		for i, targetCB := range is.callbacks {
			if targetCB == cb {
				numCBs := len(is.callbacks)
				is.callbacks[i] = is.callbacks[numCBs-1]
				is.callbacks = is.callbacks[:numCBs-1]
				break
			}
		}
	}
}

// processCallbacks processes callbacks for the specified key by
// matching callback regular expression against the key and invoking
// the corresponding callback method on a match.
func (is *infoStore) processCallbacks(key string, content roachpb.Value) {
	var matches []Callback
	for _, cb := range is.callbacks {
		if cb.pattern.MatchString(key) {
			matches = append(matches, cb.method)
		}
	}
	is.runCallbacks(key, content, matches...)
}

func (is *infoStore) runCallbacks(key string, content roachpb.Value, callbacks ...Callback) {
	// Add the callbacks to the callback work list.
	f := func() {
		for _, method := range callbacks {
			method(key, content)
		}
	}
	is.callbackWorkMu.Lock()
	is.callbackWork = append(is.callbackWork, f)
	is.callbackWorkMu.Unlock()

	// Run callbacks in a goroutine to avoid mutex reentry. We also guarantee
	// callbacks are run in order such that if a key is updated twice in
	// succession, the second callback will never be run before the first.
	go func() {
		// Grab the callback mutex to serialize execution of the callbacks.
		is.callbackMu.Lock()
		defer is.callbackMu.Unlock()

		// Grab and execute the list of work.
		is.callbackWorkMu.Lock()
		work := is.callbackWork
		is.callbackWork = nil
		is.callbackWorkMu.Unlock()

		for _, w := range work {
			w()
		}
	}()
}

// visitInfos implements a visitor pattern to run the visitInfo
// function against each info in turn. Be sure to skip over any expired
// infos.
func (is *infoStore) visitInfos(visitInfo func(string, *Info) error) error {
	now := time.Now().UnixNano()

	if visitInfo != nil {
		for k, i := range is.Infos {
			if i.expired(now) {
				delete(is.Infos, k)
				continue
			}
			if err := visitInfo(k, i); err != nil {
				return err
			}
		}
	}

	return nil
}

// combine combines an incremental delta with the current infoStore.
// All hop distances on infos are incremented to indicate they've
// arrived from an external source. Returns the count of "fresh"
// infos in the provided delta.
func (is *infoStore) combine(infos map[string]*Info, nodeID roachpb.NodeID) (freshCount int, err error) {
	for key, i := range infos {
		copy := *i
		copy.Hops++
		copy.PeerID = nodeID
		// Errors from addInfo here are not a problem; they simply
		// indicate that the data in *is is newer than in *delta.
		if copy.OrigStamp == 0 {
			panic(util.Errorf("combining info from node %d with 0 original timestamp", nodeID))
		}
		if addErr := is.addInfo(key, &copy); addErr == nil {
			freshCount++
		} else if addErr != errNotFresh {
			err = addErr
		}
	}
	return
}

// delta returns a map of infos which are newer or have fewer hops
// than the values indicated by the supplied nodes map. The
// supplied nodes map contains gossip node information from the
// perspective of the peer asking for the delta. That is, the map
// contains a record of the most recent info timestamp and min hops
// which the requester has seen from each node in the network.
func (is *infoStore) delta(nodeID roachpb.NodeID, nodes map[int32]*Node) map[string]*Info {
	infos := make(map[string]*Info)
	// Compute delta of infos.
	if err := is.visitInfos(func(key string, i *Info) error {
		if i.isFresh(nodes[int32(i.NodeID)]) {
			infos[key] = i
		}
		return nil
	}); err != nil {
		panic(err)
	}

	return infos
}

// mostDistant returns the most distant gossip node known to the
// store as well as the number of hops to reach it.
func (is *infoStore) mostDistant() (roachpb.NodeID, uint32) {
	var nodeID roachpb.NodeID
	var maxHops uint32
	if err := is.visitInfos(func(key string, i *Info) error {
		if i.Hops > maxHops {
			maxHops = i.Hops
			nodeID = i.NodeID
		}
		return nil
	}); err != nil {
		panic(err)
	}
	return nodeID, maxHops
}

// leastUseful determines which node ID from amongst the set is
// currently contributing the least. Returns the node ID. If nodes is
// empty, returns 0.
func (is *infoStore) leastUseful(nodes nodeSet) roachpb.NodeID {
	contrib := make(map[roachpb.NodeID]map[roachpb.NodeID]struct{}, nodes.len())
	for node := range nodes.nodes {
		contrib[node] = map[roachpb.NodeID]struct{}{}
	}
	if err := is.visitInfos(func(key string, i *Info) error {
		if _, ok := contrib[i.PeerID]; !ok {
			contrib[i.PeerID] = map[roachpb.NodeID]struct{}{}
		}
		contrib[i.PeerID][i.NodeID] = struct{}{}
		return nil
	}); err != nil {
		panic(err)
	}

	least := math.MaxInt32
	var leastNode roachpb.NodeID
	for id, m := range contrib {
		count := len(m)
		if nodes.hasNode(id) {
			if count < least {
				least = count
				leastNode = id
			}
		}
	}
	return leastNode
}
<commit_msg>gossip: avoid keyword<commit_after>// Copyright 2014 The Cockroach 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package gossip

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"regexp"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
)

// callback holds regexp pattern match and GossipCallback method.
type callback struct {
	pattern *regexp.Regexp
	method  Callback
}

// infoStore objects manage maps of Info objects. They maintain a
// sequence number generator which they use to allocate new info
// objects.
//
// infoStores can be queried for incremental updates occurring since a
// specified map of peer node high water timestamps.
//
// infoStores can be combined using deltas from peer nodes.
//
// infoStores are not thread safe.
type infoStore struct {
	Infos     infoMap             `json:"infos,omitempty"` // Map from key to info
	NodeID    roachpb.NodeID      `json:"-"`               // Owning node's ID
	NodeAddr  util.UnresolvedAddr `json:"-"`               // Address of node owning this info store: "host:port"
	nodes     map[int32]*Node     // Per-node information for gossip peers
	callbacks []*callback

	callbackMu     sync.Mutex // Serializes callbacks
	callbackWorkMu sync.Mutex // Protects callbackWork
	callbackWork   []func()
}

var monoTime struct {
	sync.Mutex
	last int64
}

var errNotFresh = errors.New("info not fresh")

// monotonicUnixNano returns a monotonically increasing value for
// nanoseconds in Unix time. Since equal times are ignored with
// updates to infos, we're careful to avoid incorrectly ignoring a
// newly created value in the event one is created within the same
// nanosecond. Really unlikely except for the case of unittests, but
// better safe than sorry.
func monotonicUnixNano() int64 {
	monoTime.Lock()
	defer monoTime.Unlock()

	now := time.Now().UnixNano()
	if now <= monoTime.last {
		now = monoTime.last + 1
	}
	monoTime.last = now
	return now
}

// String returns a string representation of an infostore.
func (is *infoStore) String() string {
	buf := bytes.Buffer{}
	if infoCount := len(is.Infos); infoCount > 0 {
		fmt.Fprintf(&buf, "infostore with %d info(s): ", infoCount)
	} else {
		return "infostore (empty)"
	}

	prepend := ""

	if err := is.visitInfos(func(key string, i *Info) error {
		fmt.Fprintf(&buf, "%sinfo %q: %+v", prepend, key, i.Value)
		prepend = ", "
		return nil
	}); err != nil {
		log.Errorf("failed to properly construct string representation of infoStore: %s", err)
	}
	return buf.String()
}

// newInfoStore allocates and returns a new infoStore.
func newInfoStore(nodeID roachpb.NodeID, nodeAddr util.UnresolvedAddr) *infoStore {
	return &infoStore{
		Infos:    make(infoMap),
		NodeID:   nodeID,
		NodeAddr: nodeAddr,
		nodes:    map[int32]*Node{},
	}
}

// newInfo allocates and returns a new info object using specified key,
// value, and time-to-live.
func (is *infoStore) newInfo(val []byte, ttl time.Duration) *Info {
	if is.NodeID == 0 {
		panic("gossip infostore's NodeID is 0")
	}
	now := monotonicUnixNano()
	ttlStamp := now + int64(ttl)
	if ttl == 0 {
		ttlStamp = math.MaxInt64
	}
	v := roachpb.MakeValueFromBytesAndTimestamp(val, roachpb.Timestamp{WallTime: now})
	return &Info{
		Value:    v,
		TTLStamp: ttlStamp,
		NodeID:   is.NodeID,
	}
}

// getInfo returns the Info at key. Returns nil when key is not present
// in the infoStore.
func (is *infoStore) getInfo(key string) *Info {
	if info, ok := is.Infos[key]; ok {
		// Check TTL and discard if too old.
		if info.expired(time.Now().UnixNano()) {
			delete(is.Infos, key)
		} else {
			return info
		}
	}
	return nil
}

// addInfo adds or updates an info in the infos map.
//
// Returns nil if info was added; error otherwise.
func (is *infoStore) addInfo(key string, i *Info) error {
	if i.NodeID == 0 {
		panic("gossip info's NodeID is 0")
	}
	// Only replace an existing info if new timestamp is greater, or if
	// timestamps are equal, but new hops is smaller.
	if existingInfo, ok := is.Infos[key]; ok {
		iNanos := i.Value.Timestamp.WallTime
		existingNanos := existingInfo.Value.Timestamp.WallTime
		if iNanos < existingNanos || (iNanos == existingNanos && i.Hops >= existingInfo.Hops) {
			return errNotFresh
		}
	}
	if i.OrigStamp == 0 {
		i.Value.InitChecksum([]byte(key))
		i.OrigStamp = monotonicUnixNano()
		if n, ok := is.nodes[int32(i.NodeID)]; ok && n.HighWaterStamp >= i.OrigStamp {
			panic(util.Errorf("high water stamp %d >= %d", n.HighWaterStamp, i.OrigStamp))
		}
	}
	// Update info map.
	is.Infos[key] = i
	// Update the high water timestamp & min hops for the originating node.
	if nID := int32(i.NodeID); nID != 0 {
		n, ok := is.nodes[nID]
		if !ok {
			is.nodes[nID] = &Node{i.OrigStamp, i.Hops}
		} else {
			if n.HighWaterStamp < i.OrigStamp {
				n.HighWaterStamp = i.OrigStamp
			}
			if n.MinHops > i.Hops {
				n.MinHops = i.Hops
			}
		}
	}
	is.processCallbacks(key, i.Value)
	return nil
}

// getNodes returns a copy of the nodes map of gossip peer info
// maintained by this infostore.
func (is *infoStore) getNodes() map[int32]*Node {
	copy := make(map[int32]*Node, len(is.nodes))
	for k, v := range is.nodes {
		nodeCopy := *v
		copy[k] = &nodeCopy
	}
	return copy
}

// registerCallback registers a callback for a key pattern to be
// invoked whenever new info for a gossip key matching pattern is
// received. The callback method is invoked with the info key which
// matched pattern. Returns a function to unregister the callback.
// Note: the callback may fire after being unregistered.
func (is *infoStore) registerCallback(pattern string, method Callback) func() {
	re := regexp.MustCompile(pattern)
	cb := &callback{pattern: re, method: method}
	is.callbacks = append(is.callbacks, cb)
	if err := is.visitInfos(func(key string, i *Info) error {
		if re.MatchString(key) {
			is.runCallbacks(key, i.Value, method)
		}
		return nil
	}); err != nil {
		panic(err)
	}

	return func() {
		for i, targetCB := range is.callbacks {
			if targetCB == cb {
				numCBs := len(is.callbacks)
				is.callbacks[i] = is.callbacks[numCBs-1]
				is.callbacks = is.callbacks[:numCBs-1]
				break
			}
		}
	}
}

// processCallbacks processes callbacks for the specified key by
// matching callback regular expression against the key and invoking
// the corresponding callback method on a match.
func (is *infoStore) processCallbacks(key string, content roachpb.Value) {
	var matches []Callback
	for _, cb := range is.callbacks {
		if cb.pattern.MatchString(key) {
			matches = append(matches, cb.method)
		}
	}
	is.runCallbacks(key, content, matches...)
}

func (is *infoStore) runCallbacks(key string, content roachpb.Value, callbacks ...Callback) {
	// Add the callbacks to the callback work list.
	f := func() {
		for _, method := range callbacks {
			method(key, content)
		}
	}
	is.callbackWorkMu.Lock()
	is.callbackWork = append(is.callbackWork, f)
	is.callbackWorkMu.Unlock()

	// Run callbacks in a goroutine to avoid mutex reentry. We also guarantee
	// callbacks are run in order such that if a key is updated twice in
	// succession, the second callback will never be run before the first.
	go func() {
		// Grab the callback mutex to serialize execution of the callbacks.
		is.callbackMu.Lock()
		defer is.callbackMu.Unlock()

		// Grab and execute the list of work.
		is.callbackWorkMu.Lock()
		work := is.callbackWork
		is.callbackWork = nil
		is.callbackWorkMu.Unlock()

		for _, w := range work {
			w()
		}
	}()
}

// visitInfos implements a visitor pattern to run the visitInfo
// function against each info in turn. Be sure to skip over any expired
// infos.
func (is *infoStore) visitInfos(visitInfo func(string, *Info) error) error {
	now := time.Now().UnixNano()

	if visitInfo != nil {
		for k, i := range is.Infos {
			if i.expired(now) {
				delete(is.Infos, k)
				continue
			}
			if err := visitInfo(k, i); err != nil {
				return err
			}
		}
	}

	return nil
}

// combine combines an incremental delta with the current infoStore.
// All hop distances on infos are incremented to indicate they've
// arrived from an external source. Returns the count of "fresh"
// infos in the provided delta.
func (is *infoStore) combine(infos map[string]*Info, nodeID roachpb.NodeID) (freshCount int, err error) {
	for key, i := range infos {
		infoCopy := *i
		infoCopy.Hops++
		infoCopy.PeerID = nodeID
		// Errors from addInfo here are not a problem; they simply
		// indicate that the data in *is is newer than in *delta.
		if infoCopy.OrigStamp == 0 {
			panic(util.Errorf("combining info from node %d with 0 original timestamp", nodeID))
		}
		if addErr := is.addInfo(key, &infoCopy); addErr == nil {
			freshCount++
		} else if addErr != errNotFresh {
			err = addErr
		}
	}
	return
}

// delta returns a map of infos which are newer or have fewer hops
// than the values indicated by the supplied nodes map. The
// supplied nodes map contains gossip node information from the
// perspective of the peer asking for the delta. That is, the map
// contains a record of the most recent info timestamp and min hops
// which the requester has seen from each node in the network.
func (is *infoStore) delta(nodeID roachpb.NodeID, nodes map[int32]*Node) map[string]*Info {
	infos := make(map[string]*Info)
	// Compute delta of infos.
	if err := is.visitInfos(func(key string, i *Info) error {
		if i.isFresh(nodes[int32(i.NodeID)]) {
			infos[key] = i
		}
		return nil
	}); err != nil {
		panic(err)
	}

	return infos
}

// mostDistant returns the most distant gossip node known to the
// store as well as the number of hops to reach it.
func (is *infoStore) mostDistant() (roachpb.NodeID, uint32) {
	var nodeID roachpb.NodeID
	var maxHops uint32
	if err := is.visitInfos(func(key string, i *Info) error {
		if i.Hops > maxHops {
			maxHops = i.Hops
			nodeID = i.NodeID
		}
		return nil
	}); err != nil {
		panic(err)
	}
	return nodeID, maxHops
}

// leastUseful determines which node ID from amongst the set is
// currently contributing the least. Returns the node ID. If nodes is
// empty, returns 0.
func (is *infoStore) leastUseful(nodes nodeSet) roachpb.NodeID {
	contrib := make(map[roachpb.NodeID]map[roachpb.NodeID]struct{}, nodes.len())
	for node := range nodes.nodes {
		contrib[node] = map[roachpb.NodeID]struct{}{}
	}
	if err := is.visitInfos(func(key string, i *Info) error {
		if _, ok := contrib[i.PeerID]; !ok {
			contrib[i.PeerID] = map[roachpb.NodeID]struct{}{}
		}
		contrib[i.PeerID][i.NodeID] = struct{}{}
		return nil
	}); err != nil {
		panic(err)
	}

	least := math.MaxInt32
	var leastNode roachpb.NodeID
	for id, m := range contrib {
		count := len(m)
		if nodes.hasNode(id) {
			if count < least {
				least = count
				leastNode = id
			}
		}
	}
	return leastNode
}
<|endoftext|>


<commit_before>package libvirt

import (
	"bytes"
	"fmt"
	"html/template"
	"net"
	"net/url"
	"strings"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"gopkg.in/alexzorin/libvirt-go.v2"
)

type stepCreateDomain struct{}

func (s *stepCreateDomain) Run(state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	lv, err := libvirt.NewVirConnection(config.LibvirtUrl)
	if err != nil {
		err := fmt.Errorf("Error connecting to libvirt: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	defer lv.CloseConnection()
	if lvd, err := lv.LookupDomainByName(config.VMName); err != nil {

		domainXml := bytes.NewBuffer(nil)
		tmpl, err := template.New("domain").Parse(config.DomainXml)
		if err != nil {
			err := fmt.Errorf("Error creating domain: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		u, err := url.Parse(config.ISOUrl)
		if err != nil {
			err := fmt.Errorf("Error parse iso_url: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
		var h string
		var p string

		if strings.Index(u.Host, ":") == -1 {
			h = u.Host
		} else {
			h, p, _ = net.SplitHostPort(u.Host)
		}
		if p == "" {
			switch u.Scheme {
			case "https":
				p = "443"
			case "http":
				p = "80"
			}
		}

		data := struct {
			VMName      string
			DiskName    string
			DiskType    string
			PoolName    string
			MemorySize  uint
			ISOUrlProto string
			ISOUrlPath  string
			ISOUrlHost  string
			ISOUrlPort  string
			SSHPort     string
		}{
			config.VMName,
			config.DiskName,
			"raw",
			config.PoolName,
			config.MemorySize,
			u.Scheme,
			u.Path,
			h,
			p,
			"2022",
		}
		err = tmpl.Execute(domainXml, data)
		if err != nil {
			err := fmt.Errorf("Error creating domain: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		ui.Say(fmt.Sprintf("domain config:\n%s", domainXml.Bytes()))

		lvd, err = lv.DomainCreateXML(string(domainXml.Bytes()), 0)
		if err != nil {
			err := fmt.Errorf("Error creating domain: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	} else {
		defer lvd.Free()
	}
	return multistep.ActionContinue
}

func (s *stepCreateDomain) Cleanup(state multistep.StateBag) {

}
<commit_msg>fix<commit_after>package libvirt

import (
	"bytes"
	"fmt"
	"html/template"
	"net"
	"net/url"
	"strings"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"gopkg.in/alexzorin/libvirt-go.v2"
)

type stepCreateDomain struct{}

func (s *stepCreateDomain) Run(state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	ui := state.Get("ui").(packer.Ui)

	lv, err := libvirt.NewVirConnection(config.LibvirtUrl)
	if err != nil {
		err := fmt.Errorf("Error connecting to libvirt: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	defer lv.CloseConnection()
	if lvd, err := lv.LookupDomainByName(config.VMName); err == nil {
		err = lvd.Destroy()
		if err != nil {
			err := fmt.Errorf("Error domain already running")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	domainXml := bytes.NewBuffer(nil)
	tmpl, err := template.New("domain").Parse(config.DomainXml)
	if err != nil {
		err := fmt.Errorf("Error creating domain: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	u, err := url.Parse(config.ISOUrl)
	if err != nil {
		err := fmt.Errorf("Error parse iso_url: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	var h string
	var p string

	if strings.Index(u.Host, ":") == -1 {
		h = u.Host
	} else {
		h, p, _ = net.SplitHostPort(u.Host)
	}
	if p == "" {
		switch u.Scheme {
		case "https":
			p = "443"
		case "http":
			p = "80"
		}
	}

	data := struct {
		VMName      string
		DiskName    string
		DiskType    string
		PoolName    string
		MemorySize  uint
		ISOUrlProto string
		ISOUrlPath  string
		ISOUrlHost  string
		ISOUrlPort  string
		SSHPort     string
	}{
		config.VMName,
		config.DiskName,
		"raw",
		config.PoolName,
		config.MemorySize,
		u.Scheme,
		u.Path,
		h,
		p,
		"2022",
	}
	err = tmpl.Execute(domainXml, data)
	if err != nil {
		err := fmt.Errorf("Error creating domain: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say(fmt.Sprintf("domain config:\n%s", domainXml.Bytes()))

	lvd, err := lv.DomainCreateXML(string(domainXml.Bytes()), 0)
	if err != nil {
		err := fmt.Errorf("Error creating domain: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	defer lvd.Free()

	return multistep.ActionContinue
}

func (s *stepCreateDomain) Cleanup(state multistep.StateBag) {

}
<|endoftext|>


<commit_before>package stringUtils

import "testing"

func TestAbbreviate(t *testing.T) {
	if Abbreviate("", 4) != "" {
		t.Errorf("fail test abbreviate 1")
	}
	if Abbreviate("abcdefg", 6) != "abc..." {
		t.Errorf("fail test abbreviate 2")
	}
	if Abbreviate("abcdefg", 7) != "abcdefg" {
		t.Errorf("fail test abbreviate 2")
	}
	if Abbreviate("abcdefg", 8) != "abcdefg" {
		t.Errorf("fail test abbreviate 3")
	}
	if Abbreviate("abcdefg", 4) != "a..." {
		t.Errorf("fail test abbreviate 4")
	}
}

func TestAbbreviateWithOffset(t *testing.T) {
	if AbbreviateWithOffset("", 0, 4) != "" {
		t.Errorf("fail test abbreviate 1")
	}
	if AbbreviateWithOffset("abcdefghijklmno", -1, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 2")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 0, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 3")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 1, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 4")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 4, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 5")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 5, 10) != "...fghi..." {
		t.Errorf("fail test abbreviate 5")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 6, 10) != "...ghij..." {
		t.Errorf("fail test abbreviate 6")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 8, 10) != "...ijklmno" {
		t.Errorf("fail test abbreviate 7")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 10, 10) != "...ijklmno" {
		t.Errorf("fail test abbreviate 8")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 12, 10) != "...ijklmno" {
		t.Errorf("fail test abbreviate 9")
	}
	if AbbreviateWithOffset("abcdefghij", 0, 3) != "abcdefghij" {
		t.Errorf("fail test abbreviate 10")
	}
	if AbbreviateWithOffset("abcdefghij", 5, 6) != "abcdefghij" {
		t.Errorf("fail test abbreviate 11")
	}
}

func TestAppendIfMissing(t *testing.T) {
	if AppendIfMissing("abc", "") != "abc" {
		t.Errorf("fail test AppendIfMissing 1")
	}
	if AppendIfMissing("", "xyz") != "" {
		t.Errorf("fail test AppendIfMissing 2")
	}
	if AppendIfMissing("abc", "xyz") != "abcxyz" {
		t.Errorf("fail test AppendIfMissing 3")
	}
	if AppendIfMissing("abcxyz", "xyz") != "abcxyz" {
		t.Errorf("fail test AppendIfMissing 4")
	}
	if AppendIfMissing("abcXYZ", "xyz") != "abcXYZxyz" {
		t.Errorf("fail test AppendIfMissing 5")
	}
}

func TestCapitalize(t *testing.T) {
	if Capitalize("") != "" {
		t.Errorf("fail test capitalize 1")
	}
	if Capitalize("cat") != "Cat" {
		t.Errorf("fail test capitalize 2")
	}
	if Capitalize("cAt") != "CAt" {
		t.Errorf("fail test capitalize 3")
	}
}

func TestChomp(t *testing.T) {
	if Chomp("") != "" {
		t.Errorf("fail test chomp 1")
	}
	if Chomp("abc \r") != "abc " {
		t.Errorf("fail test chomp 2")
	}
	if Chomp("abc\n") != "abc" {
		t.Errorf("fail test chomp 3")
	}
	if Chomp("abc\r\n") != "abc" {
		t.Errorf("fail test chomp 4")
	}
	if Chomp("abc\r\n\r\n") != "abc\r\n" {
		t.Errorf("fail test chomp 5")
	}
	if Chomp("abc\n\r") != "abc\n" {
		t.Errorf("fail test chomp 6")
	}
	if Chomp("abc\n\rabc") != "abc\n\rabc" {
		t.Errorf("fail test chomp 7")
	}
	if Chomp("\r") != "" {
		t.Errorf("fail test chomp 8")
	}
	if Chomp("\n") != "" {
		t.Errorf("fail test chomp 9")
	}
	if Chomp("\r\n") != "" {
		t.Errorf("fail test chomp 10")
	}
}

func TestChop(t *testing.T) {
	if Chop("") != "" {
		t.Errorf("fail test chop 1")
	}
	if Chop("abc \r") != "abc " {
		t.Errorf("fail test chop 2")
	}
	if Chop("abc\n") != "abc" {
		t.Errorf("fail test chop 3")
	}
	if Chop("abc\r\n") != "abc" {
		t.Errorf("fail test chop 4")
	}
	if Chop("abc\r\n\r\n") != "abc\r\n" {
		t.Errorf("fail test chop 5")
	}
	if Chop("abc\n\r") != "abc\n" {
		t.Errorf("fail test chop 6")
	}
	if Chop("abc\n\rabc") != "abc\n\rab" {
		t.Errorf("fail test chop 7")
	}
	if Chop("\r") != "" {
		t.Errorf("fail test chop 8")
	}
	if Chop("\n") != "" {
		t.Errorf("fail test chop 9")
	}
	if Chop("\r\n") != "" {
		t.Errorf("fail test chop 10")
	}
}

func TestIsAllLowerCase(t *testing.T) {
	if IsAllLowerCase("") != false {
		t.Errorf("fail test IsAllLowerCase 1")
	}
	if IsAllLowerCase("  ") != false {
		t.Errorf("fail test IsAllLowerCase 2")
	}
	if IsAllLowerCase("abc") != true {
		t.Errorf("fail test IsAllLowerCase 3")
	}
	if IsAllLowerCase("abC") != false {
		t.Errorf("fail test IsAllLowerCase 4")
	}
	if IsAllLowerCase("ab c") != false {
		t.Errorf("fail test IsAllLowerCase 5")
	}
	if IsAllLowerCase("ab1c") != false {
		t.Errorf("fail test IsAllLowerCase 6")
	}
	if IsAllLowerCase("ab/c") != false {
		t.Errorf("fail test IsAllLowerCase 7")
	}
}

func TestIsAllUpperCase(t *testing.T) {
	if IsAllUpperCase("") != false {
		t.Errorf("fail test IsAllUpperCase 1")
	}
	if IsAllUpperCase("  ") != false {
		t.Errorf("fail test IsAllUpperCase 2")
	}
	if IsAllUpperCase("ABC") != true {
		t.Errorf("fail test IsAllUpperCase 3")
	}
	if IsAllUpperCase("aBC") != false {
		t.Errorf("fail test IsAllUpperCase 4")
	}
	if IsAllUpperCase("A C") != false {
		t.Errorf("fail test IsAllUpperCase 5")
	}
	if IsAllUpperCase("A1C") != false {
		t.Errorf("fail test IsAllUpperCase 6")
	}
	if IsAllUpperCase("A/C") != false {
		t.Errorf("fail test IsAllUpperCase 7")
	}
}

func TestOverlay(t *testing.T) {
	if Overlay("", "abc", 0, 0) != "abc" {
		t.Errorf("fail test Overlay 1")
	}
	if Overlay("abcdef", "", 2, 4) != "abef" {
		t.Errorf("fail test Overlay 2")
	}
	if Overlay("abcdef", "", 4, 2) != "abef" {
		t.Errorf("fail test Overlay 3")
	}
	if Overlay("abcdef", "zzzz", 2, 4) != "abzzzzef" {
		t.Errorf("fail test Overlay 4")
	}
	if Overlay("abcdef", "zzzz", 4, 2) != "abzzzzef" {
		t.Errorf("fail test Overlay 5")
	}
	if Overlay("abcdef", "zzzz", -1, 4) != "zzzzef" {
		t.Errorf("fail test Overlay 6")
	}
	if Overlay("abcdef", "zzzz", 2, 8) != "abzzzz" {
		t.Errorf("fail test Overlay 7")
	}
	if Overlay("abcdef", "zzzz", -2, -3) != "zzzzabcdef" {
		t.Errorf("fail test Overlay 8")
	}
	if Overlay("abcdef", "zzzz", 8, 10) != "abcdefzzzz" {
		t.Errorf("fail test Overlay 9")
	}
}

func TestRemove(t *testing.T) {
	if Remove("", "abc") != "" {
		t.Errorf("fail test Remove 1")
	}
	if Remove("queued", "u") != "qeed" {
		t.Errorf("fail test Remove 2")
	}
	if Remove("queued", "z") != "queued" {
		t.Errorf("fail test Remove 3")
	}
}

func TestRepeat(t *testing.T) {
	if Repeat("a", 3) != "aaa" {
		t.Errorf("fail test Repeat 1")
	}
	if Repeat("abc", 3) != "abcabcabc" {
		t.Errorf("fail test Repeat 2")
	}
}

func TestRepeatWithSeparator(t *testing.T) {
	if RepeatWithSeparator("a", ",", 3) != "a,a,a" {
		t.Errorf("fail test RepeatWithSeparator 1")
	}
	if RepeatWithSeparator("abc", "-", 3) != "abc-abc-abc" {
		t.Errorf("fail test RepeatWithSeparator 2")
	}
}

func TestStrip(t *testing.T) {
	if Strip("   abc   ") != "abc" {
		t.Errorf("fail test Strip 1")
	}
	if Strip("  . a  bc :    ") != ". a  bc :" {
		t.Errorf("fail test Strip 2")
	}
	if Strip("abc") != "abc" {
		t.Errorf("fail test Strip 3")
	}
}

func TestSubstrings(t *testing.T) {
	if SubstringBefore("abc.def.ghi", ".") != "abc" {
		t.Errorf("fail test SubstringBefore 1")
	}
	if SubstringBefore("abc.def", "g") != "abc.def" {
		t.Errorf("fail test SubstringBefore 2")
	}
	if SubstringBeforeLast("abc.def.ghi", ".") != "abc.def" {
		t.Errorf("fail test SubstringBeforeLast 1")
	}
	if SubstringAfter("abc.def.ghi", ".") != "def.ghi" {
		t.Errorf("fail test SubstringAfter 1")
	}
	if SubstringAfter("abc.def", "g") != "abc.def" {
		t.Errorf("fail test SubstringAfter 2")
	}
	if SubstringAfterLast("abc.def.ghi", ".") != "ghi" {
		t.Errorf("fail test SubstringAfterLast 1")
	}
}

func TestSwapCase(t *testing.T) {
	if SwapCase("abc.def") != "ABC.DEF" {
		t.Errorf("fail test SwapCase 1")
	}
	if SwapCase("aBc.dEf") != "AbC.DeF" {
		t.Errorf("fail test SwapCase 2")
	}
	if SwapCase(" ABC.def ") != " abc.DEF " {
		t.Errorf("fail test SwapCase 3")
	}
}

func TestUncapitalize(t *testing.T) {
	if Uncapitalize("AbcDef") != "abcDef" {
		t.Errorf("fail test Uncapitalize 1")
	}
	if Uncapitalize("abcDef") != "abcDef" {
		t.Errorf("fail test Uncapitalize 2")
	}
	if Uncapitalize("世界") != "世界" {
		t.Errorf("fail test Uncapitalize 3")
	}
}
<commit_msg>test: AppendIfMissingIgnoreCase<commit_after>package stringUtils

import "testing"

func TestAbbreviate(t *testing.T) {
	if Abbreviate("", 4) != "" {
		t.Errorf("fail test abbreviate 1")
	}
	if Abbreviate("abcdefg", 6) != "abc..." {
		t.Errorf("fail test abbreviate 2")
	}
	if Abbreviate("abcdefg", 7) != "abcdefg" {
		t.Errorf("fail test abbreviate 2")
	}
	if Abbreviate("abcdefg", 8) != "abcdefg" {
		t.Errorf("fail test abbreviate 3")
	}
	if Abbreviate("abcdefg", 4) != "a..." {
		t.Errorf("fail test abbreviate 4")
	}
}

func TestAbbreviateWithOffset(t *testing.T) {
	if AbbreviateWithOffset("", 0, 4) != "" {
		t.Errorf("fail test abbreviate 1")
	}
	if AbbreviateWithOffset("abcdefghijklmno", -1, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 2")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 0, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 3")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 1, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 4")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 4, 10) != "abcdefg..." {
		t.Errorf("fail test abbreviate 5")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 5, 10) != "...fghi..." {
		t.Errorf("fail test abbreviate 5")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 6, 10) != "...ghij..." {
		t.Errorf("fail test abbreviate 6")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 8, 10) != "...ijklmno" {
		t.Errorf("fail test abbreviate 7")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 10, 10) != "...ijklmno" {
		t.Errorf("fail test abbreviate 8")
	}
	if AbbreviateWithOffset("abcdefghijklmno", 12, 10) != "...ijklmno" {
		t.Errorf("fail test abbreviate 9")
	}
	if AbbreviateWithOffset("abcdefghij", 0, 3) != "abcdefghij" {
		t.Errorf("fail test abbreviate 10")
	}
	if AbbreviateWithOffset("abcdefghij", 5, 6) != "abcdefghij" {
		t.Errorf("fail test abbreviate 11")
	}
}

func TestAppendIfMissing(t *testing.T) {
	if AppendIfMissing("abc", "") != "abc" {
		t.Errorf("fail test AppendIfMissing 1")
	}
	if AppendIfMissing("", "xyz") != "" {
		t.Errorf("fail test AppendIfMissing 2")
	}
	if AppendIfMissing("abc", "xyz") != "abcxyz" {
		t.Errorf("fail test AppendIfMissing 3")
	}
	if AppendIfMissing("abcxyz", "xyz") != "abcxyz" {
		t.Errorf("fail test AppendIfMissing 4")
	}
	if AppendIfMissing("abcXYZ", "xyz") != "abcXYZxyz" {
		t.Errorf("fail test AppendIfMissing 5")
	}
}

func TestAppendIfMissingIgnoreCase(t *testing.T) {
	if AppendIfMissingIgnoreCase("abc", "") != "abc" {
		t.Errorf("fail test AppendIfMissingIgnoreCase 1")
	}
	if AppendIfMissingIgnoreCase("", "xyz") != "" {
		t.Errorf("fail test AppendIfMissingIgnoreCase 2")
	}
	if AppendIfMissingIgnoreCase("abc", "xyz") != "abcxyz" {
		t.Errorf("fail test AppendIfMissingIgnoreCase 3")
	}
	if AppendIfMissingIgnoreCase("abcxyz", "xyz") != "abcxyz" {
		t.Errorf("fail test AppendIfMissingIgnoreCase 4")
	}
	if AppendIfMissingIgnoreCase("abcXYZ", "xyz") != "abcXYZ" {
		t.Errorf("fail test AppendIfMissingIgnoreCase 5")
	}
}

func TestCapitalize(t *testing.T) {
	if Capitalize("") != "" {
		t.Errorf("fail test capitalize 1")
	}
	if Capitalize("cat") != "Cat" {
		t.Errorf("fail test capitalize 2")
	}
	if Capitalize("cAt") != "CAt" {
		t.Errorf("fail test capitalize 3")
	}
}

func TestChomp(t *testing.T) {
	if Chomp("") != "" {
		t.Errorf("fail test chomp 1")
	}
	if Chomp("abc \r") != "abc " {
		t.Errorf("fail test chomp 2")
	}
	if Chomp("abc\n") != "abc" {
		t.Errorf("fail test chomp 3")
	}
	if Chomp("abc\r\n") != "abc" {
		t.Errorf("fail test chomp 4")
	}
	if Chomp("abc\r\n\r\n") != "abc\r\n" {
		t.Errorf("fail test chomp 5")
	}
	if Chomp("abc\n\r") != "abc\n" {
		t.Errorf("fail test chomp 6")
	}
	if Chomp("abc\n\rabc") != "abc\n\rabc" {
		t.Errorf("fail test chomp 7")
	}
	if Chomp("\r") != "" {
		t.Errorf("fail test chomp 8")
	}
	if Chomp("\n") != "" {
		t.Errorf("fail test chomp 9")
	}
	if Chomp("\r\n") != "" {
		t.Errorf("fail test chomp 10")
	}
}

func TestChop(t *testing.T) {
	if Chop("") != "" {
		t.Errorf("fail test chop 1")
	}
	if Chop("abc \r") != "abc " {
		t.Errorf("fail test chop 2")
	}
	if Chop("abc\n") != "abc" {
		t.Errorf("fail test chop 3")
	}
	if Chop("abc\r\n") != "abc" {
		t.Errorf("fail test chop 4")
	}
	if Chop("abc\r\n\r\n") != "abc\r\n" {
		t.Errorf("fail test chop 5")
	}
	if Chop("abc\n\r") != "abc\n" {
		t.Errorf("fail test chop 6")
	}
	if Chop("abc\n\rabc") != "abc\n\rab" {
		t.Errorf("fail test chop 7")
	}
	if Chop("\r") != "" {
		t.Errorf("fail test chop 8")
	}
	if Chop("\n") != "" {
		t.Errorf("fail test chop 9")
	}
	if Chop("\r\n") != "" {
		t.Errorf("fail test chop 10")
	}
}

func TestIsAllLowerCase(t *testing.T) {
	if IsAllLowerCase("") != false {
		t.Errorf("fail test IsAllLowerCase 1")
	}
	if IsAllLowerCase("  ") != false {
		t.Errorf("fail test IsAllLowerCase 2")
	}
	if IsAllLowerCase("abc") != true {
		t.Errorf("fail test IsAllLowerCase 3")
	}
	if IsAllLowerCase("abC") != false {
		t.Errorf("fail test IsAllLowerCase 4")
	}
	if IsAllLowerCase("ab c") != false {
		t.Errorf("fail test IsAllLowerCase 5")
	}
	if IsAllLowerCase("ab1c") != false {
		t.Errorf("fail test IsAllLowerCase 6")
	}
	if IsAllLowerCase("ab/c") != false {
		t.Errorf("fail test IsAllLowerCase 7")
	}
}

func TestIsAllUpperCase(t *testing.T) {
	if IsAllUpperCase("") != false {
		t.Errorf("fail test IsAllUpperCase 1")
	}
	if IsAllUpperCase("  ") != false {
		t.Errorf("fail test IsAllUpperCase 2")
	}
	if IsAllUpperCase("ABC") != true {
		t.Errorf("fail test IsAllUpperCase 3")
	}
	if IsAllUpperCase("aBC") != false {
		t.Errorf("fail test IsAllUpperCase 4")
	}
	if IsAllUpperCase("A C") != false {
		t.Errorf("fail test IsAllUpperCase 5")
	}
	if IsAllUpperCase("A1C") != false {
		t.Errorf("fail test IsAllUpperCase 6")
	}
	if IsAllUpperCase("A/C") != false {
		t.Errorf("fail test IsAllUpperCase 7")
	}
}

func TestOverlay(t *testing.T) {
	if Overlay("", "abc", 0, 0) != "abc" {
		t.Errorf("fail test Overlay 1")
	}
	if Overlay("abcdef", "", 2, 4) != "abef" {
		t.Errorf("fail test Overlay 2")
	}
	if Overlay("abcdef", "", 4, 2) != "abef" {
		t.Errorf("fail test Overlay 3")
	}
	if Overlay("abcdef", "zzzz", 2, 4) != "abzzzzef" {
		t.Errorf("fail test Overlay 4")
	}
	if Overlay("abcdef", "zzzz", 4, 2) != "abzzzzef" {
		t.Errorf("fail test Overlay 5")
	}
	if Overlay("abcdef", "zzzz", -1, 4) != "zzzzef" {
		t.Errorf("fail test Overlay 6")
	}
	if Overlay("abcdef", "zzzz", 2, 8) != "abzzzz" {
		t.Errorf("fail test Overlay 7")
	}
	if Overlay("abcdef", "zzzz", -2, -3) != "zzzzabcdef" {
		t.Errorf("fail test Overlay 8")
	}
	if Overlay("abcdef", "zzzz", 8, 10) != "abcdefzzzz" {
		t.Errorf("fail test Overlay 9")
	}
}

func TestRemove(t *testing.T) {
	if Remove("", "abc") != "" {
		t.Errorf("fail test Remove 1")
	}
	if Remove("queued", "u") != "qeed" {
		t.Errorf("fail test Remove 2")
	}
	if Remove("queued", "z") != "queued" {
		t.Errorf("fail test Remove 3")
	}
}

func TestRepeat(t *testing.T) {
	if Repeat("a", 3) != "aaa" {
		t.Errorf("fail test Repeat 1")
	}
	if Repeat("abc", 3) != "abcabcabc" {
		t.Errorf("fail test Repeat 2")
	}
}

func TestRepeatWithSeparator(t *testing.T) {
	if RepeatWithSeparator("a", ",", 3) != "a,a,a" {
		t.Errorf("fail test RepeatWithSeparator 1")
	}
	if RepeatWithSeparator("abc", "-", 3) != "abc-abc-abc" {
		t.Errorf("fail test RepeatWithSeparator 2")
	}
}

func TestStrip(t *testing.T) {
	if Strip("   abc   ") != "abc" {
		t.Errorf("fail test Strip 1")
	}
	if Strip("  . a  bc :    ") != ". a  bc :" {
		t.Errorf("fail test Strip 2")
	}
	if Strip("abc") != "abc" {
		t.Errorf("fail test Strip 3")
	}
}

func TestSubstrings(t *testing.T) {
	if SubstringBefore("abc.def.ghi", ".") != "abc" {
		t.Errorf("fail test SubstringBefore 1")
	}
	if SubstringBefore("abc.def", "g") != "abc.def" {
		t.Errorf("fail test SubstringBefore 2")
	}
	if SubstringBeforeLast("abc.def.ghi", ".") != "abc.def" {
		t.Errorf("fail test SubstringBeforeLast 1")
	}
	if SubstringAfter("abc.def.ghi", ".") != "def.ghi" {
		t.Errorf("fail test SubstringAfter 1")
	}
	if SubstringAfter("abc.def", "g") != "abc.def" {
		t.Errorf("fail test SubstringAfter 2")
	}
	if SubstringAfterLast("abc.def.ghi", ".") != "ghi" {
		t.Errorf("fail test SubstringAfterLast 1")
	}
}

func TestSwapCase(t *testing.T) {
	if SwapCase("abc.def") != "ABC.DEF" {
		t.Errorf("fail test SwapCase 1")
	}
	if SwapCase("aBc.dEf") != "AbC.DeF" {
		t.Errorf("fail test SwapCase 2")
	}
	if SwapCase(" ABC.def ") != " abc.DEF " {
		t.Errorf("fail test SwapCase 3")
	}
}

func TestUncapitalize(t *testing.T) {
	if Uncapitalize("AbcDef") != "abcDef" {
		t.Errorf("fail test Uncapitalize 1")
	}
	if Uncapitalize("abcDef") != "abcDef" {
		t.Errorf("fail test Uncapitalize 2")
	}
	if Uncapitalize("世界") != "世界" {
		t.Errorf("fail test Uncapitalize 3")
	}
}
<|endoftext|>


<commit_before>package container

import (
	"fmt"
	"sync"

	"github.com/docker/cli/cli/command/formatter"
	"github.com/docker/docker/pkg/stringid"
	units "github.com/docker/go-units"
)

const (
	winOSType                  = "windows"
	defaultStatsTableFormat    = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
	winDefaultStatsTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"

	containerHeader = "CONTAINER"
	cpuPercHeader   = "CPU %"
	netIOHeader     = "NET I/O"
	blockIOHeader   = "BLOCK I/O"
	memPercHeader   = "MEM %"             // Used only on Linux
	winMemUseHeader = "PRIV WORKING SET"  // Used only on Windows
	memUseHeader    = "MEM USAGE / LIMIT" // Used only on Linux
	pidsHeader      = "PIDS"              // Used only on Linux
)

// StatsEntry represents represents the statistics data collected from a container
type StatsEntry struct {
	Container        string
	Name             string
	ID               string
	CPUPercentage    float64
	Memory           float64 // On Windows this is the private working set
	MemoryLimit      float64 // Not used on Windows
	MemoryPercentage float64 // Not used on Windows
	NetworkRx        float64
	NetworkTx        float64
	BlockRead        float64
	BlockWrite       float64
	PidsCurrent      uint64 // Not used on Windows
	IsInvalid        bool
}

// Stats represents an entity to store containers statistics synchronously
type Stats struct {
	mutex sync.Mutex
	StatsEntry
	err error
}

// GetError returns the container statistics error.
// This is used to determine whether the statistics are valid or not
func (cs *Stats) GetError() error {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	return cs.err
}

// SetErrorAndReset zeroes all the container statistics and store the error.
// It is used when receiving time out error during statistics collecting to reduce lock overhead
func (cs *Stats) SetErrorAndReset(err error) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	cs.CPUPercentage = 0
	cs.Memory = 0
	cs.MemoryPercentage = 0
	cs.MemoryLimit = 0
	cs.NetworkRx = 0
	cs.NetworkTx = 0
	cs.BlockRead = 0
	cs.BlockWrite = 0
	cs.PidsCurrent = 0
	cs.err = err
	cs.IsInvalid = true
}

// SetError sets container statistics error
func (cs *Stats) SetError(err error) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	cs.err = err
	if err != nil {
		cs.IsInvalid = true
	}
}

// SetStatistics set the container statistics
func (cs *Stats) SetStatistics(s StatsEntry) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	s.Container = cs.Container
	cs.StatsEntry = s
}

// GetStatistics returns container statistics with other meta data such as the container name
func (cs *Stats) GetStatistics() StatsEntry {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	return cs.StatsEntry
}

// NewStatsFormat returns a format for rendering an CStatsContext
func NewStatsFormat(source, osType string) formatter.Format {
	if source == formatter.TableFormatKey {
		if osType == winOSType {
			return winDefaultStatsTableFormat
		}
		return defaultStatsTableFormat
	}
	return formatter.Format(source)
}

// NewStats returns a new Stats entity and sets in it the given name
func NewStats(container string) *Stats {
	return &Stats{StatsEntry: StatsEntry{Container: container}}
}

// statsFormatWrite renders the context for a list of containers statistics
func statsFormatWrite(ctx formatter.Context, Stats []StatsEntry, osType string, trunc bool) error {
	render := func(format func(subContext formatter.SubContext) error) error {
		for _, cstats := range Stats {
			statsCtx := &statsContext{
				s:     cstats,
				os:    osType,
				trunc: trunc,
			}
			if err := format(statsCtx); err != nil {
				return err
			}
		}
		return nil
	}
	memUsage := memUseHeader
	if osType == winOSType {
		memUsage = winMemUseHeader
	}
	statsCtx := statsContext{}
	statsCtx.Header = formatter.SubHeaderContext{
		"Container": containerHeader,
		"Name":      formatter.NameHeader,
		"ID":        formatter.ContainerIDHeader,
		"CPUPerc":   cpuPercHeader,
		"MemUsage":  memUsage,
		"MemPerc":   memPercHeader,
		"NetIO":     netIOHeader,
		"BlockIO":   blockIOHeader,
		"PIDs":      pidsHeader,
	}
	statsCtx.os = osType
	return ctx.Write(&statsCtx, render)
}

type statsContext struct {
	formatter.HeaderContext
	s     StatsEntry
	os    string
	trunc bool
}

func (c *statsContext) MarshalJSON() ([]byte, error) {
	return formatter.MarshalJSON(c)
}

func (c *statsContext) Container() string {
	return c.s.Container
}

func (c *statsContext) Name() string {
	if len(c.s.Name) > 1 {
		return c.s.Name[1:]
	}
	return "--"
}

func (c *statsContext) ID() string {
	if c.trunc {
		return stringid.TruncateID(c.s.ID)
	}
	return c.s.ID
}

func (c *statsContext) CPUPerc() string {
	if c.s.IsInvalid {
		return "--"
	}
	return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
}

func (c *statsContext) MemUsage() string {
	if c.s.IsInvalid {
		return "-- / --"
	}
	if c.os == winOSType {
		return units.BytesSize(c.s.Memory)
	}
	return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
}

func (c *statsContext) MemPerc() string {
	if c.s.IsInvalid || c.os == winOSType {
		return "--"
	}
	return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
}

func (c *statsContext) NetIO() string {
	if c.s.IsInvalid {
		return "--"
	}
	return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
}

func (c *statsContext) BlockIO() string {
	if c.s.IsInvalid {
		return "--"
	}
	return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
}

func (c *statsContext) PIDs() string {
	if c.s.IsInvalid || c.os == winOSType {
		return "--"
	}
	return fmt.Sprintf("%d", c.s.PidsCurrent)
}
<commit_msg>cli/command/container: some small performance optimizations for formatting stats<commit_after>package container

import (
	"strconv"
	"sync"

	"github.com/docker/cli/cli/command/formatter"
	"github.com/docker/docker/pkg/stringid"
	units "github.com/docker/go-units"
)

const (
	winOSType                  = "windows"
	defaultStatsTableFormat    = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
	winDefaultStatsTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"

	containerHeader = "CONTAINER"
	cpuPercHeader   = "CPU %"
	netIOHeader     = "NET I/O"
	blockIOHeader   = "BLOCK I/O"
	memPercHeader   = "MEM %"             // Used only on Linux
	winMemUseHeader = "PRIV WORKING SET"  // Used only on Windows
	memUseHeader    = "MEM USAGE / LIMIT" // Used only on Linux
	pidsHeader      = "PIDS"              // Used only on Linux
)

// StatsEntry represents represents the statistics data collected from a container
type StatsEntry struct {
	Container        string
	Name             string
	ID               string
	CPUPercentage    float64
	Memory           float64 // On Windows this is the private working set
	MemoryLimit      float64 // Not used on Windows
	MemoryPercentage float64 // Not used on Windows
	NetworkRx        float64
	NetworkTx        float64
	BlockRead        float64
	BlockWrite       float64
	PidsCurrent      uint64 // Not used on Windows
	IsInvalid        bool
}

// Stats represents an entity to store containers statistics synchronously
type Stats struct {
	mutex sync.Mutex
	StatsEntry
	err error
}

// GetError returns the container statistics error.
// This is used to determine whether the statistics are valid or not
func (cs *Stats) GetError() error {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	return cs.err
}

// SetErrorAndReset zeroes all the container statistics and store the error.
// It is used when receiving time out error during statistics collecting to reduce lock overhead
func (cs *Stats) SetErrorAndReset(err error) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	cs.CPUPercentage = 0
	cs.Memory = 0
	cs.MemoryPercentage = 0
	cs.MemoryLimit = 0
	cs.NetworkRx = 0
	cs.NetworkTx = 0
	cs.BlockRead = 0
	cs.BlockWrite = 0
	cs.PidsCurrent = 0
	cs.err = err
	cs.IsInvalid = true
}

// SetError sets container statistics error
func (cs *Stats) SetError(err error) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	cs.err = err
	if err != nil {
		cs.IsInvalid = true
	}
}

// SetStatistics set the container statistics
func (cs *Stats) SetStatistics(s StatsEntry) {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	s.Container = cs.Container
	cs.StatsEntry = s
}

// GetStatistics returns container statistics with other meta data such as the container name
func (cs *Stats) GetStatistics() StatsEntry {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()
	return cs.StatsEntry
}

// NewStatsFormat returns a format for rendering an CStatsContext
func NewStatsFormat(source, osType string) formatter.Format {
	if source == formatter.TableFormatKey {
		if osType == winOSType {
			return winDefaultStatsTableFormat
		}
		return defaultStatsTableFormat
	}
	return formatter.Format(source)
}

// NewStats returns a new Stats entity and sets in it the given name
func NewStats(container string) *Stats {
	return &Stats{StatsEntry: StatsEntry{Container: container}}
}

// statsFormatWrite renders the context for a list of containers statistics
func statsFormatWrite(ctx formatter.Context, Stats []StatsEntry, osType string, trunc bool) error {
	render := func(format func(subContext formatter.SubContext) error) error {
		for _, cstats := range Stats {
			statsCtx := &statsContext{
				s:     cstats,
				os:    osType,
				trunc: trunc,
			}
			if err := format(statsCtx); err != nil {
				return err
			}
		}
		return nil
	}
	memUsage := memUseHeader
	if osType == winOSType {
		memUsage = winMemUseHeader
	}
	statsCtx := statsContext{}
	statsCtx.Header = formatter.SubHeaderContext{
		"Container": containerHeader,
		"Name":      formatter.NameHeader,
		"ID":        formatter.ContainerIDHeader,
		"CPUPerc":   cpuPercHeader,
		"MemUsage":  memUsage,
		"MemPerc":   memPercHeader,
		"NetIO":     netIOHeader,
		"BlockIO":   blockIOHeader,
		"PIDs":      pidsHeader,
	}
	statsCtx.os = osType
	return ctx.Write(&statsCtx, render)
}

type statsContext struct {
	formatter.HeaderContext
	s     StatsEntry
	os    string
	trunc bool
}

func (c *statsContext) MarshalJSON() ([]byte, error) {
	return formatter.MarshalJSON(c)
}

func (c *statsContext) Container() string {
	return c.s.Container
}

func (c *statsContext) Name() string {
	if len(c.s.Name) > 1 {
		return c.s.Name[1:]
	}
	return "--"
}

func (c *statsContext) ID() string {
	if c.trunc {
		return stringid.TruncateID(c.s.ID)
	}
	return c.s.ID
}

func (c *statsContext) CPUPerc() string {
	if c.s.IsInvalid {
		return "--"
	}
	return formatPercentage(c.s.CPUPercentage)
}

func (c *statsContext) MemUsage() string {
	if c.s.IsInvalid {
		return "-- / --"
	}
	if c.os == winOSType {
		return units.BytesSize(c.s.Memory)
	}
	return units.BytesSize(c.s.Memory) + " / " + units.BytesSize(c.s.MemoryLimit)
}

func (c *statsContext) MemPerc() string {
	if c.s.IsInvalid || c.os == winOSType {
		return "--"
	}
	return formatPercentage(c.s.MemoryPercentage)
}

func (c *statsContext) NetIO() string {
	if c.s.IsInvalid {
		return "--"
	}
	return units.HumanSizeWithPrecision(c.s.NetworkRx, 3) + " / " + units.HumanSizeWithPrecision(c.s.NetworkTx, 3)
}

func (c *statsContext) BlockIO() string {
	if c.s.IsInvalid {
		return "--"
	}
	return units.HumanSizeWithPrecision(c.s.BlockRead, 3) + " / " + units.HumanSizeWithPrecision(c.s.BlockWrite, 3)
}

func (c *statsContext) PIDs() string {
	if c.s.IsInvalid || c.os == winOSType {
		return "--"
	}
	return strconv.FormatUint(c.s.PidsCurrent, 10)
}

func formatPercentage(val float64) string {
	return strconv.FormatFloat(val, 'f', 2, 64) + "%"
}
<|endoftext|>


<commit_before>package store

import (
	"container/list"
	"errors"
	"fmt"
	"github.com/balzaczyy/golucene/core/codec"
	"github.com/balzaczyy/golucene/core/util"
	"sync"
)

// store/CompoundFileWriter.java

type AtomicBool struct {
	*sync.RWMutex
	v bool
}

func NewAtomicBool() *AtomicBool {
	return &AtomicBool{&sync.RWMutex{}, false}
}

func (b *AtomicBool) Get() bool {
	b.RLock()
	defer b.RUnlock()
	return b.v
}

func (b *AtomicBool) CompareAndSet(from, to bool) bool {
	b.Lock()
	defer b.Unlock()
	if b.v == from {
		b.v = to
	}
	return b.v
}

type FileEntry struct {
	file           string    // source file
	length, offset int64     // temporary holder for the start of this file's data section
	dir            Directory // which contains the file.
}

// Combines multiple files into a single compound file
type CompoundFileWriter struct {
	sync.Locker
	directory Directory
	entries   map[string]*FileEntry
	seenIDs   map[string]bool
	// all entries that are written to a sep. file but not yet moved into CFS
	pendingEntries *list.List
	closed         bool
	dataOut        IndexOutput
	outputTaken    *AtomicBool
	entryTableName string
	dataFileName   string
}

/*
Create the compound stream in the specified file. The filename is the
entire name (no extensions are added).
*/
func newCompoundFileWriter(dir Directory, name string) *CompoundFileWriter {
	assert2(dir != nil, "directory cannot be nil")
	assert2(name != "", "name cannot be empty")
	return &CompoundFileWriter{
		Locker:         &sync.Mutex{},
		directory:      dir,
		entries:        make(map[string]*FileEntry),
		seenIDs:        make(map[string]bool),
		pendingEntries: list.New(),
		outputTaken:    NewAtomicBool(),
		entryTableName: util.SegmentFileName(
			util.StripExtension(name),
			"",
			COMPOUND_FILE_ENTRIES_EXTENSION,
		),
		dataFileName: name,
	}
}

func (w *CompoundFileWriter) output() (IndexOutput, error) {
	w.Lock()
	defer w.Unlock()
	if w.dataOut == nil {
		var success = false
		defer func() {
			if !success {
				util.CloseWhileSuppressingError(w.dataOut)
			}
		}()

		var err error
		w.dataOut, err = w.directory.CreateOutput(w.dataFileName, IO_CONTEXT_DEFAULT)
		if err != nil {
			return nil, err
		}
		err = codec.WriteHeader(w.dataOut, CFD_DATA_CODEC, CFD_VERSION_CURRENT)
		if err != nil {
			return nil, err
		}
		success = true
	}
	return w.dataOut, nil
}

/* Closes all resouces and writes the entry table */
func (w *CompoundFileWriter) Close() (err error) {
	if w.closed {
		return nil
	}

	var priorError error
	var entryTableOut IndexOutput
	// TODO this code should clean up after itself (remove partial .cfs/.cfe)
	func() {
		defer func() {
			err = util.CloseWhileHandlingError(priorError, w.dataOut)
		}()
		assert2(w.pendingEntries.Len() == 0 && !w.outputTaken.Get(),
			"CFS has pending open files")
		w.closed = true
		// open the compound stream
		_, err = w.output()
		if err != nil {
			return
		}
		assert(w.dataOut != nil)
	}()

	defer func() {
		err = util.CloseWhileHandlingError(priorError, entryTableOut)
	}()
	entryTableOut, err = w.directory.CreateOutput(w.entryTableName, IO_CONTEXT_DEFAULT)
	if err == nil {
		err = w.writeEntryTable(w.entries, entryTableOut)
	}
	return
}

func (w *CompoundFileWriter) ensureOpen() {
	assert2(!w.closed, "CFS Directory is already closed")
}

/* Copy the contents of the file with specified extension into the provided output stream. */
func (w *CompoundFileWriter) copyFileEntry(dataOut IndexOutput, fileEntry *FileEntry) (n int64, err error) {
	var is IndexInput
	is, err = fileEntry.dir.OpenInput(fileEntry.file, IO_CONTEXT_READONCE)
	if err != nil {
		return 0, err
	}
	var success = false
	defer func() {
		if success {
			err = util.Close(is)
			// copy successful - delete file
			if err == nil {
				err = fileEntry.dir.DeleteFile(fileEntry.file)
			}
		} else {
			util.CloseWhileSuppressingError(is)
		}
	}()

	startPtr := dataOut.FilePointer()
	length := fileEntry.length
	err = dataOut.CopyBytes(is, length)
	if err != nil {
		return 0, err
	}
	// verify that the output length diff is equal to original file
	endPtr := dataOut.FilePointer()
	diff := endPtr - startPtr
	if diff != length {
		return 0, errors.New(fmt.Sprintf(
			"Difference in the output file offsets %v does not match the original file length %v",
			diff, length))
	}
	fileEntry.offset = startPtr
	success = true
	return length, nil
}

func (w *CompoundFileWriter) writeEntryTable(entries map[string]*FileEntry,
	entryOut IndexOutput) error {
	panic("not implemented yet")
}

func (w *CompoundFileWriter) createOutput(name string, context IOContext) (IndexOutput, error) {
	w.ensureOpen()
	var success = false
	var outputLocked = false
	defer func() {
		if !success {
			delete(w.entries, name)
			if outputLocked { // release the output lock if not successful
				assert(w.outputTaken.Get())
				w.releaseOutputLock()
			}
		}
	}()

	assert2(name != "", "name must not be empty")
	_, ok := w.entries[name]
	assert2(!ok, "File %v already exists", name)
	entry := &FileEntry{}
	entry.file = name
	w.entries[name] = entry
	id := util.StripSegmentName(name)
	_, ok = w.seenIDs[id]
	assert2(!ok, "file='%v' maps to id='%v', which was already written", name, id)
	w.seenIDs[id] = true

	var out *DirectCFSIndexOutput
	if outputLocked := w.outputTaken.CompareAndSet(false, true); outputLocked {
		o, err := w.output()
		if err != nil {
			return nil, err
		}
		out = newDirectCFSIndexOutput(w, o, entry, false)
	} else {
		entry.dir = w.directory
		assert2(!w.directory.FileExists(name), "File %v already exists", name)
		o, err := w.directory.CreateOutput(name, context)
		if err != nil {
			return nil, err
		}
		out = newDirectCFSIndexOutput(w, o, entry, true)
	}
	success = true
	return out, nil
}

func (w *CompoundFileWriter) releaseOutputLock() {
	w.outputTaken.CompareAndSet(true, false)
}

func (w *CompoundFileWriter) prunePendingEntries() error {
	// claim the output and copy all pending files in
	if w.outputTaken.CompareAndSet(false, true) {
		defer func() {
			cas := w.outputTaken.CompareAndSet(true, false)
			assert(cas)
		}()
		for w.pendingEntries.Len() > 0 {
			head := w.pendingEntries.Front()
			w.pendingEntries.Remove(head)
			entry := head.Value.(*FileEntry)
			out, err := w.output()
			if err == nil {
				_, err = w.copyFileEntry(out, entry)
			}
			if err != nil {
				return err
			}
			w.entries[entry.file] = entry
		}
	}
	return nil
}

type DirectCFSIndexOutput struct {
	*IndexOutputImpl
	owner        *CompoundFileWriter
	delegate     IndexOutput
	offset       int64
	closed       bool
	entry        *FileEntry
	writtenBytes int64
	isSeparate   bool
}

func newDirectCFSIndexOutput(owner *CompoundFileWriter,
	delegate IndexOutput, entry *FileEntry, isSeparate bool) *DirectCFSIndexOutput {
	ans := &DirectCFSIndexOutput{
		owner:      owner,
		delegate:   delegate,
		entry:      entry,
		offset:     delegate.FilePointer(),
		isSeparate: isSeparate,
	}
	ans.entry.offset = ans.offset
	ans.IndexOutputImpl = NewIndexOutput(ans)
	return ans
}

func (out *DirectCFSIndexOutput) Flush() error {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) Close() error {
	if out.closed {
		return nil
	}
	out.closed = true
	out.entry.length = out.writtenBytes
	if out.isSeparate {
		err := out.delegate.Close()
		if err != nil {
			return err
		}
		// we are a separate file - push into the pending entries
		out.owner.pendingEntries.PushBack(out.entry)
	} else {
		// we have been written into the CFS directly - release the lock
		out.owner.releaseOutputLock()
	}
	// now prune all pending entries and push them into the CFS
	return out.owner.prunePendingEntries()
}

func (out *DirectCFSIndexOutput) FilePointer() int64 {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) Length() (int64, error) {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) WriteByte(b byte) error {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) WriteBytes(b []byte) error {
	assert(!out.closed)
	out.writtenBytes += int64(len(b))
	return out.delegate.WriteBytes(b)
}
<commit_msg>fix AtomicBool.compareAndSet<commit_after>package store

import (
	"container/list"
	"errors"
	"fmt"
	"github.com/balzaczyy/golucene/core/codec"
	"github.com/balzaczyy/golucene/core/util"
	"sync"
)

// store/CompoundFileWriter.java

type AtomicBool struct {
	*sync.RWMutex
	v bool
}

func NewAtomicBool() *AtomicBool {
	return &AtomicBool{&sync.RWMutex{}, false}
}

func (b *AtomicBool) Get() bool {
	b.RLock()
	defer b.RUnlock()
	return b.v
}

func (b *AtomicBool) CompareAndSet(from, to bool) bool {
	b.Lock()
	defer b.Unlock()
	if b.v == from {
		b.v = to
		return true
	}
	return false
}

type FileEntry struct {
	file           string    // source file
	length, offset int64     // temporary holder for the start of this file's data section
	dir            Directory // which contains the file.
}

// Combines multiple files into a single compound file
type CompoundFileWriter struct {
	sync.Locker
	directory Directory
	entries   map[string]*FileEntry
	seenIDs   map[string]bool
	// all entries that are written to a sep. file but not yet moved into CFS
	pendingEntries *list.List
	closed         bool
	dataOut        IndexOutput
	outputTaken    *AtomicBool
	entryTableName string
	dataFileName   string
}

/*
Create the compound stream in the specified file. The filename is the
entire name (no extensions are added).
*/
func newCompoundFileWriter(dir Directory, name string) *CompoundFileWriter {
	assert2(dir != nil, "directory cannot be nil")
	assert2(name != "", "name cannot be empty")
	return &CompoundFileWriter{
		Locker:         &sync.Mutex{},
		directory:      dir,
		entries:        make(map[string]*FileEntry),
		seenIDs:        make(map[string]bool),
		pendingEntries: list.New(),
		outputTaken:    NewAtomicBool(),
		entryTableName: util.SegmentFileName(
			util.StripExtension(name),
			"",
			COMPOUND_FILE_ENTRIES_EXTENSION,
		),
		dataFileName: name,
	}
}

func (w *CompoundFileWriter) output() (IndexOutput, error) {
	w.Lock()
	defer w.Unlock()
	if w.dataOut == nil {
		var success = false
		defer func() {
			if !success {
				util.CloseWhileSuppressingError(w.dataOut)
			}
		}()

		var err error
		w.dataOut, err = w.directory.CreateOutput(w.dataFileName, IO_CONTEXT_DEFAULT)
		if err != nil {
			return nil, err
		}
		err = codec.WriteHeader(w.dataOut, CFD_DATA_CODEC, CFD_VERSION_CURRENT)
		if err != nil {
			return nil, err
		}
		success = true
	}
	return w.dataOut, nil
}

/* Closes all resouces and writes the entry table */
func (w *CompoundFileWriter) Close() (err error) {
	if w.closed {
		return nil
	}

	var priorError error
	var entryTableOut IndexOutput
	// TODO this code should clean up after itself (remove partial .cfs/.cfe)
	func() {
		defer func() {
			err = util.CloseWhileHandlingError(priorError, w.dataOut)
		}()
		assert2(w.pendingEntries.Len() == 0 && !w.outputTaken.Get(),
			"CFS has pending open files")
		w.closed = true
		// open the compound stream
		_, err = w.output()
		if err != nil {
			return
		}
		assert(w.dataOut != nil)
	}()

	defer func() {
		err = util.CloseWhileHandlingError(priorError, entryTableOut)
	}()
	entryTableOut, err = w.directory.CreateOutput(w.entryTableName, IO_CONTEXT_DEFAULT)
	if err == nil {
		err = w.writeEntryTable(w.entries, entryTableOut)
	}
	return
}

func (w *CompoundFileWriter) ensureOpen() {
	assert2(!w.closed, "CFS Directory is already closed")
}

/* Copy the contents of the file with specified extension into the provided output stream. */
func (w *CompoundFileWriter) copyFileEntry(dataOut IndexOutput, fileEntry *FileEntry) (n int64, err error) {
	var is IndexInput
	is, err = fileEntry.dir.OpenInput(fileEntry.file, IO_CONTEXT_READONCE)
	if err != nil {
		return 0, err
	}
	var success = false
	defer func() {
		if success {
			err = util.Close(is)
			// copy successful - delete file
			if err == nil {
				err = fileEntry.dir.DeleteFile(fileEntry.file)
			}
		} else {
			util.CloseWhileSuppressingError(is)
		}
	}()

	startPtr := dataOut.FilePointer()
	length := fileEntry.length
	err = dataOut.CopyBytes(is, length)
	if err != nil {
		return 0, err
	}
	// verify that the output length diff is equal to original file
	endPtr := dataOut.FilePointer()
	diff := endPtr - startPtr
	if diff != length {
		return 0, errors.New(fmt.Sprintf(
			"Difference in the output file offsets %v does not match the original file length %v",
			diff, length))
	}
	fileEntry.offset = startPtr
	success = true
	return length, nil
}

func (w *CompoundFileWriter) writeEntryTable(entries map[string]*FileEntry,
	entryOut IndexOutput) error {
	panic("not implemented yet")
}

func (w *CompoundFileWriter) createOutput(name string, context IOContext) (IndexOutput, error) {
	w.ensureOpen()
	var success = false
	var outputLocked = false
	defer func() {
		if !success {
			delete(w.entries, name)
			if outputLocked { // release the output lock if not successful
				assert(w.outputTaken.Get())
				w.releaseOutputLock()
			}
		}
	}()

	assert2(name != "", "name must not be empty")
	_, ok := w.entries[name]
	assert2(!ok, "File %v already exists", name)
	entry := &FileEntry{}
	entry.file = name
	w.entries[name] = entry
	id := util.StripSegmentName(name)
	_, ok = w.seenIDs[id]
	assert2(!ok, "file='%v' maps to id='%v', which was already written", name, id)
	w.seenIDs[id] = true

	var out *DirectCFSIndexOutput
	if outputLocked := w.outputTaken.CompareAndSet(false, true); outputLocked {
		o, err := w.output()
		if err != nil {
			return nil, err
		}
		out = newDirectCFSIndexOutput(w, o, entry, false)
	} else {
		entry.dir = w.directory
		assert2(!w.directory.FileExists(name), "File %v already exists", name)
		o, err := w.directory.CreateOutput(name, context)
		if err != nil {
			return nil, err
		}
		out = newDirectCFSIndexOutput(w, o, entry, true)
	}
	success = true
	return out, nil
}

func (w *CompoundFileWriter) releaseOutputLock() {
	w.outputTaken.CompareAndSet(true, false)
}

func (w *CompoundFileWriter) prunePendingEntries() error {
	// claim the output and copy all pending files in
	if w.outputTaken.CompareAndSet(false, true) {
		defer func() {
			cas := w.outputTaken.CompareAndSet(true, false)
			assert(cas)
		}()
		for w.pendingEntries.Len() > 0 {
			head := w.pendingEntries.Front()
			w.pendingEntries.Remove(head)
			entry := head.Value.(*FileEntry)
			out, err := w.output()
			if err == nil {
				_, err = w.copyFileEntry(out, entry)
			}
			if err != nil {
				return err
			}
			w.entries[entry.file] = entry
		}
	}
	return nil
}

type DirectCFSIndexOutput struct {
	*IndexOutputImpl
	owner        *CompoundFileWriter
	delegate     IndexOutput
	offset       int64
	closed       bool
	entry        *FileEntry
	writtenBytes int64
	isSeparate   bool
}

func newDirectCFSIndexOutput(owner *CompoundFileWriter,
	delegate IndexOutput, entry *FileEntry, isSeparate bool) *DirectCFSIndexOutput {
	ans := &DirectCFSIndexOutput{
		owner:      owner,
		delegate:   delegate,
		entry:      entry,
		offset:     delegate.FilePointer(),
		isSeparate: isSeparate,
	}
	ans.entry.offset = ans.offset
	ans.IndexOutputImpl = NewIndexOutput(ans)
	return ans
}

func (out *DirectCFSIndexOutput) Flush() error {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) Close() error {
	if out.closed {
		return nil
	}
	out.closed = true
	out.entry.length = out.writtenBytes
	if out.isSeparate {
		err := out.delegate.Close()
		if err != nil {
			return err
		}
		// we are a separate file - push into the pending entries
		out.owner.pendingEntries.PushBack(out.entry)
	} else {
		// we have been written into the CFS directly - release the lock
		out.owner.releaseOutputLock()
	}
	// now prune all pending entries and push them into the CFS
	return out.owner.prunePendingEntries()
}

func (out *DirectCFSIndexOutput) FilePointer() int64 {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) Length() (int64, error) {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) WriteByte(b byte) error {
	panic("not implemented yet")
}

func (out *DirectCFSIndexOutput) WriteBytes(b []byte) error {
	assert(!out.closed)
	out.writtenBytes += int64(len(b))
	return out.delegate.WriteBytes(b)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2015 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import `unsafe`

const (
	cachedNone = uint8(0)
	cacheExact = uint8(1)
	cacheAlpha = uint8(2) // Upper bound.
	cacheBeta  = uint8(4) // Lower bound.
	cacheEntrySize = int(unsafe.Sizeof(CacheEntry{}))
)

type CacheEntry struct {
	id    uint32
	move  Move
	score int16
	depth int16
	flags uint8
	token uint8
}

type Cache []CacheEntry

func cacheUsage() (hits int) {
	for i := 0; i < len(game.cache); i++ {
		if game.cache[i].id != uint32(0) {
			hits++
		}
	}
	return
}

func NewCache(megaBytes float64) Cache {
	if megaBytes > 0.0 {
		cacheSize := int(1024*1024*megaBytes) / cacheEntrySize
		// If cache size has changed then create a new cache; otherwise
		// simply clear the existing one.
		if cacheSize != len(game.cache) {
			return make(Cache, cacheSize)
		}
		game.cache = Cache{}
		return game.cache
	}
	return nil
}

func (p *Position) cache(move Move, score, depth, ply int, flags uint8) *Position {
	if cacheSize := len(game.cache); cacheSize > 0 {
		index := p.hash & uint64(cacheSize - 1)
		// fmt.Printf("cache size %d entries, index %d\n", len(game.cache), index)
		entry := &game.cache[index]

		if depth > int(entry.depth) || game.token != entry.token {
			if score > Checkmate-MaxPly && score <= Checkmate {
				entry.score = int16(score + ply)
			} else if score >= -Checkmate && score < -Checkmate+MaxPly {
				entry.score = int16(score - ply)
			} else {
				entry.score = int16(score)
			}
			entry.move = move
			entry.depth = int16(depth)
			entry.flags = flags
			entry.token = game.token
			entry.id = uint32(p.hash >> 32)
		}
	}

	return p
}

func (p *Position) probeCache() *CacheEntry {
	if cacheSize := len(game.cache); cacheSize > 0 {
		index := p.hash & uint64(cacheSize - 1)
		if entry := &game.cache[index]; entry.id == uint32(p.hash >>32) {
			return entry
		}
	}
	return nil
}

func (p *Position) cachedMove() Move {
	if cached := p.probeCache(); cached != nil {
		return cached.move
	}
	return Move(0)
}
<commit_msg>Don't override best move with null one when caching<commit_after>// Copyright (c) 2014-2015 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import `unsafe`

const (
	cachedNone = uint8(0)
	cacheExact = uint8(1)
	cacheAlpha = uint8(2) // Upper bound.
	cacheBeta  = uint8(4) // Lower bound.
	cacheEntrySize = int(unsafe.Sizeof(CacheEntry{}))
)

type CacheEntry struct {
	id    uint32
	move  Move
	score int16
	depth int16
	flags uint8
	token uint8
}

type Cache []CacheEntry

func cacheUsage() (hits int) {
	for i := 0; i < len(game.cache); i++ {
		if game.cache[i].id != uint32(0) {
			hits++
		}
	}
	return
}

func NewCache(megaBytes float64) Cache {
	if megaBytes > 0.0 {
		cacheSize := int(1024*1024*megaBytes) / cacheEntrySize
		// If cache size has changed then create a new cache; otherwise
		// simply clear the existing one.
		if cacheSize != len(game.cache) {
			return make(Cache, cacheSize)
		}
		game.cache = Cache{}
		return game.cache
	}
	return nil
}

func (p *Position) cache(move Move, score, depth, ply int, flags uint8) *Position {
	if cacheSize := len(game.cache); cacheSize > 0 {
		index := p.hash & uint64(cacheSize - 1)
		// fmt.Printf("cache size %d entries, index %d\n", len(game.cache), index)
		entry := &game.cache[index]

		if depth > int(entry.depth) || game.token != entry.token {
			if score > Checkmate-MaxPly && score <= Checkmate {
				entry.score = int16(score + ply)
			} else if score >= -Checkmate && score < -Checkmate+MaxPly {
				entry.score = int16(score - ply)
			} else {
				entry.score = int16(score)
			}
			if move != Move(0) || uint32(p.hash >> 32) != entry.id {
				entry.move = move
			}
			entry.depth = int16(depth)
			entry.flags = flags
			entry.token = game.token
			entry.id = uint32(p.hash >> 32)
		}
	}

	return p
}

func (p *Position) probeCache() *CacheEntry {
	if cacheSize := len(game.cache); cacheSize > 0 {
		index := p.hash & uint64(cacheSize - 1)
		if entry := &game.cache[index]; entry.id == uint32(p.hash >>32) {
			return entry
		}
	}
	return nil
}

func (p *Position) cachedMove() Move {
	if cached := p.probeCache(); cached != nil {
		return cached.move
	}
	return Move(0)
}
<|endoftext|>