<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package master

import (
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"fmt"

	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/api"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	certutil "k8s.io/kubernetes/pkg/util/cert"
)

type kubeDiscovery struct {
	Deployment *extensions.Deployment
	Secret     *api.Secret
}

const (
	kubeDiscoverynName      = "kube-discovery"
	kubeDiscoverySecretName = "clusterinfo"
)

func encodeKubeDiscoverySecretData(s *kubeadmapi.KubeadmConfig, caCert *x509.Certificate) map[string][]byte {
	// TODO ListenIP is probably not the right now, although it's best we have right now
	// if user provides a DNS name, or anything else, we should use that, may be it's really
	// the list of all SANs (minus internal DNS names and service IP)?

	var (
		data         = map[string][]byte{}
		endpointList = []string{}
		tokenMap     = map[string]string{}
	)

	for _, addr := range s.InitFlags.API.AdvertiseAddrs {
		endpointList = append(endpointList, fmt.Sprintf("https://%s:443", addr.String()))
	}

	tokenMap[s.Secrets.TokenID] = hex.EncodeToString(s.Secrets.Token)

	data["endpoint-list.json"], _ = json.Marshal(endpointList)
	data["token-map.json"], _ = json.Marshal(tokenMap)
	data["ca.pem"] = certutil.EncodeCertPEM(caCert)

	return data
}

func newKubeDiscoveryPodSpec(s *kubeadmapi.KubeadmConfig) api.PodSpec {
	return api.PodSpec{
		// We have to use host network namespace, as `HostPort`/`HostIP` are Docker's
		// buisness and CNI support isn't quite there yet (except for kubenet)
		// (see https://github.com/kubernetes/kubernetes/issues/31307)
		// TODO update this when #31307 is resolved
		SecurityContext: &api.PodSecurityContext{HostNetwork: true},
		Containers: []api.Container{{
			Name:    kubeDiscoverynName,
			Image:   s.EnvParams["discovery_image"],
			Command: []string{"/usr/bin/kube-discovery"},
			VolumeMounts: []api.VolumeMount{{
				Name:      kubeDiscoverySecretName,
				MountPath: "/tmp/secret", // TODO use a shared constant
				ReadOnly:  true,
			}},
			Ports: []api.ContainerPort{
				// TODO when CNI issue (#31307) is resolved, we should consider adding
				// `HostIP: s.API.AdvertiseAddrs[0]`, if there is only one address`
				{Name: "http", ContainerPort: 9898, HostPort: 9898},
			},
		}},
		Volumes: []api.Volume{{
			Name: kubeDiscoverySecretName,
			VolumeSource: api.VolumeSource{
				Secret: &api.SecretVolumeSource{SecretName: kubeDiscoverySecretName},
			}},
		},
	}
}

func newKubeDiscovery(s *kubeadmapi.KubeadmConfig, caCert *x509.Certificate) kubeDiscovery {
	kd := kubeDiscovery{
		Deployment: NewDeployment(kubeDiscoverynName, 1, newKubeDiscoveryPodSpec(s)),
		Secret: &api.Secret{
			ObjectMeta: api.ObjectMeta{Name: kubeDiscoverySecretName},
			Type:       api.SecretTypeOpaque,
			Data:       encodeKubeDiscoverySecretData(s, caCert),
		},
	}

	SetMasterTaintTolerations(&kd.Deployment.Spec.Template.ObjectMeta)
	SetMasterNodeAffinity(&kd.Deployment.Spec.Template.ObjectMeta)

	return kd
}

func CreateDiscoveryDeploymentAndSecret(s *kubeadmapi.KubeadmConfig, client *clientset.Clientset, caCert *x509.Certificate) error {
	kd := newKubeDiscovery(s, caCert)

	if _, err := client.Extensions().Deployments(api.NamespaceSystem).Create(kd.Deployment); err != nil {
		return fmt.Errorf("<master/discovery> failed to create %q deployment [%s]", kubeDiscoverynName, err)
	}
	if _, err := client.Secrets(api.NamespaceSystem).Create(kd.Secret); err != nil {
		return fmt.Errorf("<master/discovery> failed to create %q secret [%s]", kubeDiscoverySecretName, err)
	}

	fmt.Println("<master/discovery> created essential addon: kube-discovery")

	// TODO we should probably wait for the pod to become ready

	return nil
}
<commit_msg>Before declaring success, require that the discovery deployment has at least one active pod.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package master

import (
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"

	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/api"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	certutil "k8s.io/kubernetes/pkg/util/cert"
	"k8s.io/kubernetes/pkg/util/wait"
)

type kubeDiscovery struct {
	Deployment *extensions.Deployment
	Secret     *api.Secret
}

const (
	kubeDiscoveryName       = "kube-discovery"
	kubeDiscoverySecretName = "clusterinfo"
)

func encodeKubeDiscoverySecretData(s *kubeadmapi.KubeadmConfig, caCert *x509.Certificate) map[string][]byte {
	// TODO ListenIP is probably not the right now, although it's best we have right now
	// if user provides a DNS name, or anything else, we should use that, may be it's really
	// the list of all SANs (minus internal DNS names and service IP)?

	var (
		data         = map[string][]byte{}
		endpointList = []string{}
		tokenMap     = map[string]string{}
	)

	for _, addr := range s.InitFlags.API.AdvertiseAddrs {
		endpointList = append(endpointList, fmt.Sprintf("https://%s:443", addr.String()))
	}

	tokenMap[s.Secrets.TokenID] = hex.EncodeToString(s.Secrets.Token)

	data["endpoint-list.json"], _ = json.Marshal(endpointList)
	data["token-map.json"], _ = json.Marshal(tokenMap)
	data["ca.pem"] = certutil.EncodeCertPEM(caCert)

	return data
}

func newKubeDiscoveryPodSpec(s *kubeadmapi.KubeadmConfig) api.PodSpec {
	return api.PodSpec{
		// We have to use host network namespace, as `HostPort`/`HostIP` are Docker's
		// buisness and CNI support isn't quite there yet (except for kubenet)
		// (see https://github.com/kubernetes/kubernetes/issues/31307)
		// TODO update this when #31307 is resolved
		SecurityContext: &api.PodSecurityContext{HostNetwork: true},
		Containers: []api.Container{{
			Name:    kubeDiscoveryName,
			Image:   s.EnvParams["discovery_image"],
			Command: []string{"/usr/bin/kube-discovery"},
			VolumeMounts: []api.VolumeMount{{
				Name:      kubeDiscoverySecretName,
				MountPath: "/tmp/secret", // TODO use a shared constant
				ReadOnly:  true,
			}},
			Ports: []api.ContainerPort{
				// TODO when CNI issue (#31307) is resolved, we should consider adding
				// `HostIP: s.API.AdvertiseAddrs[0]`, if there is only one address`
				{Name: "http", ContainerPort: 9898, HostPort: 9898},
			},
		}},
		Volumes: []api.Volume{{
			Name: kubeDiscoverySecretName,
			VolumeSource: api.VolumeSource{
				Secret: &api.SecretVolumeSource{SecretName: kubeDiscoverySecretName},
			}},
		},
	}
}

func newKubeDiscovery(s *kubeadmapi.KubeadmConfig, caCert *x509.Certificate) kubeDiscovery {
	kd := kubeDiscovery{
		Deployment: NewDeployment(kubeDiscoveryName, 1, newKubeDiscoveryPodSpec(s)),
		Secret: &api.Secret{
			ObjectMeta: api.ObjectMeta{Name: kubeDiscoverySecretName},
			Type:       api.SecretTypeOpaque,
			Data:       encodeKubeDiscoverySecretData(s, caCert),
		},
	}

	SetMasterTaintTolerations(&kd.Deployment.Spec.Template.ObjectMeta)
	SetMasterNodeAffinity(&kd.Deployment.Spec.Template.ObjectMeta)

	return kd
}

func CreateDiscoveryDeploymentAndSecret(s *kubeadmapi.KubeadmConfig, client *clientset.Clientset, caCert *x509.Certificate) error {
	kd := newKubeDiscovery(s, caCert)

	if _, err := client.Extensions().Deployments(api.NamespaceSystem).Create(kd.Deployment); err != nil {
		return fmt.Errorf("<master/discovery> failed to create %q deployment [%s]", kubeDiscoveryName, err)
	}
	if _, err := client.Secrets(api.NamespaceSystem).Create(kd.Secret); err != nil {
		return fmt.Errorf("<master/discovery> failed to create %q secret [%s]", kubeDiscoverySecretName, err)
	}

	fmt.Println("<master/discovery> created essential addon: kube-discovery, waiting for it to become ready")

	// wait for the pod to become ready
	start := time.Now()
	wait.PollInfinite(500*time.Millisecond, func() (bool, error) {
		d, err := client.Extensions().Deployments(api.NamespaceSystem).Get(kubeDiscoveryName)
		if err != nil {
			return false, nil
		}
		if d.Status.AvailableReplicas < 1 {
			return false, nil
		}
		return true, nil
	})
	fmt.Printf("<master/discovery> kube-discovery is ready after %f seconds\n", time.Since(start).Seconds())

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/anacrolix/tagflag"
	"github.com/bradfitz/iter"

	"github.com/anacrolix/torrent/metainfo"
)

var flags struct {
	JustName    bool
	PieceHashes bool
	Files       bool
	tagflag.StartPos
	TorrentFiles []string
}

func main() {
	tagflag.Parse(&flags)
	for _, filename := range flags.TorrentFiles {
		metainfo, err := metainfo.LoadFromFile(filename)
		if err != nil {
			log.Print(err)
			continue
		}
		info := &metainfo.Info.Info
		if flags.JustName {
			fmt.Printf("%s\n", metainfo.Info.Name)
			continue
		}
		d := map[string]interface{}{
			"Name":        info.Name,
			"NumPieces":   info.NumPieces(),
			"PieceLength": info.PieceLength,
			"InfoHash":    metainfo.Info.Hash().HexString(),
			"NumFiles":    len(info.UpvertedFiles()),
			"TotalLength": info.TotalLength(),
		}
		if flags.Files {
			d["Files"] = info.Files
		}
		if flags.PieceHashes {
			d["PieceHashes"] = func() (ret []string) {
				for i := range iter.N(info.NumPieces()) {
					ret = append(ret, hex.EncodeToString(info.Pieces[i*20:(i+1)*20]))
				}
				return
			}()
		}
		b, _ := json.MarshalIndent(d, "", "  ")
		os.Stdout.Write(b)
	}
	if !flags.JustName {
		os.Stdout.WriteString("\n")
	}
}
<commit_msg>cmd/torrent-metainfo-pprint: Add Announce and AnnounceList keys<commit_after>package main

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/anacrolix/tagflag"
	"github.com/bradfitz/iter"

	"github.com/anacrolix/torrent/metainfo"
)

var flags struct {
	JustName    bool
	PieceHashes bool
	Files       bool
	tagflag.StartPos
	TorrentFiles []string
}

func main() {
	tagflag.Parse(&flags)
	for _, filename := range flags.TorrentFiles {
		metainfo, err := metainfo.LoadFromFile(filename)
		if err != nil {
			log.Print(err)
			continue
		}
		info := &metainfo.Info.Info
		if flags.JustName {
			fmt.Printf("%s\n", metainfo.Info.Name)
			continue
		}
		d := map[string]interface{}{
			"Name":         info.Name,
			"NumPieces":    info.NumPieces(),
			"PieceLength":  info.PieceLength,
			"InfoHash":     metainfo.Info.Hash().HexString(),
			"NumFiles":     len(info.UpvertedFiles()),
			"TotalLength":  info.TotalLength(),
			"Announce":     metainfo.Announce,
			"AnnounceList": metainfo.AnnounceList,
		}
		if flags.Files {
			d["Files"] = info.Files
		}
		if flags.PieceHashes {
			d["PieceHashes"] = func() (ret []string) {
				for i := range iter.N(info.NumPieces()) {
					ret = append(ret, hex.EncodeToString(info.Pieces[i*20:(i+1)*20]))
				}
				return
			}()
		}
		b, _ := json.MarshalIndent(d, "", "  ")
		os.Stdout.Write(b)
	}
	if !flags.JustName {
		os.Stdout.WriteString("\n")
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package web

import "html/template"

var Top = template.New("top").Funcs(Funcmap)

var DidYouMeanTemplate = template.Must(Top.New("didyoumean").Parse(`

    <title>Error</title>

{{.Message}}. Did you mean {{.Suggestion}} ?

`))

// QueryTemplate is the template for the search box.
var QueryTemplate = template.Must(Top.New("box").Parse(`
  <form action="search">
    Search some code: <input
      autofocus
      onfocus="this.value = this.value;"
      {{if .Query}}value={{.Query}}
      {{end}}type="text" name="q"> Max results:  <input style="width: 5em;" type="text" name="num" value="50"> <input type="submit" value="Search">
  </form>
`))

var SearchBoxTemplate = template.Must(Top.New("search").Parse(
	`

<title>Zoekt, en gij zult spinazie eten</title>

{{template "box" .Last}}

  Search examples:

needle

search for "needle"

thread or needle

search for either "thread" or "needle"

class needle

search for files containing both "class" and "needle"

class Needle

search for files containing both "class" (case insensitive) and "Needle" (case sensitive)

class Needle case:yes

search for files containing "class" and "Needle", both case sensitively

"class Needle"

search for files with the phrase "class Needle"

needle -hay

search for files with the word "needle" but not the word "hay"

path file:java

search for the word "path" in files whose name contains "java"

f:\.c$

search for files whose name ends with ".c"

path -file:java

search for the word "path" excluding files whose name contains "java"

foo.*bar

search for the regular expression "foo.*bar"

-(Path File) Stream

search "Stream", but exclude files containing both "Path" and "File"

-Path\ File Stream

search "Stream", but exclude files containing "Path File"

phone repo:droid

search for "phone" in repositories whose name contains "droid"

phone r:droid

search for "phone" to repositories whose name contains "droid"

r:droid

list repositories whose name contains "droid"

phone branch:aster

for Git repos, find "phone" in files in branches whose name contains "aster".

phone b:HEAD

for Git repos, find "phone" in the default ('HEAD') branch.

Used {{HumanUnit .Stats.IndexBytes}} memory for
{{.Stats.Documents}} documents ({{HumanUnit .Stats.ContentBytes}})
from {{len .Stats.Repos}} repositories.

{{if .Version}}Zoekt version {{.Version}}, uptime{{else}}Uptime{{end}} {{.Uptime}}

To list repositories, try:

r:droid

list repositories whose name contains "droid".

r:go -r:google

list repositories whose name contains "go" but not "google".

`))

var ResultsTemplate = template.Must(Top.New("results").Parse(`

    <title>Results for {{.QueryStr}}</title>

  {{template "box" .Last}}

  {{if .Stats.Crashes}}
{{.Stats.Crashes}} shards crashed
{{end}}
  Found {{.Stats.MatchCount}} results in {{.Stats.FileCount}} files ({{.Stats.NgramMatches}} ngram matches,
    {{.Stats.FilesConsidered}} docs considered, {{.Stats.FilesLoaded}} docs ({{HumanUnit .Stats.BytesLoaded}}B) loaded,
    {{.Stats.FilesSkipped}} docs skipped): for

{{.Query}} with options {{.SearchOptions}}

  in {{.Stats.Duration}} (queued: {{.Stats.Wait}})

  {{range .FileMatches}}
    {{if .URL}}{{end}}
    <tt>{{.Repo}}:{{.FileName}}{{if .URL}}{{end}}:{{if .Branches}}[{{range .Branches}}{{.}}, {{end}}]{{end}} </tt>

    {{range .Matches}}

{{if .URL}}{{end}}{{.LineNum}}{{if .URL}}{{end}}: {{range .Fragments}}{{.Pre}}{{.Match}}{{.Post}}{{end}}

    {{end}}

  {{end}}

`))

var RepoListTemplate = template.Must(Top.New("repolist").Parse(`

    <title>Repo search result for {{.Last.Query}}</title>

{{template "box" .Last}}

  Found {{.RepoCount}} repositories:

  {{range .Repos}}

      <tt>{{if .URL}}{{end}}{{.Name}}{{if .URL}}{{end}}
      </tt> ({{.IndexTime.Format "Jan 02, 2006 15:04"}}). Branches:
      {{range .Branches}}
         {{if .URL}}{{end}}{{.Name}}{{if .URL}}{{end}},
      {{end}}

  {{end}}

`))

var PrintTemplate = template.Must(Top.New("print").Parse(`

    <title>{{.Repo}}:{{.Name}}</title>

{{template "box" .Last}}

{{.Content}}

`))
<commit_msg>Provide links for example queries.<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package web

import "html/template"

var Top = template.New("top").Funcs(Funcmap)

var DidYouMeanTemplate = template.Must(Top.New("didyoumean").Parse(`

    <title>Error</title>

{{.Message}}. Did you mean {{.Suggestion}} ?

`))

// QueryTemplate is the template for the search box.
var QueryTemplate = template.Must(Top.New("box").Parse(`
  <form action="search">
    Search some code: <input
      autofocus
      onfocus="this.value = this.value;"
      {{if .Query}}value={{.Query}}
      {{end}}type="text" name="q"> Max results:  <input style="width: 5em;" type="text" name="num" value="50"> <input type="submit" value="Search">
  </form>
`))

var SearchBoxTemplate = template.Must(Top.New("search").Parse(
	`

<title>Zoekt, en gij zult spinazie eten</title>

{{template "box" .Last}}

  Search examples:

needle

search for "needle"

thread or needle

search for either "thread" or "needle"

class needle

search for files containing both "class" and "needle"

class Needle

search for files containing both "class" (case insensitive) and "Needle" (case sensitive)

class Needle case:yes

search for files containing "class" and "Needle", both case sensitively

"class Needle"

search for files with the phrase "class Needle"

needle -hay

search for files with the word "needle" but not the word "hay"

path file:java

search for the word "path" in files whose name contains "java"

f:\.c$

search for files whose name ends with ".c"

path -file:java

search for the word "path" excluding files whose name contains "java"

foo.*bar

search for the regular expression "foo.*bar"

-(Path File) Stream

search "Stream", but exclude files containing both "Path" and "File"

-Path\ file Stream

search "Stream", but exclude files containing "Path File"

phone r:droid

search for "phone" in repositories whose name contains "droid"

phone b:aster

for Git repos, find "phone" in files in branches whose name contains "aster".

phone b:HEAD

for Git repos, find "phone" in the default ('HEAD') branch.

Used {{HumanUnit .Stats.IndexBytes}} memory for
{{.Stats.Documents}} documents ({{HumanUnit .Stats.ContentBytes}})
from {{len .Stats.Repos}} repositories.

{{if .Version}}Zoekt version {{.Version}}, uptime{{else}}Uptime{{end}} {{.Uptime}}

To list repositories, try:

r:droid

list repositories whose name contains "droid".

r:go -r:google

list repositories whose name contains "go" but not "google".

`))

var ResultsTemplate = template.Must(Top.New("results").Parse(`

    <title>Results for {{.QueryStr}}</title>

  {{template "box" .Last}}

  {{if .Stats.Crashes}}
{{.Stats.Crashes}} shards crashed
{{end}}
  Found {{.Stats.MatchCount}} results in {{.Stats.FileCount}} files ({{.Stats.NgramMatches}} ngram matches,
    {{.Stats.FilesConsidered}} docs considered, {{.Stats.FilesLoaded}} docs ({{HumanUnit .Stats.BytesLoaded}}B) loaded,
    {{.Stats.FilesSkipped}} docs skipped): for

{{.Query}} with options {{.SearchOptions}}

  in {{.Stats.Duration}} (queued: {{.Stats.Wait}})

  {{range .FileMatches}}
    {{if .URL}}{{end}}
    <tt>{{.Repo}}:{{.FileName}}{{if .URL}}{{end}}:{{if .Branches}}[{{range .Branches}}{{.}}, {{end}}]{{end}} </tt>

    {{range .Matches}}

{{if .URL}}{{end}}{{.LineNum}}{{if .URL}}{{end}}: {{range .Fragments}}{{.Pre}}{{.Match}}{{.Post}}{{end}}

    {{end}}

  {{end}}

`))

var RepoListTemplate = template.Must(Top.New("repolist").Parse(`

    <title>Repo search result for {{.Last.Query}}</title>

{{template "box" .Last}}

  Found {{.RepoCount}} repositories:

  {{range .Repos}}

      <tt>{{if .URL}}{{end}}{{.Name}}{{if .URL}}{{end}}
      </tt> ({{.IndexTime.Format "Jan 02, 2006 15:04"}}). Branches:
      {{range .Branches}}
         {{if .URL}}{{end}}{{.Name}}{{if .URL}}{{end}},
      {{end}}

  {{end}}

`))

var PrintTemplate = template.Must(Top.New("print").Parse(`

    <title>{{.Repo}}:{{.Name}}</title>

{{template "box" .Last}}

{{.Content}}

`))
<|endoftext|>


<commit_before>package api

import (
	"net/http"
	"strings"
)

func put(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/profile"):
		i.PUTProfile(w, r)
	case strings.Contains(path, "/ob/settings"):
		i.PUTSettings(w, r)
	case strings.Contains(path, "/ob/moderator"):
		i.PUTModerator(w, r)
	case strings.Contains(path, "/ob/listing"):
		i.PUTListing(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func post(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/listing"):
		i.POSTListing(w, r)
	case strings.Contains(path, "/ob/purpose"):
		i.POSTPurchase(w, r)
	case strings.Contains(path, "/ob/follow"):
		i.POSTFollow(w, r)
	case strings.Contains(path, "/ob/unfollow"):
		i.POSTUnfollow(w, r)
	case strings.Contains(path, "/ob/profile"):
		i.POSTProfile(w, r)
	case strings.Contains(path, "/ob/images"):
		i.POSTImage(w, r)
	case strings.Contains(path, "/wallet/spend"):
		i.POSTSpendCoins(w, r)
	case strings.Contains(path, "/ob/settings"):
		i.POSTSettings(w, r)
	case strings.Contains(path, "/ob/inventory"):
		i.POSTInventory(w, r)
	case strings.Contains(path, "/ob/avatar"):
		i.POSTAvatar(w, r)
	case strings.Contains(path, "/ob/header"):
		i.POSTHeader(w, r)
	case strings.Contains(path, "/ob/orderconfirmation"):
		i.POSTOrderConfirmation(w, r)
	case strings.Contains(path, "/ob/ordercancel"):
		i.POSTOrderCancel(w, r)
	case strings.Contains(path, "/ob/orderfulfillment"):
		i.POSTOrderFulfill(w, r)
	case strings.Contains(path, "/ob/ordercompletion"):
		i.POSTOrderComplete(w, r)
	case strings.Contains(path, "/ob/refund"):
		i.POSTRefund(w, r)
	case strings.Contains(path, "/wallet/resyncblockchain"):
		i.POSTResyncBlockchain(w, r)
	case strings.Contains(path, "/ob/opendispute"):
		i.POSTOpenDispute(w, r)
	case strings.Contains(path, "/ob/closedispute"):
		i.POSTCloseDispute(w, r)
	case strings.Contains(path, "/ob/releasefunds"):
		i.POSTReleaseFunds(w, r)
	case strings.Contains(path, "/ob/chat"):
		i.POSTChat(w, r)
	case strings.Contains(path, "/ob/markchatasread"):
		i.POSTMarkChatAsRead(w, r)
	case strings.Contains(path, "/ob/marknotificationasread"):
		i.POSTMarkNotificationAsRead(w, r)
	case strings.Contains(path, "/ob/fetchprofiles"):
		i.POSTFetchProfiles(w, r)
	case strings.Contains(path, "/ob/shutdown"):
		i.POSTShutdown(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func get(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/status"):
		i.GETStatus(w, r)
	case strings.Contains(path, "/ob/peers"):
		i.GETPeers(w, r)
	case strings.Contains(path, "/ob/config"):
		i.GETConfig(w, r)
	case strings.Contains(path, "/wallet/address"):
		i.GETAddress(w, r)
	case strings.Contains(path, "/wallet/mnemonic"):
		i.GETMnemonic(w, r)
	case strings.Contains(path, "/wallet/balance"):
		i.GETBalance(w, r)
	case strings.Contains(path, "/ob/settings"):
		i.GETSettings(w, r)
	case strings.Contains(path, "/ob/closestpeers"):
		i.GETClosestPeers(w, r)
	case strings.Contains(path, "/ob/exchangerate"):
		i.GETExchangeRate(w, r)
	case strings.Contains(path, "/ob/followers"):
		i.GETFollowers(w, r)
	case strings.Contains(path, "/ob/following"):
		i.GETFollowing(w, r)
	case strings.Contains(path, "/ob/inventory"):
		i.GETInventory(w, r)
	case strings.Contains(path, "/ob/profile"):
		i.GETProfile(w, r)
	case strings.Contains(path, "/ob/listings"):
		i.GETListings(w, r)
	case strings.Contains(path, "/ob/listing"):
		i.GETListing(w, r)
	case strings.Contains(path, "/ob/followsme"):
		i.GETFollowsMe(w, r)
	case strings.Contains(path, "/ob/isfollowing"):
		i.GETIsFollowing(w, r)
	case strings.Contains(path, "/ob/order"):
		i.GETOrder(w, r)
	case strings.Contains(path, "/ob/moderators"):
		i.GETModerators(w, r)
	case strings.Contains(path, "/ob/case"):
		i.GETCase(w, r)
	case strings.Contains(path, "/ob/chatmessages"):
		i.GETChatMessages(w, r)
	case strings.Contains(path, "/ob/chatconversations"):
		i.GETChatConversations(w, r)
	case strings.Contains(path, "/ob/notifications"):
		i.GETNotifications(w, r)
	case strings.Contains(path, "/ob/images"):
		i.GETImage(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func patch(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/settings"):
		i.PATCHSettings(w, r)
	case strings.Contains(path, "/ob/profile"):
		i.PATCHProfile(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func deleter(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.Contains(path, "/ob/moderator"):
		i.DELETEModerator(w, r)
	case strings.Contains(path, "/ob/listing"):
		i.DELETEListing(w, r)
	case strings.Contains(path, "/ob/chatmessage"):
		i.DELETEChatMessage(w, r)
	case strings.Contains(path, "/ob/chatconversation"):
		i.DELETEChatConversation(w, r)
	case strings.Contains(path, "/ob/notifications"):
		i.DELETENotification(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}
<commit_msg>Switch Contains to HasPrefix in endpoints<commit_after>package api

import (
	"net/http"
	"strings"
)

func put(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.HasPrefix(path, "/ob/profile"):
		i.PUTProfile(w, r)
	case strings.HasPrefix(path, "/ob/settings"):
		i.PUTSettings(w, r)
	case strings.HasPrefix(path, "/ob/moderator"):
		i.PUTModerator(w, r)
	case strings.HasPrefix(path, "/ob/listing"):
		i.PUTListing(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func post(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.HasPrefix(path, "/ob/listing"):
		i.POSTListing(w, r)
	case strings.HasPrefix(path, "/ob/purpose"):
		i.POSTPurchase(w, r)
	case strings.HasPrefix(path, "/ob/follow"):
		i.POSTFollow(w, r)
	case strings.HasPrefix(path, "/ob/unfollow"):
		i.POSTUnfollow(w, r)
	case strings.HasPrefix(path, "/ob/profile"):
		i.POSTProfile(w, r)
	case strings.HasPrefix(path, "/ob/images"):
		i.POSTImage(w, r)
	case strings.HasPrefix(path, "/wallet/spend"):
		i.POSTSpendCoins(w, r)
	case strings.HasPrefix(path, "/ob/settings"):
		i.POSTSettings(w, r)
	case strings.HasPrefix(path, "/ob/inventory"):
		i.POSTInventory(w, r)
	case strings.HasPrefix(path, "/ob/avatar"):
		i.POSTAvatar(w, r)
	case strings.HasPrefix(path, "/ob/header"):
		i.POSTHeader(w, r)
	case strings.HasPrefix(path, "/ob/orderconfirmation"):
		i.POSTOrderConfirmation(w, r)
	case strings.HasPrefix(path, "/ob/ordercancel"):
		i.POSTOrderCancel(w, r)
	case strings.HasPrefix(path, "/ob/orderfulfillment"):
		i.POSTOrderFulfill(w, r)
	case strings.HasPrefix(path, "/ob/ordercompletion"):
		i.POSTOrderComplete(w, r)
	case strings.HasPrefix(path, "/ob/refund"):
		i.POSTRefund(w, r)
	case strings.HasPrefix(path, "/wallet/resyncblockchain"):
		i.POSTResyncBlockchain(w, r)
	case strings.HasPrefix(path, "/ob/opendispute"):
		i.POSTOpenDispute(w, r)
	case strings.HasPrefix(path, "/ob/closedispute"):
		i.POSTCloseDispute(w, r)
	case strings.HasPrefix(path, "/ob/releasefunds"):
		i.POSTReleaseFunds(w, r)
	case strings.HasPrefix(path, "/ob/chat"):
		i.POSTChat(w, r)
	case strings.HasPrefix(path, "/ob/markchatasread"):
		i.POSTMarkChatAsRead(w, r)
	case strings.HasPrefix(path, "/ob/marknotificationasread"):
		i.POSTMarkNotificationAsRead(w, r)
	case strings.HasPrefix(path, "/ob/fetchprofiles"):
		i.POSTFetchProfiles(w, r)
	case strings.HasPrefix(path, "/ob/shutdown"):
		i.POSTShutdown(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func get(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.HasPrefix(path, "/ob/status"):
		i.GETStatus(w, r)
	case strings.HasPrefix(path, "/ob/peers"):
		i.GETPeers(w, r)
	case strings.HasPrefix(path, "/ob/config"):
		i.GETConfig(w, r)
	case strings.HasPrefix(path, "/wallet/address"):
		i.GETAddress(w, r)
	case strings.HasPrefix(path, "/wallet/mnemonic"):
		i.GETMnemonic(w, r)
	case strings.HasPrefix(path, "/wallet/balance"):
		i.GETBalance(w, r)
	case strings.HasPrefix(path, "/ob/settings"):
		i.GETSettings(w, r)
	case strings.HasPrefix(path, "/ob/closestpeers"):
		i.GETClosestPeers(w, r)
	case strings.HasPrefix(path, "/ob/exchangerate"):
		i.GETExchangeRate(w, r)
	case strings.HasPrefix(path, "/ob/followers"):
		i.GETFollowers(w, r)
	case strings.HasPrefix(path, "/ob/following"):
		i.GETFollowing(w, r)
	case strings.HasPrefix(path, "/ob/inventory"):
		i.GETInventory(w, r)
	case strings.HasPrefix(path, "/ob/profile"):
		i.GETProfile(w, r)
	case strings.HasPrefix(path, "/ob/listings"):
		i.GETListings(w, r)
	case strings.HasPrefix(path, "/ob/listing"):
		i.GETListing(w, r)
	case strings.HasPrefix(path, "/ob/followsme"):
		i.GETFollowsMe(w, r)
	case strings.HasPrefix(path, "/ob/isfollowing"):
		i.GETIsFollowing(w, r)
	case strings.HasPrefix(path, "/ob/order"):
		i.GETOrder(w, r)
	case strings.HasPrefix(path, "/ob/moderators"):
		i.GETModerators(w, r)
	case strings.HasPrefix(path, "/ob/case"):
		i.GETCase(w, r)
	case strings.HasPrefix(path, "/ob/chatmessages"):
		i.GETChatMessages(w, r)
	case strings.HasPrefix(path, "/ob/chatconversations"):
		i.GETChatConversations(w, r)
	case strings.HasPrefix(path, "/ob/notifications"):
		i.GETNotifications(w, r)
	case strings.HasPrefix(path, "/ob/images"):
		i.GETImage(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func patch(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.HasPrefix(path, "/ob/settings"):
		i.PATCHSettings(w, r)
	case strings.HasPrefix(path, "/ob/profile"):
		i.PATCHProfile(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}

func deleter(i *jsonAPIHandler, path string, w http.ResponseWriter, r *http.Request) {
	switch {
	case strings.HasPrefix(path, "/ob/moderator"):
		i.DELETEModerator(w, r)
	case strings.HasPrefix(path, "/ob/listing"):
		i.DELETEListing(w, r)
	case strings.HasPrefix(path, "/ob/chatmessage"):
		i.DELETEChatMessage(w, r)
	case strings.HasPrefix(path, "/ob/chatconversation"):
		i.DELETEChatConversation(w, r)
	case strings.HasPrefix(path, "/ob/notifications"):
		i.DELETENotification(w, r)
	default:
		ErrorResponse(w, http.StatusNotFound, "Not Found")
	}
}
<|endoftext|>


<commit_before>package contractor

// TODO: We are in the middle of migrating the contractor to a new concurrency
// model. The contractor should never call out to another package while under a
// lock (except for the proto package). This is because the renter is going to
// start calling contractor methods while holding the renter lock, so we need to
// be absolutely confident that no contractor thread will attempt to grab a
// renter lock.

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sync"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/renter/proto"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

var (
	errNilCS     = errors.New("cannot create contractor with nil consensus set")
	errNilTpool  = errors.New("cannot create contractor with nil transaction pool")
	errNilWallet = errors.New("cannot create contractor with nil wallet")

	// COMPATv1.0.4-lts
	// metricsContractID identifies a special contract that contains aggregate
	// financial metrics from older contractors
	metricsContractID = types.FileContractID{'m', 'e', 't', 'r', 'i', 'c', 's'}
)

// A Contractor negotiates, revises, renews, and provides access to file
// contracts.
type Contractor struct {
	// dependencies
	cs      consensusSet
	deps    modules.Dependencies
	hdb     hostDB
	log     *persist.Logger
	mu      sync.RWMutex
	persist persister
	tg      siasync.ThreadGroup
	tpool   transactionPool
	wallet  wallet

	// Only one thread should be performing contract maintenance at a time.
	interruptMaintenance chan struct{}
	maintenanceLock      siasync.TryMutex

	allowance     modules.Allowance
	blockHeight   types.BlockHeight
	currentPeriod types.BlockHeight
	lastChange    modules.ConsensusChangeID

	downloaders map[types.FileContractID]*hostDownloader
	editors     map[types.FileContractID]*hostEditor
	renewing    map[types.FileContractID]bool // prevent revising during renewal
	revising    map[types.FileContractID]bool // prevent overlapping revisions

	contracts    *proto.ContractSet
	oldContracts map[types.FileContractID]modules.RenterContract
	renewedIDs   map[types.FileContractID]types.FileContractID
}

// resolveID returns the ID of the most recent renewal of id.
func (c *Contractor) resolveID(id types.FileContractID) types.FileContractID {
	newID, exists := c.renewedIDs[id]
	for exists {
		id = newID
		newID, exists = c.renewedIDs[id]
	}
	return id
}

// Allowance returns the current allowance.
func (c *Contractor) Allowance() modules.Allowance {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.allowance
}

// PeriodSpending returns the amount spent on contracts during the current
// billing period.
func (c *Contractor) PeriodSpending() modules.ContractorSpending {
	c.mu.RLock()
	defer c.mu.RUnlock()

	var spending modules.ContractorSpending
	for _, contract := range c.contracts.ViewAll() {
		// Calculate ContractFees
		spending.ContractFees = spending.ContractFees.Add(contract.ContractFee)
		spending.ContractFees = spending.ContractFees.Add(contract.TxnFee)
		spending.ContractFees = spending.ContractFees.Add(contract.SiafundFee)
		// Calculate TotalAllocated
		spending.TotalAllocated = spending.TotalAllocated.Add(contract.TotalCost)
		spending.ContractSpendingDeprecated = spending.TotalAllocated
		// Calculate Spending
		spending.DownloadSpending = spending.DownloadSpending.Add(contract.DownloadSpending)
		spending.UploadSpending = spending.UploadSpending.Add(contract.UploadSpending)
		spending.StorageSpending = spending.StorageSpending.Add(contract.StorageSpending)
		// TODO: fix PreviousContracts
		// for _, pre := range contract.PreviousContracts {
		// 	spending.ContractSpending = spending.ContractSpending.Add(pre.TotalCost)
		// 	spending.DownloadSpending = spending.DownloadSpending.Add(pre.DownloadSpending)
		// 	spending.UploadSpending = spending.UploadSpending.Add(pre.UploadSpending)
		// 	spending.StorageSpending = spending.StorageSpending.Add(pre.StorageSpending)
		// }
	}
	// Calculate amount of spent money to get unspent money.
	allSpending := spending.ContractFees
	allSpending = allSpending.Add(spending.DownloadSpending)
	allSpending = allSpending.Add(spending.UploadSpending)
	allSpending = allSpending.Add(spending.StorageSpending)
	if c.allowance.Funds.Cmp(allSpending) >= 0 {
		spending.Unspent = c.allowance.Funds.Sub(allSpending)
	}

	return spending
}

// ContractByID returns the contract with the id specified, if it exists. The
// contract will be resolved if possible to the most recent child contract.
func (c *Contractor) ContractByID(id types.FileContractID) (modules.RenterContract, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.contracts.View(c.resolveID(id))
}

// Contracts returns the contracts formed by the contractor in the current
// allowance period. Only contracts formed with currently online hosts are
// returned.
func (c *Contractor) Contracts() []modules.RenterContract {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.contracts.ViewAll()
}

// ContractUtility returns the utility fields for the given contract.
func (c *Contractor) ContractUtility(id types.FileContractID) (modules.ContractUtility, bool) {
	return c.staticContractUtility(id)
}

// CurrentPeriod returns the height at which the current allowance period
// began.
func (c *Contractor) CurrentPeriod() types.BlockHeight {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.currentPeriod
}

// ResolveID returns the ID of the most recent renewal of id.
func (c *Contractor) ResolveID(id types.FileContractID) types.FileContractID {
	c.mu.RLock()
	newID := c.resolveID(id)
	c.mu.RUnlock()
	return newID
}

// SetRateLimits sets the bandwidth limits for connections created by the
// contractSet.
func (c *Contractor) SetRateLimits(readBPS, writeBPS int64, packetSize uint64) {
	c.contracts.SetRateLimits(readBPS, writeBPS, packetSize)
}

// Close closes the Contractor.
func (c *Contractor) Close() error {
	return c.tg.Stop()
}

// New returns a new Contractor.
func New(cs consensusSet, wallet walletShim, tpool transactionPool, hdb hostDB, persistDir string) (*Contractor, error) {
	// Check for nil inputs.
	if cs == nil {
		return nil, errNilCS
	}
	if wallet == nil {
		return nil, errNilWallet
	}
	if tpool == nil {
		return nil, errNilTpool
	}

	// Create the persist directory if it does not yet exist.
	if err := os.MkdirAll(persistDir, 0700); err != nil {
		return nil, err
	}

	// Convert the old persist file(s), if necessary. This must occur before
	// loading the contract set.
	if err := convertPersist(persistDir); err != nil {
		return nil, err
	}

	// Create the contract set.
	contractSet, err := proto.NewContractSet(filepath.Join(persistDir, "contracts"), modules.ProdDependencies)
	if err != nil {
		return nil, err
	}
	// Create the logger.
	logger, err := persist.NewFileLogger(filepath.Join(persistDir, "contractor.log"))
	if err != nil {
		return nil, err
	}

	// Create Contractor using production dependencies.
	return NewCustomContractor(cs, &WalletBridge{W: wallet}, tpool, hdb, contractSet, NewPersist(persistDir), logger, modules.ProdDependencies)
}

// NewCustomContractor creates a Contractor using the provided dependencies.
func NewCustomContractor(cs consensusSet, w wallet, tp transactionPool, hdb hostDB, contractSet *proto.ContractSet, p persister, l *persist.Logger, deps modules.Dependencies) (*Contractor, error) {
	// Create the Contractor object.
	c := &Contractor{
		cs:      cs,
		deps:    deps,
		hdb:     hdb,
		log:     l,
		persist: p,
		tpool:   tp,
		wallet:  w,

		interruptMaintenance: make(chan struct{}),

		contracts:    contractSet,
		downloaders:  make(map[types.FileContractID]*hostDownloader),
		editors:      make(map[types.FileContractID]*hostEditor),
		oldContracts: make(map[types.FileContractID]modules.RenterContract),
		renewedIDs:   make(map[types.FileContractID]types.FileContractID),
		renewing:     make(map[types.FileContractID]bool),
		revising:     make(map[types.FileContractID]bool),
	}

	// Close the contract set and logger upon shutdown.
	c.tg.AfterStop(func() {
		if err := c.contracts.Close(); err != nil {
			c.log.Println("Failed to close contract set:", err)
		}
		if err := c.log.Close(); err != nil {
			fmt.Println("Failed to close the contractor logger:", err)
		}
	})

	// Load the prior persistence structures.
	err := c.load()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	// Mark contract utility.
	c.managedMarkContractsUtility()

	// Subscribe to the consensus set.
	err = cs.ConsensusSetSubscribe(c, c.lastChange, c.tg.StopChan())
	if err == modules.ErrInvalidConsensusChangeID {
		// Reset the contractor consensus variables and try rescanning.
		c.blockHeight = 0
		c.lastChange = modules.ConsensusChangeBeginning
		err = cs.ConsensusSetSubscribe(c, c.lastChange, c.tg.StopChan())
	}
	if err != nil {
		return nil, errors.New("contractor subscription failed: " + err.Error())
	}
	// Unsubscribe from the consensus set upon shutdown.
	c.tg.OnStop(func() {
		cs.Unsubscribe(c)
	})

	// We may have upgraded persist or resubscribed. Save now so that we don't
	// lose our work.
	c.mu.Lock()
	err = c.save()
	c.mu.Unlock()
	if err != nil {
		return nil, err
	}

	return c, nil
}
<commit_msg>add todo<commit_after>package contractor

// TODO: We are in the middle of migrating the contractor to a new concurrency
// model. The contractor should never call out to another package while under a
// lock (except for the proto package). This is because the renter is going to
// start calling contractor methods while holding the renter lock, so we need to
// be absolutely confident that no contractor thread will attempt to grab a
// renter lock.

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"sync"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/renter/proto"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

var (
	errNilCS     = errors.New("cannot create contractor with nil consensus set")
	errNilTpool  = errors.New("cannot create contractor with nil transaction pool")
	errNilWallet = errors.New("cannot create contractor with nil wallet")

	// COMPATv1.0.4-lts
	// metricsContractID identifies a special contract that contains aggregate
	// financial metrics from older contractors
	metricsContractID = types.FileContractID{'m', 'e', 't', 'r', 'i', 'c', 's'}
)

// A Contractor negotiates, revises, renews, and provides access to file
// contracts.
type Contractor struct {
	// dependencies
	cs      consensusSet
	deps    modules.Dependencies
	hdb     hostDB
	log     *persist.Logger
	mu      sync.RWMutex
	persist persister
	tg      siasync.ThreadGroup
	tpool   transactionPool
	wallet  wallet

	// Only one thread should be performing contract maintenance at a time.
	interruptMaintenance chan struct{}
	maintenanceLock      siasync.TryMutex

	allowance     modules.Allowance
	blockHeight   types.BlockHeight
	currentPeriod types.BlockHeight
	lastChange    modules.ConsensusChangeID

	downloaders map[types.FileContractID]*hostDownloader
	editors     map[types.FileContractID]*hostEditor
	renewing    map[types.FileContractID]bool // prevent revising during renewal
	revising    map[types.FileContractID]bool // prevent overlapping revisions

	contracts    *proto.ContractSet
	oldContracts map[types.FileContractID]modules.RenterContract
	renewedIDs   map[types.FileContractID]types.FileContractID
}

// resolveID returns the ID of the most recent renewal of id.
func (c *Contractor) resolveID(id types.FileContractID) types.FileContractID {
	newID, exists := c.renewedIDs[id]
	for exists {
		id = newID
		newID, exists = c.renewedIDs[id]
	}
	return id
}

// Allowance returns the current allowance.
func (c *Contractor) Allowance() modules.Allowance {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.allowance
}

// PeriodSpending returns the amount spent on contracts during the current
// billing period.
func (c *Contractor) PeriodSpending() modules.ContractorSpending {
	c.mu.RLock()
	defer c.mu.RUnlock()

	var spending modules.ContractorSpending
	for _, contract := range c.contracts.ViewAll() {
		// Calculate ContractFees
		spending.ContractFees = spending.ContractFees.Add(contract.ContractFee)
		spending.ContractFees = spending.ContractFees.Add(contract.TxnFee)
		spending.ContractFees = spending.ContractFees.Add(contract.SiafundFee)
		// Calculate TotalAllocated
		spending.TotalAllocated = spending.TotalAllocated.Add(contract.TotalCost)
		spending.ContractSpendingDeprecated = spending.TotalAllocated
		// Calculate Spending
		spending.DownloadSpending = spending.DownloadSpending.Add(contract.DownloadSpending)
		spending.UploadSpending = spending.UploadSpending.Add(contract.UploadSpending)
		spending.StorageSpending = spending.StorageSpending.Add(contract.StorageSpending)
		// TODO: fix PreviousContracts
		// for _, pre := range contract.PreviousContracts {
		// 	spending.ContractSpending = spending.ContractSpending.Add(pre.TotalCost)
		// 	spending.DownloadSpending = spending.DownloadSpending.Add(pre.DownloadSpending)
		// 	spending.UploadSpending = spending.UploadSpending.Add(pre.UploadSpending)
		// 	spending.StorageSpending = spending.StorageSpending.Add(pre.StorageSpending)
		// }
	}
	// Calculate amount of spent money to get unspent money.
	allSpending := spending.ContractFees
	allSpending = allSpending.Add(spending.DownloadSpending)
	allSpending = allSpending.Add(spending.UploadSpending)
	allSpending = allSpending.Add(spending.StorageSpending)
	if c.allowance.Funds.Cmp(allSpending) >= 0 {
		spending.Unspent = c.allowance.Funds.Sub(allSpending)
	}

	return spending
}

// ContractByID returns the contract with the id specified, if it exists. The
// contract will be resolved if possible to the most recent child contract.
func (c *Contractor) ContractByID(id types.FileContractID) (modules.RenterContract, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.contracts.View(c.resolveID(id))
}

// Contracts returns the contracts formed by the contractor in the current
// allowance period. Only contracts formed with currently online hosts are
// returned.
func (c *Contractor) Contracts() []modules.RenterContract {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.contracts.ViewAll()
}

// ContractUtility returns the utility fields for the given contract.
func (c *Contractor) ContractUtility(id types.FileContractID) (modules.ContractUtility, bool) {
	return c.staticContractUtility(id)
}

// CurrentPeriod returns the height at which the current allowance period
// began.
func (c *Contractor) CurrentPeriod() types.BlockHeight {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.currentPeriod
}

// ResolveID returns the ID of the most recent renewal of id.
func (c *Contractor) ResolveID(id types.FileContractID) types.FileContractID {
	c.mu.RLock()
	newID := c.resolveID(id)
	c.mu.RUnlock()
	return newID
}

// SetRateLimits sets the bandwidth limits for connections created by the
// contractSet.
func (c *Contractor) SetRateLimits(readBPS, writeBPS int64, packetSize uint64) {
	c.contracts.SetRateLimits(readBPS, writeBPS, packetSize)
}

// Close closes the Contractor.
func (c *Contractor) Close() error {
	return c.tg.Stop()
}

// New returns a new Contractor.
func New(cs consensusSet, wallet walletShim, tpool transactionPool, hdb hostDB, persistDir string) (*Contractor, error) {
	// Check for nil inputs.
	if cs == nil {
		return nil, errNilCS
	}
	if wallet == nil {
		return nil, errNilWallet
	}
	if tpool == nil {
		return nil, errNilTpool
	}

	// Create the persist directory if it does not yet exist.
	if err := os.MkdirAll(persistDir, 0700); err != nil {
		return nil, err
	}

	// Convert the old persist file(s), if necessary. This must occur before
	// loading the contract set.
	if err := convertPersist(persistDir); err != nil {
		return nil, err
	}

	// Create the contract set.
	contractSet, err := proto.NewContractSet(filepath.Join(persistDir, "contracts"), modules.ProdDependencies)
	if err != nil {
		return nil, err
	}
	// Create the logger.
	logger, err := persist.NewFileLogger(filepath.Join(persistDir, "contractor.log"))
	if err != nil {
		return nil, err
	}

	// Create Contractor using production dependencies.
	return NewCustomContractor(cs, &WalletBridge{W: wallet}, tpool, hdb, contractSet, NewPersist(persistDir), logger, modules.ProdDependencies)
}

// NewCustomContractor creates a Contractor using the provided dependencies.
func NewCustomContractor(cs consensusSet, w wallet, tp transactionPool, hdb hostDB, contractSet *proto.ContractSet, p persister, l *persist.Logger, deps modules.Dependencies) (*Contractor, error) {
	// Create the Contractor object.
	c := &Contractor{
		cs:      cs,
		deps:    deps,
		hdb:     hdb,
		log:     l,
		persist: p,
		tpool:   tp,
		wallet:  w,

		interruptMaintenance: make(chan struct{}),

		contracts:    contractSet,
		downloaders:  make(map[types.FileContractID]*hostDownloader),
		editors:      make(map[types.FileContractID]*hostEditor),
		oldContracts: make(map[types.FileContractID]modules.RenterContract),
		renewedIDs:   make(map[types.FileContractID]types.FileContractID),
		renewing:     make(map[types.FileContractID]bool),
		revising:     make(map[types.FileContractID]bool),
	}

	// Close the contract set and logger upon shutdown.
	c.tg.AfterStop(func() {
		if err := c.contracts.Close(); err != nil {
			c.log.Println("Failed to close contract set:", err)
		}
		if err := c.log.Close(); err != nil {
			fmt.Println("Failed to close the contractor logger:", err)
		}
	})

	// Load the prior persistence structures.
	err := c.load()
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	// Mark contract utility.
	// TODO we probably have to force this to succeed even if there is a scan
	// going on. Otherwise all the contract utilities might end up
	// uninitialized.
	c.managedMarkContractsUtility()

	// Subscribe to the consensus set.
	err = cs.ConsensusSetSubscribe(c, c.lastChange, c.tg.StopChan())
	if err == modules.ErrInvalidConsensusChangeID {
		// Reset the contractor consensus variables and try rescanning.
		c.blockHeight = 0
		c.lastChange = modules.ConsensusChangeBeginning
		err = cs.ConsensusSetSubscribe(c, c.lastChange, c.tg.StopChan())
	}
	if err != nil {
		return nil, errors.New("contractor subscription failed: " + err.Error())
	}
	// Unsubscribe from the consensus set upon shutdown.
	c.tg.OnStop(func() {
		cs.Unsubscribe(c)
	})

	// We may have upgraded persist or resubscribed. Save now so that we don't
	// lose our work.
	c.mu.Lock()
	err = c.save()
	c.mu.Unlock()
	if err != nil {
		return nil, err
	}

	return c, nil
}
<|endoftext|>


<commit_before><commit_msg>Add basic tests using provided examples<commit_after><|endoftext|>


<commit_before>// Copyright 2015 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1

import (
	"errors"
	"fmt"

	"github.com/coreos/clair/database"
	"github.com/coreos/clair/utils/types"
)

type Error struct {
	Message string `json:"Layer`
}

type Layer struct {
	Name             string    `json:"Name,omitempty"`
	Namespace        string    `json:"Namespace,omitempty"`
	Path             string    `json:"Path,omitempty"`
	ParentName       string    `json:"ParentName,omitempty"`
	Format           string    `json:"Format,omitempty"`
	IndexedByVersion int       `json:"IndexedByVersion,omitempty"`
	Features         []Feature `json:"Features,omitempty"`
}

func LayerFromDatabaseModel(dbLayer database.Layer, withFeatures, withVulnerabilities bool) Layer {
	layer := Layer{
		Name:             dbLayer.Name,
		IndexedByVersion: dbLayer.EngineVersion,
	}

	if dbLayer.Parent != nil {
		layer.ParentName = dbLayer.Parent.Name
	}

	if dbLayer.Namespace != nil {
		layer.Namespace = dbLayer.Namespace.Name
	}

	if withFeatures || withVulnerabilities && dbLayer.Features != nil {
		for _, dbFeatureVersion := range dbLayer.Features {
			feature := Feature{
				Name:      dbFeatureVersion.Feature.Name,
				Namespace: dbFeatureVersion.Feature.Namespace.Name,
				Version:   dbFeatureVersion.Version.String(),
			}

			for _, dbVuln := range dbFeatureVersion.AffectedBy {
				vuln := Vulnerability{
					Name:        dbVuln.Name,
					Namespace:   dbVuln.Namespace.Name,
					Description: dbVuln.Description,
					Severity:    string(dbVuln.Severity),
					Metadata:    dbVuln.Metadata,
				}

				if dbVuln.FixedBy != types.MaxVersion {
					vuln.FixedBy = dbVuln.FixedBy.String()
				}
				feature.Vulnerabilities = append(feature.Vulnerabilities, vuln)
			}
			layer.Features = append(layer.Features, feature)
		}
	}

	return layer
}

type Vulnerability struct {
	Name        string                 `json:"Name,omitempty"`
	Namespace   string                 `json:"Namespace,omitempty"`
	Description string                 `json:"Description,omitempty"`
	Link        string                 `json:"Link,omitempty"`
	Severity    string                 `json:"Severity,omitempty"`
	Metadata    map[string]interface{} `json:"Metadata,omitempty"`
	FixedBy     string                 `json:"FixedBy,omitempty"`
	FixedIn     []Feature              `json:"FixedIn,omitempty"`
}

func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
	severity := types.Priority(v.Severity)
	if !severity.IsValid() {
		return database.Vulnerability{}, errors.New("Invalid severity")
	}

	var dbFeatures []database.FeatureVersion
	for _, feature := range v.FixedIn {
		version, err := types.NewVersion(feature.Version)
		if err != nil {
			return database.Vulnerability{}, err
		}

		dbFeatures = append(dbFeatures, database.FeatureVersion{
			Feature: database.Feature{
				Name:      feature.Name,
				Namespace: database.Namespace{Name: feature.Namespace},
			},
			Version: version,
		})
	}

	return database.Vulnerability{
		Name:        v.Name,
		Namespace:   database.Namespace{Name: v.Namespace},
		Description: v.Description,
		Link:        v.Link,
		Severity:    severity,
		Metadata:    v.Metadata,
		FixedIn:     dbFeatures,
	}, nil
}

func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability, withFixedIn bool) Vulnerability {
	vuln := Vulnerability{
		Name:        dbVuln.Name,
		Namespace:   dbVuln.Namespace.Name,
		Description: dbVuln.Description,
		Link:        dbVuln.Link,
		Severity:    string(dbVuln.Severity),
		Metadata:    dbVuln.Metadata,
	}

	if withFixedIn {
		for _, dbFeatureVersion := range dbVuln.FixedIn {
			vuln.FixedIn = append(vuln.FixedIn, Feature{
				Name:      dbFeatureVersion.Feature.Name,
				Namespace: dbFeatureVersion.Feature.Namespace.Name,
				Version:   dbFeatureVersion.Version.String(),
			})
		}
	}

	return vuln
}

type Feature struct {
	Name            string          `json:"Name,omitempty"`
	Namespace       string          `json:"Namespace,omitempty"`
	Version         string          `json:"Version,omitempty"`
	Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
}

type Notification struct {
	Name     string                   `json:"Name,omitempty"`
	Created  string                   `json:"Created,omitempty"`
	Notified string                   `json:"Notified,omitempty"`
	Deleted  string                   `json:"Deleted,omitempty"`
	Limit    int                      `json:"Limit,omitempty"`
	Page     string                   `json:"Page,omitempty"`
	NextPage string                   `json:"NextPage,omitempty"`
	Old      *VulnerabilityWithLayers `json:"Old,omitempty"`
	New      VulnerabilityWithLayers  `json:"New,omitempty"`
	Changed  []string                 `json:"Changed,omitempty"`
}

func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotification, limit int, page, nextPage database.VulnerabilityNotificationPageNumber) Notification {
	var oldVuln *VulnerabilityWithLayers
	if dbNotification.OldVulnerability != nil {
		*oldVuln = VulnerabilityWithLayersFromDatabaseModel(*dbNotification.OldVulnerability)
	}

	// TODO(jzelinskie): implement "changed" key
	return Notification{
		Name:     dbNotification.Name,
		Created:  fmt.Sprintf("%d", dbNotification.Created.Unix()),
		Notified: fmt.Sprintf("%d", dbNotification.Notified.Unix()),
		Deleted:  fmt.Sprintf("%d", dbNotification.Deleted.Unix()),
		Limit:    limit,
		Page:     DBPageNumberToString(page),
		NextPage: DBPageNumberToString(nextPage),
		Old:      oldVuln,
		New:      VulnerabilityWithLayersFromDatabaseModel(dbNotification.NewVulnerability),
	}
}

type VulnerabilityWithLayers struct {
	Vulnerability                  *Vulnerability `json:"Vulnerability,omitempty"`
	LayersIntroducingVulnerability []string       `json:"LayersIntroducingVulnerability,omitempty"`
}

func VulnerabilityWithLayersFromDatabaseModel(dbVuln database.Vulnerability) VulnerabilityWithLayers {
	vuln := VulnerabilityFromDatabaseModel(dbVuln, true)

	var layers []string
	for _, layer := range dbVuln.LayersIntroducingVulnerability {
		layers = append(layers, layer.Name)
	}

	return VulnerabilityWithLayers{
		Vulnerability:                  &vuln,
		LayersIntroducingVulnerability: layers,
	}
}

type LayerEnvelope struct {
	Layer *Layer `json:"Layer,omitempty"`
	Error *Error `json:"Error,omitempty"`
}

type NamespaceEnvelope struct {
	Namespaces *[]string `json:"Namespaces,omitempty"`
	Error      *Error    `json:"Error,omitempty"`
}

type VulnerabilityEnvelope struct {
	Vulnerability *Vulnerability `json:"Vulnerability,omitempty"`
	Error         *Error         `json:"Error,omitempty"`
}

type NotificationEnvelope struct {
	Notification *Notification `json:"Notification,omitempty"`
	Error        *Error        `json:"Error,omitempty"`
}

func pageStringToDBPageNumber(pageStr string) (database.VulnerabilityNotificationPageNumber, error) {
	// TODO(jzelinskie): turn pagination into an encrypted token
	var old, new int
	_, err := fmt.Sscanf(pageStr, "%d-%d", &old, &new)
	return database.VulnerabilityNotificationPageNumber{old, new}, err
}

func DBPageNumberToString(page database.VulnerabilityNotificationPageNumber) string {
	// TODO(jzelinskie): turn pagination into an encrypted token
	return fmt.Sprintf("%d-%d", page.OldVulnerability, page.NewVulnerability)
}
<commit_msg>api: handle last page for notifications<commit_after>// Copyright 2015 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1

import (
	"errors"
	"fmt"

	"github.com/coreos/clair/database"
	"github.com/coreos/clair/utils/types"
)

type Error struct {
	Message string `json:"Layer`
}

type Layer struct {
	Name             string    `json:"Name,omitempty"`
	Namespace        string    `json:"Namespace,omitempty"`
	Path             string    `json:"Path,omitempty"`
	ParentName       string    `json:"ParentName,omitempty"`
	Format           string    `json:"Format,omitempty"`
	IndexedByVersion int       `json:"IndexedByVersion,omitempty"`
	Features         []Feature `json:"Features,omitempty"`
}

func LayerFromDatabaseModel(dbLayer database.Layer, withFeatures, withVulnerabilities bool) Layer {
	layer := Layer{
		Name:             dbLayer.Name,
		IndexedByVersion: dbLayer.EngineVersion,
	}

	if dbLayer.Parent != nil {
		layer.ParentName = dbLayer.Parent.Name
	}

	if dbLayer.Namespace != nil {
		layer.Namespace = dbLayer.Namespace.Name
	}

	if withFeatures || withVulnerabilities && dbLayer.Features != nil {
		for _, dbFeatureVersion := range dbLayer.Features {
			feature := Feature{
				Name:      dbFeatureVersion.Feature.Name,
				Namespace: dbFeatureVersion.Feature.Namespace.Name,
				Version:   dbFeatureVersion.Version.String(),
			}

			for _, dbVuln := range dbFeatureVersion.AffectedBy {
				vuln := Vulnerability{
					Name:        dbVuln.Name,
					Namespace:   dbVuln.Namespace.Name,
					Description: dbVuln.Description,
					Severity:    string(dbVuln.Severity),
					Metadata:    dbVuln.Metadata,
				}

				if dbVuln.FixedBy != types.MaxVersion {
					vuln.FixedBy = dbVuln.FixedBy.String()
				}
				feature.Vulnerabilities = append(feature.Vulnerabilities, vuln)
			}
			layer.Features = append(layer.Features, feature)
		}
	}

	return layer
}

type Vulnerability struct {
	Name        string                 `json:"Name,omitempty"`
	Namespace   string                 `json:"Namespace,omitempty"`
	Description string                 `json:"Description,omitempty"`
	Link        string                 `json:"Link,omitempty"`
	Severity    string                 `json:"Severity,omitempty"`
	Metadata    map[string]interface{} `json:"Metadata,omitempty"`
	FixedBy     string                 `json:"FixedBy,omitempty"`
	FixedIn     []Feature              `json:"FixedIn,omitempty"`
}

func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
	severity := types.Priority(v.Severity)
	if !severity.IsValid() {
		return database.Vulnerability{}, errors.New("Invalid severity")
	}

	var dbFeatures []database.FeatureVersion
	for _, feature := range v.FixedIn {
		version, err := types.NewVersion(feature.Version)
		if err != nil {
			return database.Vulnerability{}, err
		}

		dbFeatures = append(dbFeatures, database.FeatureVersion{
			Feature: database.Feature{
				Name:      feature.Name,
				Namespace: database.Namespace{Name: feature.Namespace},
			},
			Version: version,
		})
	}

	return database.Vulnerability{
		Name:        v.Name,
		Namespace:   database.Namespace{Name: v.Namespace},
		Description: v.Description,
		Link:        v.Link,
		Severity:    severity,
		Metadata:    v.Metadata,
		FixedIn:     dbFeatures,
	}, nil
}

func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability, withFixedIn bool) Vulnerability {
	vuln := Vulnerability{
		Name:        dbVuln.Name,
		Namespace:   dbVuln.Namespace.Name,
		Description: dbVuln.Description,
		Link:        dbVuln.Link,
		Severity:    string(dbVuln.Severity),
		Metadata:    dbVuln.Metadata,
	}

	if withFixedIn {
		for _, dbFeatureVersion := range dbVuln.FixedIn {
			vuln.FixedIn = append(vuln.FixedIn, Feature{
				Name:      dbFeatureVersion.Feature.Name,
				Namespace: dbFeatureVersion.Feature.Namespace.Name,
				Version:   dbFeatureVersion.Version.String(),
			})
		}
	}

	return vuln
}

type Feature struct {
	Name            string          `json:"Name,omitempty"`
	Namespace       string          `json:"Namespace,omitempty"`
	Version         string          `json:"Version,omitempty"`
	Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
}

type Notification struct {
	Name     string                   `json:"Name,omitempty"`
	Created  string                   `json:"Created,omitempty"`
	Notified string                   `json:"Notified,omitempty"`
	Deleted  string                   `json:"Deleted,omitempty"`
	Limit    int                      `json:"Limit,omitempty"`
	Page     string                   `json:"Page,omitempty"`
	NextPage string                   `json:"NextPage,omitempty"`
	Old      *VulnerabilityWithLayers `json:"Old,omitempty"`
	New      VulnerabilityWithLayers  `json:"New,omitempty"`
	Changed  []string                 `json:"Changed,omitempty"`
}

func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotification, limit int, page, nextPage database.VulnerabilityNotificationPageNumber) Notification {
	var oldVuln *VulnerabilityWithLayers
	if dbNotification.OldVulnerability != nil {
		*oldVuln = VulnerabilityWithLayersFromDatabaseModel(*dbNotification.OldVulnerability)
	}

	var nextPageStr string
	if nextPage != database.NoVulnerabilityNotificationPage {
		nextPageStr = DBPageNumberToString(nextPage)
	}

	// TODO(jzelinskie): implement "changed" key
	return Notification{
		Name:     dbNotification.Name,
		Created:  fmt.Sprintf("%d", dbNotification.Created.Unix()),
		Notified: fmt.Sprintf("%d", dbNotification.Notified.Unix()),
		Deleted:  fmt.Sprintf("%d", dbNotification.Deleted.Unix()),
		Limit:    limit,
		Page:     DBPageNumberToString(page),
		NextPage: nextPageStr,
		Old:      oldVuln,
		New:      VulnerabilityWithLayersFromDatabaseModel(dbNotification.NewVulnerability),
	}
}

type VulnerabilityWithLayers struct {
	Vulnerability                  *Vulnerability `json:"Vulnerability,omitempty"`
	LayersIntroducingVulnerability []string       `json:"LayersIntroducingVulnerability,omitempty"`
}

func VulnerabilityWithLayersFromDatabaseModel(dbVuln database.Vulnerability) VulnerabilityWithLayers {
	vuln := VulnerabilityFromDatabaseModel(dbVuln, true)

	var layers []string
	for _, layer := range dbVuln.LayersIntroducingVulnerability {
		layers = append(layers, layer.Name)
	}

	return VulnerabilityWithLayers{
		Vulnerability:                  &vuln,
		LayersIntroducingVulnerability: layers,
	}
}

type LayerEnvelope struct {
	Layer *Layer `json:"Layer,omitempty"`
	Error *Error `json:"Error,omitempty"`
}

type NamespaceEnvelope struct {
	Namespaces *[]string `json:"Namespaces,omitempty"`
	Error      *Error    `json:"Error,omitempty"`
}

type VulnerabilityEnvelope struct {
	Vulnerability *Vulnerability `json:"Vulnerability,omitempty"`
	Error         *Error         `json:"Error,omitempty"`
}

type NotificationEnvelope struct {
	Notification *Notification `json:"Notification,omitempty"`
	Error        *Error        `json:"Error,omitempty"`
}

func pageStringToDBPageNumber(pageStr string) (database.VulnerabilityNotificationPageNumber, error) {
	// TODO(jzelinskie): turn pagination into an encrypted token
	var old, new int
	_, err := fmt.Sscanf(pageStr, "%d-%d", &old, &new)
	return database.VulnerabilityNotificationPageNumber{old, new}, err
}

func DBPageNumberToString(page database.VulnerabilityNotificationPageNumber) string {
	// TODO(jzelinskie): turn pagination into an encrypted token
	return fmt.Sprintf("%d-%d", page.OldVulnerability, page.NewVulnerability)
}
<|endoftext|>


<commit_before>package runrunc_test

import (
	"errors"
	"os/exec"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagertest"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"code.cloudfoundry.org/commandrunner/fake_command_runner"
	. "code.cloudfoundry.org/commandrunner/fake_command_runner/matchers"
	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/rundmc/runrunc"
	fakes "code.cloudfoundry.org/guardian/rundmc/runrunc/runruncfakes"
)

var _ = Describe("Stats", func() {
	var (
		commandRunner *fake_command_runner.FakeCommandRunner
		runner        *fakes.FakeRuncCmdRunner
		runcBinary    *fakes.FakeRuncBinary
		depot         *fakes.FakeDepot
		logger        *lagertest.TestLogger

		statser *runrunc.Statser
	)

	BeforeEach(func() {
		runcBinary = new(fakes.FakeRuncBinary)
		depot = new(fakes.FakeDepot)
		commandRunner = fake_command_runner.New()
		runner = new(fakes.FakeRuncCmdRunner)
		logger = lagertest.NewTestLogger("test")

		statser = runrunc.NewStatser(runner, runcBinary, depot)

		runcBinary.StatsCommandStub = func(id string, logFile string) *exec.Cmd {
			return exec.Command("funC-stats", "--log", logFile, id)
		}

		runcBinary.StateCommandStub = func(id string, logFile string) *exec.Cmd {
			return exec.Command("funC-state", "--log", logFile, id)
		}

		runner.RunAndLogStub = func(_ lager.Logger, fn runrunc.LoggingCmd) error {
			return commandRunner.Run(fn("potato.log"))
		}
	})

	Context("when runC reports valid JSON", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{
					"type": "stats",
					"data": {
						"cpu": {
							"usage": {
								"total": 1,
								"kernel": 2,
								"user": 3
							}
						},
						"memory": {
							"raw": {
								"active_anon": 1,
								"active_file": 2,
								"cache": 3,
								"hierarchical_memory_limit": 4,
								"inactive_anon": 5,
								"inactive_file": 6,
								"mapped_file": 7,
								"pgfault": 8,
								"pgmajfault": 9,
								"pgpgin": 10,
								"pgpgout": 11,
								"rss": 12,
								"rss_huge": 13,
								"total_active_anon": 14,
								"total_active_file": 15,
								"total_cache": 16,
								"total_inactive_anon": 17,
								"total_inactive_file": 18,
								"total_mapped_file": 19,
								"total_pgfault": 20,
								"total_pgmajfault": 21,
								"total_pgpgin": 22,
								"total_pgpgout": 23,
								"total_rss": 24,
								"total_rss_huge": 25,
								"total_unevictable": 26,
								"total_writeback": 27,
								"unevictable": 28,
								"writeback": 29,
								"swap": 30,
								"hierarchical_memsw_limit": 31,
								"total_swap": 32
							}
						},
						"pids": {
							"current": 33,
							"limit": 34
            }
					}
				}`))
				return nil
			})

			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{
					"created": "2018-08-17T11:05:57.464894007Z"
				}`))
				return nil
			})
		})

		It("parses the CPU stats", func() {
			stats, err := statser.Stats(logger, "some-handle")
			Expect(err).NotTo(HaveOccurred())

			Expect(stats.CPU).To(Equal(garden.ContainerCPUStat{
				Usage:  1,
				System: 2,
				User:   3,
			}))
		})

		It("shows container's age", func() {
			initialStats, err := statser.Stats(logger, "some-handle")
			Expect(err).NotTo(HaveOccurred())

			time.Sleep(5 * time.Millisecond)

			subsequentStats, err := statser.Stats(logger, "some-handle")
			Expect(err).NotTo(HaveOccurred())

			Expect(subsequentStats.Age).To(BeNumerically(">", initialStats.Age))
		})

		It("parses the memory stats", func() {
			stats, err := statser.Stats(logger, "some-handle")
			Expect(err).NotTo(HaveOccurred())

			Expect(stats.Memory).To(Equal(garden.ContainerMemoryStat{
				ActiveAnon:              1,
				ActiveFile:              2,
				Cache:                   3,
				HierarchicalMemoryLimit: 4,
				InactiveAnon:            5,
				InactiveFile:            6,
				MappedFile:              7,
				Pgfault:                 8,
				Pgmajfault:              9,
				Pgpgin:                  10,
				Pgpgout:                 11,
				Rss:                     12,
				TotalActiveAnon:         14,
				TotalActiveFile:         15,
				TotalCache:              16,
				TotalInactiveAnon:       17,
				TotalInactiveFile:       18,
				TotalMappedFile:         19,
				TotalPgfault:            20,
				TotalPgmajfault:         21,
				TotalPgpgin:             22,
				TotalPgpgout:            23,
				TotalRss:                24,
				TotalUnevictable:        26,
				Unevictable:             28,
				Swap:                    30,
				HierarchicalMemswLimit:  31,
				TotalSwap:               32,
				TotalUsageTowardLimit:   22,
			}))
		})

		It("parses the Pid stats", func() {
			stats, err := statser.Stats(logger, "some-handle")
			Expect(err).NotTo(HaveOccurred())

			Expect(stats.Pid).To(Equal(garden.ContainerPidStat{
				Current: 33,
				Max:     34,
			}))
		})

		It("forwards logs from runc", func() {
			_, err := statser.Stats(logger, "some-container")
			Expect(err).NotTo(HaveOccurred())

			Expect(commandRunner).To(HaveExecutedSerially(
				fake_command_runner.CommandSpec{
					Path: "funC-stats",
					Args: []string{"--log", "potato.log", "some-container"},
				},
				fake_command_runner.CommandSpec{
					Path: "funC-state",
					Args: []string{"--log", "potato.log", "some-container"},
				},
			))
		})
	})

	Context("when the JSON does not contain a 'created' field", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{ "type": "stats" }`))
				return nil
			})

			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{}`))
				return nil
			})
		})

		It("fetches the container age from the depot", func() {
			depot.CreatedTimeReturns(time.Now().Add(-time.Hour), nil)

			stats, err := statser.Stats(logger, "some-handle")
			Expect(err).NotTo(HaveOccurred())
			Expect(depot.CreatedTimeCallCount()).To(Equal(1))

			Expect(stats.Age).To(BeNumerically(">=", time.Hour))
			Expect(stats.Age).To(BeNumerically("<", time.Second*3601))
		})

		Context("when the depot cannot return the container created time", func() {
			It("forwards the error", func() {
				depot.CreatedTimeReturns(time.Time{}, errors.New("Bad depot"))

				_, err := statser.Stats(logger, "some-handle")
				Expect(err).To(MatchError("Bad depot"))
			})
		})
	})

	Context("when runC stats reports invalid JSON", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{ banana potato banana potato }`))

				return nil
			})
		})

		It("should return an error", func() {
			_, err := statser.Stats(logger, "some-container")
			Expect(err).To(MatchError(ContainSubstring("decode stats")))
		})
	})

	Context("when runC state reports invalid JSON", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{}`))
				return nil
			})
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{ banana potato banana potato }`))

				return nil
			})
		})

		It("should return an error", func() {
			_, err := statser.Stats(logger, "some-container")
			Expect(err).To(MatchError(ContainSubstring("decode state")))
		})
	})

	Context("when runC stats fails", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				return errors.New("banana")
			})
		})

		It("returns an error", func() {
			_, err := statser.Stats(logger, "some-container")
			Expect(err).To(MatchError(ContainSubstring("runC stats: banana")))
		})
	})

	Context("when runC state fails", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{}`))
				return nil
			})
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				return errors.New("banana")
			})
		})

		It("returns an error", func() {
			_, err := statser.Stats(logger, "some-container")
			Expect(err).To(MatchError(ContainSubstring("runC state: banana")))
		})
	})
})
<commit_msg>Introduce JustBeforeEach in the Statser spec<commit_after>package runrunc_test

import (
	"errors"
	"os/exec"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagertest"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"code.cloudfoundry.org/commandrunner/fake_command_runner"
	. "code.cloudfoundry.org/commandrunner/fake_command_runner/matchers"
	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gardener"
	"code.cloudfoundry.org/guardian/rundmc/runrunc"
	fakes "code.cloudfoundry.org/guardian/rundmc/runrunc/runruncfakes"
)

var _ = Describe("Stats", func() {
	var (
		commandRunner *fake_command_runner.FakeCommandRunner
		runner        *fakes.FakeRuncCmdRunner
		runcBinary    *fakes.FakeRuncBinary
		depot         *fakes.FakeDepot
		logger        *lagertest.TestLogger

		statser *runrunc.Statser

		stats  gardener.StatsContainerMetrics
		err    error
		handle = "some-handle"
	)

	BeforeEach(func() {
		runcBinary = new(fakes.FakeRuncBinary)
		depot = new(fakes.FakeDepot)
		commandRunner = fake_command_runner.New()
		runner = new(fakes.FakeRuncCmdRunner)
		logger = lagertest.NewTestLogger("test")

		statser = runrunc.NewStatser(runner, runcBinary, depot)

		runcBinary.StatsCommandStub = func(id string, logFile string) *exec.Cmd {
			return exec.Command("funC-stats", "--log", logFile, id)
		}

		runcBinary.StateCommandStub = func(id string, logFile string) *exec.Cmd {
			return exec.Command("funC-state", "--log", logFile, id)
		}

		runner.RunAndLogStub = func(_ lager.Logger, fn runrunc.LoggingCmd) error {
			return commandRunner.Run(fn("potato.log"))
		}
	})

	JustBeforeEach(func() {
		stats, err = statser.Stats(logger, handle)
	})

	Context("when runC reports valid JSON", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{
					"type": "stats",
					"data": {
						"cpu": {
							"usage": {
								"total": 1,
								"kernel": 2,
								"user": 3
							}
						},
						"memory": {
							"raw": {
								"active_anon": 1,
								"active_file": 2,
								"cache": 3,
								"hierarchical_memory_limit": 4,
								"inactive_anon": 5,
								"inactive_file": 6,
								"mapped_file": 7,
								"pgfault": 8,
								"pgmajfault": 9,
								"pgpgin": 10,
								"pgpgout": 11,
								"rss": 12,
								"rss_huge": 13,
								"total_active_anon": 14,
								"total_active_file": 15,
								"total_cache": 16,
								"total_inactive_anon": 17,
								"total_inactive_file": 18,
								"total_mapped_file": 19,
								"total_pgfault": 20,
								"total_pgmajfault": 21,
								"total_pgpgin": 22,
								"total_pgpgout": 23,
								"total_rss": 24,
								"total_rss_huge": 25,
								"total_unevictable": 26,
								"total_writeback": 27,
								"unevictable": 28,
								"writeback": 29,
								"swap": 30,
								"hierarchical_memsw_limit": 31,
								"total_swap": 32
							}
						},
						"pids": {
							"current": 33,
							"limit": 34
            }
					}
				}`))
				return nil
			})

			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{
					"created": "2018-08-17T11:05:57.464894007Z"
				}`))
				return nil
			})
		})

		It("succeeds", func() {
			Expect(err).NotTo(HaveOccurred())
		})

		It("parses the CPU stats", func() {
			Expect(stats.CPU).To(Equal(garden.ContainerCPUStat{
				Usage:  1,
				System: 2,
				User:   3,
			}))
		})

		It("shows container's age", func() {
			time.Sleep(5 * time.Millisecond)

			subsequentStats, err := statser.Stats(logger, handle)
			Expect(err).NotTo(HaveOccurred())

			Expect(subsequentStats.Age).To(BeNumerically(">", stats.Age))
		})

		It("parses the memory stats", func() {
			Expect(stats.Memory).To(Equal(garden.ContainerMemoryStat{
				ActiveAnon:              1,
				ActiveFile:              2,
				Cache:                   3,
				HierarchicalMemoryLimit: 4,
				InactiveAnon:            5,
				InactiveFile:            6,
				MappedFile:              7,
				Pgfault:                 8,
				Pgmajfault:              9,
				Pgpgin:                  10,
				Pgpgout:                 11,
				Rss:                     12,
				TotalActiveAnon:         14,
				TotalActiveFile:         15,
				TotalCache:              16,
				TotalInactiveAnon:       17,
				TotalInactiveFile:       18,
				TotalMappedFile:         19,
				TotalPgfault:            20,
				TotalPgmajfault:         21,
				TotalPgpgin:             22,
				TotalPgpgout:            23,
				TotalRss:                24,
				TotalUnevictable:        26,
				Unevictable:             28,
				Swap:                    30,
				HierarchicalMemswLimit:  31,
				TotalSwap:               32,
				TotalUsageTowardLimit:   22,
			}))
		})

		It("parses the Pid stats", func() {
			Expect(stats.Pid).To(Equal(garden.ContainerPidStat{
				Current: 33,
				Max:     34,
			}))
		})

		It("forwards logs from runc", func() {
			Expect(commandRunner).To(HaveExecutedSerially(
				fake_command_runner.CommandSpec{
					Path: "funC-stats",
					Args: []string{"--log", "potato.log", handle},
				},
				fake_command_runner.CommandSpec{
					Path: "funC-state",
					Args: []string{"--log", "potato.log", handle},
				},
			))
		})
	})

	Context("when the JSON does not contain a 'created' field", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{ "type": "stats" }`))
				return nil
			})

			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{}`))
				return nil
			})

			depot.CreatedTimeReturns(time.Now().Add(-time.Hour), nil)
		})

		It("fetches the container age from the depot", func() {
			Expect(depot.CreatedTimeCallCount()).To(Equal(1))

			Expect(stats.Age).To(BeNumerically(">=", time.Hour))
			Expect(stats.Age).To(BeNumerically("<", time.Second*3601))
		})

		Context("when the depot cannot return the container created time", func() {
			BeforeEach(func() {
				depot.CreatedTimeReturns(time.Time{}, errors.New("Bad depot"))
			})

			It("forwards the error", func() {
				Expect(err).To(MatchError("Bad depot"))
			})
		})
	})

	Context("when runC stats reports invalid JSON", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{ banana potato banana potato }`))

				return nil
			})
		})

		It("should return an error", func() {
			Expect(err).To(MatchError(ContainSubstring("decode stats")))
		})
	})

	Context("when runC state reports invalid JSON", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{}`))
				return nil
			})
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{ banana potato banana potato }`))

				return nil
			})
		})

		It("should return an error", func() {
			Expect(err).To(MatchError(ContainSubstring("decode state")))
		})
	})

	Context("when runC stats fails", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				return errors.New("banana")
			})
		})

		It("returns an error", func() {
			Expect(err).To(MatchError(ContainSubstring("runC stats: banana")))
		})
	})

	Context("when runC state fails", func() {
		BeforeEach(func() {
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-stats",
			}, func(cmd *exec.Cmd) error {
				cmd.Stdout.Write([]byte(`{}`))
				return nil
			})
			commandRunner.WhenRunning(fake_command_runner.CommandSpec{
				Path: "funC-state",
			}, func(cmd *exec.Cmd) error {
				return errors.New("banana")
			})
		})

		It("returns an error", func() {
			Expect(err).To(MatchError(ContainSubstring("runC state: banana")))
		})
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mungers

import (
	"fmt"
	"strings"

	"k8s.io/contrib/mungegithub/features"
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

const (
	clearAfterMergeName = "cherrypick-clear-after-merge"
)

// ClearPickAfterMerge will remove the the cherrypick-candidate label from
// any PR that does not have a 'release' milestone set.
type ClearPickAfterMerge struct {
	features *features.Features
}

func init() {
	RegisterMungerOrDie(&ClearPickAfterMerge{})
}

// Name is the name usable in --pr-mungers
func (c *ClearPickAfterMerge) Name() string { return clearAfterMergeName }

// RequiredFeatures is a slice of 'features' that must be provided
func (c *ClearPickAfterMerge) RequiredFeatures() []string { return []string{features.RepoFeatureName} }

// Initialize will initialize the munger
func (c *ClearPickAfterMerge) Initialize(config *github.Config, features *features.Features) error {
	c.features = features
	return nil
}

// EachLoop is called at the start of every munge loop
func (c *ClearPickAfterMerge) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (c *ClearPickAfterMerge) AddFlags(cmd *cobra.Command, config *github.Config) {}

func handleFound(obj *github.MungeObject, branch string) error {
	msg := fmt.Sprintf("Commit found in the %q branch appears to be this PR. Removing the %q label. If this is an error find help to get your PR picked.", branch, cpCandidateLabel)
	obj.WriteComment(msg)
	obj.RemoveLabel(cpCandidateLabel)
	return nil
}

// foundLog will return if the given `logString` exists on the branch in question.
// it will also return the actual logs for further processing
func (c *ClearPickAfterMerge) foundLog(branch string, logString string) (bool, string) {
	args := []string{"merge-base", "--fork-point", "origin/master", "origin/" + branch}
	out, err := c.features.Repos.GitCommand(args)
	base := string(out)
	if err != nil {
		glog.Errorf("Unable to find the fork point for branch %s. %s:%v", branch, base, err)
		return false, ""
	}
	base = strings.TrimSuffix(base, "\n")

	// if release-1.2 branched from master at abcdef123 this should result in:
	// abcdef123..origin/release-1.2
	logRefs := fmt.Sprintf("%s..origin/%s", base, branch)

	args = []string{"log", "--pretty=tformat:%H%n%s%n%b", "--grep", logString, logRefs}
	out, err = c.features.Repos.GitCommand(args)
	logs := string(out)
	if err != nil {
		glog.Errorf("Error grepping logs out=%q: %v", logs, err)
		return false, ""
	}
	glog.V(10).Infof("args:%v", args)

	glog.V(10).Infof("Searching for %q in %q", logString, logs)
	if !strings.Contains(logs, logString) {
		return false, ""
	}
	return true, logs
}

// Can we find a commit in the changelog that looks like it was done using git cherry-pick -m1 -x ?
func (c *ClearPickAfterMerge) foundByPickDashX(obj *github.MungeObject, branch string) bool {
	sha := obj.MergeCommit()
	if sha == nil {
		glog.Errorf("Unable to get SHA of merged PR %d", *obj.Issue.Number)
		return false
	}

	cherrypickMsg := fmt.Sprintf("(cherry picked from commit %s)", *sha)
	found, logs := c.foundLog(branch, cherrypickMsg)
	if !found {
		return false
	}

	// double check for the 'non -x' message
	logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number)
	if !strings.Contains(logs, logMsg) {
		return false
	}
	glog.Infof("Found cherry-pick for %d using -x information in branch %q", *obj.Issue.Number, branch)
	return true
}

// Can we find a commit in the changelog that looks like it was done using git cherry-pick -m1 ?
func (c *ClearPickAfterMerge) foundByPickWithoutDashX(obj *github.MungeObject, branch string) bool {
	logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number)

	found, _ := c.foundLog(branch, logMsg)
	if found {
		glog.Infof("Found cherry-pick for %d using log matching for `git cherry-pick` in branch %q", *obj.Issue.Number, branch)
	}
	return found
}

// Check that the commit messages for all commits in the PR are on the branch
func (c *ClearPickAfterMerge) foundByAllCommits(obj *github.MungeObject, branch string) bool {
	commits, err := obj.GetCommits()
	if err != nil {
		glog.Infof("unable to get commits")
		return false
	}
	for _, commit := range commits {
		if commit.Commit == nil {
			return false
		}
		if commit.Commit.Message == nil {
			return false
		}
		found, _ := c.foundLog(branch, *commit.Commit.Message)
		if !found {
			glog.Infof("found == false")
			return false
		}
	}
	return true
}

// Can we find a commit in the changelog that looks like it was done using the hack/cherry_pick_pull.sh script ?
func (c *ClearPickAfterMerge) foundByScript(obj *github.MungeObject, branch string) bool {
	logMsg := fmt.Sprintf("Cherry pick of #%d on %s.", *obj.Issue.Number, branch)

	found, _ := c.foundLog(branch, logMsg)
	if found {
		glog.Infof("Found cherry-pick for %d using log matching for `hack/cherry_pick_pull.sh` in branch %q", *obj.Issue.Number, branch)
	}
	return found
}

// Munge is the workhorse the will actually make updates to the PR
func (c *ClearPickAfterMerge) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}
	if !obj.HasLabel(cpCandidateLabel) {
		return
	}

	if merged, err := obj.IsMerged(); !merged || err != nil {
		return
	}

	releaseMilestone := obj.ReleaseMilestone()
	if releaseMilestone == "" || len(releaseMilestone) != 4 {
		glog.Errorf("Found invalid milestone: %q", releaseMilestone)
		return
	}
	rel := releaseMilestone[1:]
	branch := "release-" + rel

	if c.foundByPickDashX(obj, branch) {
		handleFound(obj, branch)
		return
	}

	if c.foundByAllCommits(obj, branch) {
		handleFound(obj, branch)
		return
	}

	if c.foundByPickWithoutDashX(obj, branch) {
		handleFound(obj, branch)
		return
	}

	if c.foundByScript(obj, branch) {
		handleFound(obj, branch)
		return
	}

	return
}
<commit_msg>cherrypick: Do not log every time a PR doesn't mater<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mungers

import (
	"fmt"
	"strings"

	"k8s.io/contrib/mungegithub/features"
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

const (
	clearAfterMergeName = "cherrypick-clear-after-merge"
)

// ClearPickAfterMerge will remove the the cherrypick-candidate label from
// any PR that does not have a 'release' milestone set.
type ClearPickAfterMerge struct {
	features *features.Features
}

func init() {
	RegisterMungerOrDie(&ClearPickAfterMerge{})
}

// Name is the name usable in --pr-mungers
func (c *ClearPickAfterMerge) Name() string { return clearAfterMergeName }

// RequiredFeatures is a slice of 'features' that must be provided
func (c *ClearPickAfterMerge) RequiredFeatures() []string { return []string{features.RepoFeatureName} }

// Initialize will initialize the munger
func (c *ClearPickAfterMerge) Initialize(config *github.Config, features *features.Features) error {
	c.features = features
	return nil
}

// EachLoop is called at the start of every munge loop
func (c *ClearPickAfterMerge) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (c *ClearPickAfterMerge) AddFlags(cmd *cobra.Command, config *github.Config) {}

func handleFound(obj *github.MungeObject, branch string) error {
	msg := fmt.Sprintf("Commit found in the %q branch appears to be this PR. Removing the %q label. If this is an error find help to get your PR picked.", branch, cpCandidateLabel)
	obj.WriteComment(msg)
	obj.RemoveLabel(cpCandidateLabel)
	return nil
}

// foundLog will return if the given `logString` exists on the branch in question.
// it will also return the actual logs for further processing
func (c *ClearPickAfterMerge) foundLog(branch string, logString string) (bool, string) {
	args := []string{"merge-base", "--fork-point", "origin/master", "origin/" + branch}
	out, err := c.features.Repos.GitCommand(args)
	base := string(out)
	if err != nil {
		glog.Errorf("Unable to find the fork point for branch %s. %s:%v", branch, base, err)
		return false, ""
	}
	base = strings.TrimSuffix(base, "\n")

	// if release-1.2 branched from master at abcdef123 this should result in:
	// abcdef123..origin/release-1.2
	logRefs := fmt.Sprintf("%s..origin/%s", base, branch)

	args = []string{"log", "--pretty=tformat:%H%n%s%n%b", "--grep", logString, logRefs}
	out, err = c.features.Repos.GitCommand(args)
	logs := string(out)
	if err != nil {
		glog.Errorf("Error grepping logs out=%q: %v", logs, err)
		return false, ""
	}
	glog.V(10).Infof("args:%v", args)

	glog.V(10).Infof("Searching for %q in %q", logString, logs)
	if !strings.Contains(logs, logString) {
		return false, ""
	}
	return true, logs
}

// Can we find a commit in the changelog that looks like it was done using git cherry-pick -m1 -x ?
func (c *ClearPickAfterMerge) foundByPickDashX(obj *github.MungeObject, branch string) bool {
	sha := obj.MergeCommit()
	if sha == nil {
		glog.Errorf("Unable to get SHA of merged PR %d", *obj.Issue.Number)
		return false
	}

	cherrypickMsg := fmt.Sprintf("(cherry picked from commit %s)", *sha)
	found, logs := c.foundLog(branch, cherrypickMsg)
	if !found {
		return false
	}

	// double check for the 'non -x' message
	logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number)
	if !strings.Contains(logs, logMsg) {
		return false
	}
	glog.Infof("Found cherry-pick for %d using -x information in branch %q", *obj.Issue.Number, branch)
	return true
}

// Can we find a commit in the changelog that looks like it was done using git cherry-pick -m1 ?
func (c *ClearPickAfterMerge) foundByPickWithoutDashX(obj *github.MungeObject, branch string) bool {
	logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number)

	found, _ := c.foundLog(branch, logMsg)
	if found {
		glog.Infof("Found cherry-pick for %d using log matching for `git cherry-pick` in branch %q", *obj.Issue.Number, branch)
	}
	return found
}

// Check that the commit messages for all commits in the PR are on the branch
func (c *ClearPickAfterMerge) foundByAllCommits(obj *github.MungeObject, branch string) bool {
	commits, err := obj.GetCommits()
	if err != nil {
		glog.Infof("unable to get commits")
		return false
	}
	for _, commit := range commits {
		if commit.Commit == nil {
			return false
		}
		if commit.Commit.Message == nil {
			return false
		}
		found, _ := c.foundLog(branch, *commit.Commit.Message)
		if !found {
			return false
		}
	}
	return true
}

// Can we find a commit in the changelog that looks like it was done using the hack/cherry_pick_pull.sh script ?
func (c *ClearPickAfterMerge) foundByScript(obj *github.MungeObject, branch string) bool {
	logMsg := fmt.Sprintf("Cherry pick of #%d on %s.", *obj.Issue.Number, branch)

	found, _ := c.foundLog(branch, logMsg)
	if found {
		glog.Infof("Found cherry-pick for %d using log matching for `hack/cherry_pick_pull.sh` in branch %q", *obj.Issue.Number, branch)
	}
	return found
}

// Munge is the workhorse the will actually make updates to the PR
func (c *ClearPickAfterMerge) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}
	if !obj.HasLabel(cpCandidateLabel) {
		return
	}

	if merged, err := obj.IsMerged(); !merged || err != nil {
		return
	}

	releaseMilestone := obj.ReleaseMilestone()
	if releaseMilestone == "" || len(releaseMilestone) != 4 {
		glog.Errorf("Found invalid milestone: %q", releaseMilestone)
		return
	}
	rel := releaseMilestone[1:]
	branch := "release-" + rel

	if c.foundByPickDashX(obj, branch) {
		handleFound(obj, branch)
		return
	}

	if c.foundByAllCommits(obj, branch) {
		handleFound(obj, branch)
		return
	}

	if c.foundByPickWithoutDashX(obj, branch) {
		handleFound(obj, branch)
		return
	}

	if c.foundByScript(obj, branch) {
		handleFound(obj, branch)
		return
	}

	return
}
<|endoftext|>


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

import (
	"flag"
	"image"
	"image/draw"
	_ "image/jpeg"
	_ "image/png"
	"os"

	"github.com/google/gxui"
	"github.com/google/gxui/drivers/gl"
	"github.com/google/gxui/themes/dark"
)

var data = flag.String("data", "", "path to data")
var file = flag.String("file", "", "path to file")

func appMain(driver gxui.Driver) {
	f, err := os.Open(*file)
	if err != nil {
		panic(err)
	}
	source, _, err := image.Decode(f)
	if err != nil {
		panic(err)
	}

	theme := dark.CreateTheme(driver)
	img := theme.CreateImage()

	mx := source.Bounds().Max
	window := theme.CreateWindow(mx.X, mx.Y, "Image viewer")
	window.AddChild(img)

	// Copy the image to a RGBA format before handing to a gxui.Texture
	rgba := image.NewRGBA(source.Bounds())
	draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src)
	texture := driver.CreateTexture(rgba, 1)
	texture.SetFlipY(true)
	img.SetTexture(texture)

	window.OnClose(driver.Terminate)
	gxui.EventLoop(driver)
}

func main() {
	flag.Parse()
	gl.StartDriver(appMain)
}
<commit_msg>Show usage if there's no command line arguments.<commit_after>// Copyright 2015 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 main

import (
	"flag"
	"fmt"
	"image"
	"image/draw"
	_ "image/jpeg"
	_ "image/png"
	"os"

	"github.com/google/gxui"
	"github.com/google/gxui/drivers/gl"
	"github.com/google/gxui/themes/dark"
)

func appMain(driver gxui.Driver) {
	args := flag.Args()
	if len(args) != 1 {
		fmt.Print("usage: image_viewer image-path\n")
		os.Exit(1)
	}

	file := args[0]
	f, err := os.Open(file)
	if err != nil {
		fmt.Printf("Failed to open image '%s': %v\n", file, err)
		os.Exit(1)
	}

	source, _, err := image.Decode(f)
	if err != nil {
		fmt.Printf("Failed to read image '%s': %v\n", file, err)
		os.Exit(1)
	}

	theme := dark.CreateTheme(driver)
	img := theme.CreateImage()

	mx := source.Bounds().Max
	window := theme.CreateWindow(mx.X, mx.Y, "Image viewer")
	window.AddChild(img)

	// Copy the image to a RGBA format before handing to a gxui.Texture
	rgba := image.NewRGBA(source.Bounds())
	draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src)
	texture := driver.CreateTexture(rgba, 1)
	texture.SetFlipY(true)
	img.SetTexture(texture)

	window.OnClose(driver.Terminate)
	gxui.EventLoop(driver)
}

func main() {
	flag.Parse()
	gl.StartDriver(appMain)
}
<|endoftext|>