<commit_before>package cf_helpers

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

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/craigfurman/herottp"
	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega/gexec"

	. "github.com/onsi/gomega"
)

type binding struct {
	Credentials interface{} `json:"credentials"`
	Name        string      `json:"name"`
}

var certIgnoringHTTPClient = herottp.New(herottp.Config{
	DisableTLSCertificateVerification: true,
	Timeout: ThirtySecondTimeout,
})

func findURL(cliOutput string) string {
	for _, line := range strings.Split(cliOutput, "\n") {
		if strings.HasPrefix(line, "urls:") {
			return strings.Fields(line)[1]
		}
	}
	return ""
}

func PushAndBindApp(appName, serviceName, testAppPath string) string {
	Eventually(cf.Cf("push", "-p", testAppPath, "-f", filepath.Join(testAppPath, "manifest.yml"), "--no-start", appName), FiveMinuteTimeout).Should(gexec.Exit(0))
	Eventually(cf.Cf("bind-service", appName, serviceName), FiveMinuteTimeout).Should(gexec.Exit(0))

	// The first time apps start, it is very slow as the buildpack downloads runtimes and caches them
	Eventually(cf.Cf("start", appName), FiveMinuteTimeout).Should(gexec.Exit(0))
	appDetails := cf.Cf("app", appName)
	Eventually(appDetails, FiveSecondTimeout).Should(gexec.Exit(0))
	appDetailsOutput := string(appDetails.Buffer().Contents())
	testAppURL := findURL(appDetailsOutput)
	Expect(testAppURL).NotTo(BeEmpty())
	return testAppURL
}

func PutToTestApp(testAppURL, key, value string) {
	putReq, err := http.NewRequest(
		"PUT",
		fmt.Sprintf("https://%s/%s", testAppURL, key),
		strings.NewReader(fmt.Sprintf("data=%s", value)),
	)
	Expect(err).ToNot(HaveOccurred())
	putReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	makeAndCheckHttpRequest(putReq)
}

func GetFromTestApp(testAppURL, key string) string {
	getReq, err := http.NewRequest(
		"GET",
		fmt.Sprintf("https://%s/%s", testAppURL, key),
		nil,
	)
	Expect(err).ToNot(HaveOccurred())
	return makeAndCheckHttpRequest(getReq)
}

func PushToTestAppQueue(testAppURL, queueName, message string) {
	postReq, err := http.NewRequest(
		"POST",
		fmt.Sprintf("https://%s/queues/%s", testAppURL, queueName),
		strings.NewReader(message),
	)
	Expect(err).ToNot(HaveOccurred())
	makeAndCheckHttpRequest(postReq)
}

func PopFromTestAppQueue(testAppURL, queueName string) string {
	getReq, err := http.NewRequest(
		"GET",
		fmt.Sprintf("https://%s/queues/%s", testAppURL, queueName),
		nil,
	)
	Expect(err).ToNot(HaveOccurred())
	return makeAndCheckHttpRequest(getReq)
}

func appEnv(appName string) (io.Reader, error) {
	session := cf.Cf(
		"curl",
		fmt.Sprintf("/v2/apps/%s/env", appGUID(appName)),
	)

	Eventually(session).Should(gexec.Exit(0))
	return bytes.NewReader(session.Out.Contents()), nil
}

func appBinding(appName, serviceName string) (*binding, error) {
	env, err := appEnv(appName)
	if err != nil {
		return nil, err
	}

	var envBindings struct {
		Env struct {
			Services map[string][]binding `json:"VCAP_SERVICES"`
		} `json:"system_env_json"`
	}

	if err = json.NewDecoder(env).Decode(&envBindings); err != nil {
		return nil, err
	}

	if bindings, found := envBindings.Env.Services[serviceName]; found {
		return &bindings[0], nil
	}

	return nil, fmt.Errorf("app not bound to service %q", serviceName)
}

type credentials struct {
	Password string `json: "password"`
}

func AppBindingCreds(appName, serviceName string) (interface{}, error) {
	b, err := appBinding(appName, serviceName)
	if err != nil {
		return "", err
	}

	return b.Credentials, nil
}

func guid(name, typ string) string {
	session := cf.Cf(typ, name, "--guid")
	Eventually(session).Should(gexec.Exit(0))
	return strings.Trim(string(session.Out.Contents()), " \n")
}

func appGUID(appName string) string {
	return guid(appName, "app")
}

func ServiceInstanceGUID(serviceName string) string {
	return guid(serviceName, "service")
}

func makeAndCheckHttpRequest(req *http.Request) string {
	resp, err := certIgnoringHTTPClient.Do(req)
	Expect(err).ToNot(HaveOccurred())
	defer resp.Body.Close()
	bodyContent, err := ioutil.ReadAll(resp.Body)
	Expect(err).ToNot(HaveOccurred())
	ginkgo.GinkgoWriter.Write([]byte(fmt.Sprintf(
		"response from %s %s: %d\n----------------------------------------\n%s\n----------------------------------------\n",
		req.Method,
		req.URL.String(),
		resp.StatusCode,
		bodyContent,
	)))
	Expect(resp.StatusCode).To(BeNumerically("<", 300))
	return string(bodyContent)
}
<commit_msg>Update to use new cf-cli 6.26<commit_after>package cf_helpers

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

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/craigfurman/herottp"
	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega/gexec"

	. "github.com/onsi/gomega"
)

type binding struct {
	Credentials interface{} `json:"credentials"`
	Name        string      `json:"name"`
}

var certIgnoringHTTPClient = herottp.New(herottp.Config{
	DisableTLSCertificateVerification: true,
	Timeout: ThirtySecondTimeout,
})

func findURL(cliOutput string) string {
	for _, line := range strings.Split(cliOutput, "\n") {
		if strings.HasPrefix(line, "routes:") {
			return strings.Fields(line)[1]
		}
	}
	return ""
}

func PushAndBindApp(appName, serviceName, testAppPath string) string {
	Eventually(cf.Cf("push", "-p", testAppPath, "-f", filepath.Join(testAppPath, "manifest.yml"), "--no-start", appName), FiveMinuteTimeout).Should(gexec.Exit(0))
	Eventually(cf.Cf("bind-service", appName, serviceName), FiveMinuteTimeout).Should(gexec.Exit(0))

	// The first time apps start, it is very slow as the buildpack downloads runtimes and caches them
	Eventually(cf.Cf("start", appName), FiveMinuteTimeout).Should(gexec.Exit(0))
	appDetails := cf.Cf("app", appName)
	Eventually(appDetails, FiveSecondTimeout).Should(gexec.Exit(0))
	appDetailsOutput := string(appDetails.Buffer().Contents())
	testAppURL := findURL(appDetailsOutput)
	Expect(testAppURL).NotTo(BeEmpty())
	return testAppURL
}

func PutToTestApp(testAppURL, key, value string) {
	putReq, err := http.NewRequest(
		"PUT",
		fmt.Sprintf("https://%s/%s", testAppURL, key),
		strings.NewReader(fmt.Sprintf("data=%s", value)),
	)
	Expect(err).ToNot(HaveOccurred())
	putReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	makeAndCheckHttpRequest(putReq)
}

func GetFromTestApp(testAppURL, key string) string {
	getReq, err := http.NewRequest(
		"GET",
		fmt.Sprintf("https://%s/%s", testAppURL, key),
		nil,
	)
	Expect(err).ToNot(HaveOccurred())
	return makeAndCheckHttpRequest(getReq)
}

func PushToTestAppQueue(testAppURL, queueName, message string) {
	postReq, err := http.NewRequest(
		"POST",
		fmt.Sprintf("https://%s/queues/%s", testAppURL, queueName),
		strings.NewReader(message),
	)
	Expect(err).ToNot(HaveOccurred())
	makeAndCheckHttpRequest(postReq)
}

func PopFromTestAppQueue(testAppURL, queueName string) string {
	getReq, err := http.NewRequest(
		"GET",
		fmt.Sprintf("https://%s/queues/%s", testAppURL, queueName),
		nil,
	)
	Expect(err).ToNot(HaveOccurred())
	return makeAndCheckHttpRequest(getReq)
}

func appEnv(appName string) (io.Reader, error) {
	session := cf.Cf(
		"curl",
		fmt.Sprintf("/v2/apps/%s/env", appGUID(appName)),
	)

	Eventually(session).Should(gexec.Exit(0))
	return bytes.NewReader(session.Out.Contents()), nil
}

func appBinding(appName, serviceName string) (*binding, error) {
	env, err := appEnv(appName)
	if err != nil {
		return nil, err
	}

	var envBindings struct {
		Env struct {
			Services map[string][]binding `json:"VCAP_SERVICES"`
		} `json:"system_env_json"`
	}

	if err = json.NewDecoder(env).Decode(&envBindings); err != nil {
		return nil, err
	}

	if bindings, found := envBindings.Env.Services[serviceName]; found {
		return &bindings[0], nil
	}

	return nil, fmt.Errorf("app not bound to service %q", serviceName)
}

type credentials struct {
	Password string `json: "password"`
}

func AppBindingCreds(appName, serviceName string) (interface{}, error) {
	b, err := appBinding(appName, serviceName)
	if err != nil {
		return "", err
	}

	return b.Credentials, nil
}

func guid(name, typ string) string {
	session := cf.Cf(typ, name, "--guid")
	Eventually(session).Should(gexec.Exit(0))
	return strings.Trim(string(session.Out.Contents()), " \n")
}

func appGUID(appName string) string {
	return guid(appName, "app")
}

func ServiceInstanceGUID(serviceName string) string {
	return guid(serviceName, "service")
}

func makeAndCheckHttpRequest(req *http.Request) string {
	resp, err := certIgnoringHTTPClient.Do(req)
	Expect(err).ToNot(HaveOccurred())
	defer resp.Body.Close()
	bodyContent, err := ioutil.ReadAll(resp.Body)
	Expect(err).ToNot(HaveOccurred())
	ginkgo.GinkgoWriter.Write([]byte(fmt.Sprintf(
		"response from %s %s: %d\n----------------------------------------\n%s\n----------------------------------------\n",
		req.Method,
		req.URL.String(),
		resp.StatusCode,
		bodyContent,
	)))
	Expect(resp.StatusCode).To(BeNumerically("<", 300))
	return string(bodyContent)
}
<|endoftext|>


<commit_before>package types

import (
	"os"
	"time"

	"github.com/docker/engine-api/types/container"
	"github.com/docker/engine-api/types/network"
	"github.com/docker/engine-api/types/registry"
	"github.com/docker/go-connections/nat"
)

// ContainerCreateResponse contains the information returned to a client on the
// creation of a new container.
type ContainerCreateResponse struct {
	// ID is the ID of the created container.
	ID string `json:"Id"`

	// Warnings are any warnings encountered during the creation of the container.
	Warnings []string `json:"Warnings"`
}

// ContainerExecCreateResponse contains response of Remote API:
// POST "/containers/{name:.*}/exec"
type ContainerExecCreateResponse struct {
	// ID is the exec ID.
	ID string `json:"Id"`
}

// ContainerUpdateResponse contains response of Remote API:
// POST /containers/{name:.*}/update
type ContainerUpdateResponse struct {
	// Warnings are any warnings encountered during the updating of the container.
	Warnings []string `json:"Warnings"`
}

// AuthResponse contains response of Remote API:
// POST "/auth"
type AuthResponse struct {
	// Status is the authentication status
	Status string `json:"Status"`
}

// ContainerWaitResponse contains response of Remote API:
// POST "/containers/"+containerID+"/wait"
type ContainerWaitResponse struct {
	// StatusCode is the status code of the wait job
	StatusCode int `json:"StatusCode"`
}

// ContainerCommitResponse contains response of Remote API:
// POST "/commit?container="+containerID
type ContainerCommitResponse struct {
	ID string `json:"Id"`
}

// ContainerChange contains response of Remote API:
// GET "/containers/{name:.*}/changes"
type ContainerChange struct {
	Kind int
	Path string
}

// ImageHistory contains response of Remote API:
// GET "/images/{name:.*}/history"
type ImageHistory struct {
	ID        string `json:"Id"`
	Created   int64
	CreatedBy string
	Tags      []string
	Size      int64
	Comment   string
}

// ImageDelete contains response of Remote API:
// DELETE "/images/{name:.*}"
type ImageDelete struct {
	Untagged string `json:",omitempty"`
	Deleted  string `json:",omitempty"`
}

// Image contains response of Remote API:
// GET "/images/json"
type Image struct {
	ID          string `json:"Id"`
	ParentID    string `json:"ParentId"`
	RepoTags    []string
	RepoDigests []string
	Created     int64
	Size        int64
	VirtualSize int64
	Labels      map[string]string
}

// GraphDriverData returns Image's graph driver config info
// when calling inspect command
type GraphDriverData struct {
	Name string
	Data map[string]string
}

// ImageInspect contains response of Remote API:
// GET "/images/{name:.*}/json"
type ImageInspect struct {
	ID              string `json:"Id"`
	RepoTags        []string
	RepoDigests     []string
	Parent          string
	Comment         string
	Created         string
	Container       string
	ContainerConfig *container.Config
	DockerVersion   string
	Author          string
	Config          *container.Config
	Architecture    string
	Os              string
	Size            int64
	VirtualSize     int64
	GraphDriver     GraphDriverData
}

// Port stores open ports info of container
// e.g. {"PrivatePort": 8080, "PublicPort": 80, "Type": "tcp"}
type Port struct {
	IP          string `json:",omitempty"`
	PrivatePort int
	PublicPort  int `json:",omitempty"`
	Type        string
}

// Container contains response of Remote API:
// GET  "/containers/json"
type Container struct {
	ID         string `json:"Id"`
	Names      []string
	Image      string
	ImageID    string
	Command    string
	Created    int64
	Ports      []Port
	SizeRw     int64 `json:",omitempty"`
	SizeRootFs int64 `json:",omitempty"`
	Labels     map[string]string
	Status     string
	HostConfig struct {
		NetworkMode string `json:",omitempty"`
	}
	NetworkSettings *SummaryNetworkSettings
}

// CopyConfig contains request body of Remote API:
// POST "/containers/"+containerID+"/copy"
type CopyConfig struct {
	Resource string
}

// ContainerPathStat is used to encode the header from
// GET "/containers/{name:.*}/archive"
// "Name" is the file or directory name.
type ContainerPathStat struct {
	Name       string      `json:"name"`
	Size       int64       `json:"size"`
	Mode       os.FileMode `json:"mode"`
	Mtime      time.Time   `json:"mtime"`
	LinkTarget string      `json:"linkTarget"`
}

// ContainerProcessList contains response of Remote API:
// GET "/containers/{name:.*}/top"
type ContainerProcessList struct {
	Processes [][]string
	Titles    []string
}

// Version contains response of Remote API:
// GET "/version"
type Version struct {
	Version       string
	APIVersion    string `json:"ApiVersion"`
	GitCommit     string
	GoVersion     string
	Os            string
	Arch          string
	KernelVersion string `json:",omitempty"`
	Experimental  bool   `json:",omitempty"`
	BuildTime     string `json:",omitempty"`
}

// Info contains response of Remote API:
// GET "/info"
type Info struct {
	ID                 string
	Containers         int
	ContainersRunning  int
	ContainersPaused   int
	ContainersStopped  int
	Images             int
	Driver             string
	DriverStatus       [][2]string
	Plugins            PluginsInfo
	MemoryLimit        bool
	SwapLimit          bool
	CPUCfsPeriod       bool `json:"CpuCfsPeriod"`
	CPUCfsQuota        bool `json:"CpuCfsQuota"`
	CPUShares          bool
	CPUSet             bool
	IPv4Forwarding     bool
	BridgeNfIptables   bool
	BridgeNfIP6tables  bool `json:"BridgeNfIp6tables"`
	Debug              bool
	NFd                int
	OomKillDisable     bool
	NGoroutines        int
	SystemTime         string
	ExecutionDriver    string
	LoggingDriver      string
	NEventsListener    int
	KernelVersion      string
	OperatingSystem    string
	OSType             string
	Architecture       string
	IndexServerAddress string
	RegistryConfig     *registry.ServiceConfig
	InitSha1           string
	InitPath           string
	NCPU               int
	MemTotal           int64
	DockerRootDir      string
	HTTPProxy          string `json:"HttpProxy"`
	HTTPSProxy         string `json:"HttpsProxy"`
	NoProxy            string
	Name               string
	Labels             []string
	ExperimentalBuild  bool
	ServerVersion      string
	ClusterStore       string
	ClusterAdvertise   string
}

// PluginsInfo is temp struct holds Plugins name
// registered with docker daemon. It used by Info struct
type PluginsInfo struct {
	// List of Volume plugins registered
	Volume []string
	// List of Network plugins registered
	Network []string
	// List of Authorization plugins registered
	Authorization []string
}

// ExecStartCheck is a temp struct used by execStart
// Config fields is part of ExecConfig in runconfig package
type ExecStartCheck struct {
	// ExecStart will first check if it's detached
	Detach bool
	// Check if there's a tty
	Tty bool
}

// ContainerState stores container's running state
// it's part of ContainerJSONBase and will return by "inspect" command
type ContainerState struct {
	Status     string
	Running    bool
	Paused     bool
	Restarting bool
	OOMKilled  bool
	Dead       bool
	Pid        int
	ExitCode   int
	Error      string
	StartedAt  string
	FinishedAt string
}

// ContainerJSONBase contains response of Remote API:
// GET "/containers/{name:.*}/json"
type ContainerJSONBase struct {
	ID              string `json:"Id"`
	Created         string
	Path            string
	Args            []string
	State           *ContainerState
	Image           string
	ResolvConfPath  string
	HostnamePath    string
	HostsPath       string
	LogPath         string
	Name            string
	RestartCount    int
	Driver          string
	MountLabel      string
	ProcessLabel    string
	AppArmorProfile string
	ExecIDs         []string
	HostConfig      *container.HostConfig
	GraphDriver     GraphDriverData
	SizeRw          *int64 `json:",omitempty"`
	SizeRootFs      *int64 `json:",omitempty"`
}

// ContainerJSON is newly used struct along with MountPoint
type ContainerJSON struct {
	*ContainerJSONBase
	Mounts          []MountPoint
	Config          *container.Config
	NetworkSettings *NetworkSettings
}

// NetworkSettings exposes the network settings in the api
type NetworkSettings struct {
	NetworkSettingsBase
	DefaultNetworkSettings
	Networks map[string]*network.EndpointSettings
}

// SummaryNetworkSettings provides a summary of container's networks
// in /containers/json
type SummaryNetworkSettings struct {
	Networks map[string]*network.EndpointSettings
}

// NetworkSettingsBase holds basic information about networks
type NetworkSettingsBase struct {
	Bridge                 string
	SandboxID              string
	HairpinMode            bool
	LinkLocalIPv6Address   string
	LinkLocalIPv6PrefixLen int
	Ports                  nat.PortMap
	SandboxKey             string
	SecondaryIPAddresses   []network.Address
	SecondaryIPv6Addresses []network.Address
}

// DefaultNetworkSettings holds network information
// during the 2 release deprecation period.
// It will be removed in Docker 1.11.
type DefaultNetworkSettings struct {
	EndpointID          string
	Gateway             string
	GlobalIPv6Address   string
	GlobalIPv6PrefixLen int
	IPAddress           string
	IPPrefixLen         int
	IPv6Gateway         string
	MacAddress          string
}

// MountPoint represents a mount point configuration inside the container.
type MountPoint struct {
	Name        string `json:",omitempty"`
	Source      string
	Destination string
	Driver      string `json:",omitempty"`
	Mode        string
	RW          bool
	Propagation string
}

// Volume represents the configuration of a volume for the remote API
type Volume struct {
	Name       string // Name is the name of the volume
	Driver     string // Driver is the Driver name used to create the volume
	Mountpoint string // Mountpoint is the location on disk of the volume
}

// VolumesListResponse contains the response for the remote API:
// GET "/volumes"
type VolumesListResponse struct {
	Volumes  []*Volume // Volumes is the list of volumes being returned
	Warnings []string  // Warnings is a list of warnings that occurred when getting the list from the volume drivers
}

// VolumeCreateRequest contains the response for the remote API:
// POST "/volumes/create"
type VolumeCreateRequest struct {
	Name       string            // Name is the requested name of the volume
	Driver     string            // Driver is the name of the driver that should be used to create the volume
	DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
}

// NetworkResource is the body of the "get network" http response message
type NetworkResource struct {
	Name       string
	ID         string `json:"Id"`
	Scope      string
	Driver     string
	IPAM       network.IPAM
	Containers map[string]EndpointResource
	Options    map[string]string
}

// EndpointResource contains network resources allocated and used for a container in a network
type EndpointResource struct {
	Name        string
	EndpointID  string
	MacAddress  string
	IPv4Address string
	IPv6Address string
}

// NetworkCreate is the expected body of the "create network" http request message
type NetworkCreate struct {
	Name           string
	CheckDuplicate bool
	Driver         string
	IPAM           network.IPAM
	Internal       bool
	Options        map[string]string
}

// NetworkCreateResponse is the response message sent by the server for network create call
type NetworkCreateResponse struct {
	ID      string `json:"Id"`
	Warning string
}

// NetworkConnect represents the data to be used to connect a container to the network
type NetworkConnect struct {
	Container      string
	EndpointConfig *network.EndpointSettings `json:",omitempty"`
}

// NetworkDisconnect represents the data to be used to disconnect a container from the network
type NetworkDisconnect struct {
	Container string
	Force     bool
}
<commit_msg>types: remove Init{Sha1,Path}<commit_after>package types

import (
	"os"
	"time"

	"github.com/docker/engine-api/types/container"
	"github.com/docker/engine-api/types/network"
	"github.com/docker/engine-api/types/registry"
	"github.com/docker/go-connections/nat"
)

// ContainerCreateResponse contains the information returned to a client on the
// creation of a new container.
type ContainerCreateResponse struct {
	// ID is the ID of the created container.
	ID string `json:"Id"`

	// Warnings are any warnings encountered during the creation of the container.
	Warnings []string `json:"Warnings"`
}

// ContainerExecCreateResponse contains response of Remote API:
// POST "/containers/{name:.*}/exec"
type ContainerExecCreateResponse struct {
	// ID is the exec ID.
	ID string `json:"Id"`
}

// ContainerUpdateResponse contains response of Remote API:
// POST /containers/{name:.*}/update
type ContainerUpdateResponse struct {
	// Warnings are any warnings encountered during the updating of the container.
	Warnings []string `json:"Warnings"`
}

// AuthResponse contains response of Remote API:
// POST "/auth"
type AuthResponse struct {
	// Status is the authentication status
	Status string `json:"Status"`
}

// ContainerWaitResponse contains response of Remote API:
// POST "/containers/"+containerID+"/wait"
type ContainerWaitResponse struct {
	// StatusCode is the status code of the wait job
	StatusCode int `json:"StatusCode"`
}

// ContainerCommitResponse contains response of Remote API:
// POST "/commit?container="+containerID
type ContainerCommitResponse struct {
	ID string `json:"Id"`
}

// ContainerChange contains response of Remote API:
// GET "/containers/{name:.*}/changes"
type ContainerChange struct {
	Kind int
	Path string
}

// ImageHistory contains response of Remote API:
// GET "/images/{name:.*}/history"
type ImageHistory struct {
	ID        string `json:"Id"`
	Created   int64
	CreatedBy string
	Tags      []string
	Size      int64
	Comment   string
}

// ImageDelete contains response of Remote API:
// DELETE "/images/{name:.*}"
type ImageDelete struct {
	Untagged string `json:",omitempty"`
	Deleted  string `json:",omitempty"`
}

// Image contains response of Remote API:
// GET "/images/json"
type Image struct {
	ID          string `json:"Id"`
	ParentID    string `json:"ParentId"`
	RepoTags    []string
	RepoDigests []string
	Created     int64
	Size        int64
	VirtualSize int64
	Labels      map[string]string
}

// GraphDriverData returns Image's graph driver config info
// when calling inspect command
type GraphDriverData struct {
	Name string
	Data map[string]string
}

// ImageInspect contains response of Remote API:
// GET "/images/{name:.*}/json"
type ImageInspect struct {
	ID              string `json:"Id"`
	RepoTags        []string
	RepoDigests     []string
	Parent          string
	Comment         string
	Created         string
	Container       string
	ContainerConfig *container.Config
	DockerVersion   string
	Author          string
	Config          *container.Config
	Architecture    string
	Os              string
	Size            int64
	VirtualSize     int64
	GraphDriver     GraphDriverData
}

// Port stores open ports info of container
// e.g. {"PrivatePort": 8080, "PublicPort": 80, "Type": "tcp"}
type Port struct {
	IP          string `json:",omitempty"`
	PrivatePort int
	PublicPort  int `json:",omitempty"`
	Type        string
}

// Container contains response of Remote API:
// GET  "/containers/json"
type Container struct {
	ID         string `json:"Id"`
	Names      []string
	Image      string
	ImageID    string
	Command    string
	Created    int64
	Ports      []Port
	SizeRw     int64 `json:",omitempty"`
	SizeRootFs int64 `json:",omitempty"`
	Labels     map[string]string
	Status     string
	HostConfig struct {
		NetworkMode string `json:",omitempty"`
	}
	NetworkSettings *SummaryNetworkSettings
}

// CopyConfig contains request body of Remote API:
// POST "/containers/"+containerID+"/copy"
type CopyConfig struct {
	Resource string
}

// ContainerPathStat is used to encode the header from
// GET "/containers/{name:.*}/archive"
// "Name" is the file or directory name.
type ContainerPathStat struct {
	Name       string      `json:"name"`
	Size       int64       `json:"size"`
	Mode       os.FileMode `json:"mode"`
	Mtime      time.Time   `json:"mtime"`
	LinkTarget string      `json:"linkTarget"`
}

// ContainerProcessList contains response of Remote API:
// GET "/containers/{name:.*}/top"
type ContainerProcessList struct {
	Processes [][]string
	Titles    []string
}

// Version contains response of Remote API:
// GET "/version"
type Version struct {
	Version       string
	APIVersion    string `json:"ApiVersion"`
	GitCommit     string
	GoVersion     string
	Os            string
	Arch          string
	KernelVersion string `json:",omitempty"`
	Experimental  bool   `json:",omitempty"`
	BuildTime     string `json:",omitempty"`
}

// Info contains response of Remote API:
// GET "/info"
type Info struct {
	ID                 string
	Containers         int
	ContainersRunning  int
	ContainersPaused   int
	ContainersStopped  int
	Images             int
	Driver             string
	DriverStatus       [][2]string
	Plugins            PluginsInfo
	MemoryLimit        bool
	SwapLimit          bool
	CPUCfsPeriod       bool `json:"CpuCfsPeriod"`
	CPUCfsQuota        bool `json:"CpuCfsQuota"`
	CPUShares          bool
	CPUSet             bool
	IPv4Forwarding     bool
	BridgeNfIptables   bool
	BridgeNfIP6tables  bool `json:"BridgeNfIp6tables"`
	Debug              bool
	NFd                int
	OomKillDisable     bool
	NGoroutines        int
	SystemTime         string
	ExecutionDriver    string
	LoggingDriver      string
	NEventsListener    int
	KernelVersion      string
	OperatingSystem    string
	OSType             string
	Architecture       string
	IndexServerAddress string
	RegistryConfig     *registry.ServiceConfig
	NCPU               int
	MemTotal           int64
	DockerRootDir      string
	HTTPProxy          string `json:"HttpProxy"`
	HTTPSProxy         string `json:"HttpsProxy"`
	NoProxy            string
	Name               string
	Labels             []string
	ExperimentalBuild  bool
	ServerVersion      string
	ClusterStore       string
	ClusterAdvertise   string
}

// PluginsInfo is temp struct holds Plugins name
// registered with docker daemon. It used by Info struct
type PluginsInfo struct {
	// List of Volume plugins registered
	Volume []string
	// List of Network plugins registered
	Network []string
	// List of Authorization plugins registered
	Authorization []string
}

// ExecStartCheck is a temp struct used by execStart
// Config fields is part of ExecConfig in runconfig package
type ExecStartCheck struct {
	// ExecStart will first check if it's detached
	Detach bool
	// Check if there's a tty
	Tty bool
}

// ContainerState stores container's running state
// it's part of ContainerJSONBase and will return by "inspect" command
type ContainerState struct {
	Status     string
	Running    bool
	Paused     bool
	Restarting bool
	OOMKilled  bool
	Dead       bool
	Pid        int
	ExitCode   int
	Error      string
	StartedAt  string
	FinishedAt string
}

// ContainerJSONBase contains response of Remote API:
// GET "/containers/{name:.*}/json"
type ContainerJSONBase struct {
	ID              string `json:"Id"`
	Created         string
	Path            string
	Args            []string
	State           *ContainerState
	Image           string
	ResolvConfPath  string
	HostnamePath    string
	HostsPath       string
	LogPath         string
	Name            string
	RestartCount    int
	Driver          string
	MountLabel      string
	ProcessLabel    string
	AppArmorProfile string
	ExecIDs         []string
	HostConfig      *container.HostConfig
	GraphDriver     GraphDriverData
	SizeRw          *int64 `json:",omitempty"`
	SizeRootFs      *int64 `json:",omitempty"`
}

// ContainerJSON is newly used struct along with MountPoint
type ContainerJSON struct {
	*ContainerJSONBase
	Mounts          []MountPoint
	Config          *container.Config
	NetworkSettings *NetworkSettings
}

// NetworkSettings exposes the network settings in the api
type NetworkSettings struct {
	NetworkSettingsBase
	DefaultNetworkSettings
	Networks map[string]*network.EndpointSettings
}

// SummaryNetworkSettings provides a summary of container's networks
// in /containers/json
type SummaryNetworkSettings struct {
	Networks map[string]*network.EndpointSettings
}

// NetworkSettingsBase holds basic information about networks
type NetworkSettingsBase struct {
	Bridge                 string
	SandboxID              string
	HairpinMode            bool
	LinkLocalIPv6Address   string
	LinkLocalIPv6PrefixLen int
	Ports                  nat.PortMap
	SandboxKey             string
	SecondaryIPAddresses   []network.Address
	SecondaryIPv6Addresses []network.Address
}

// DefaultNetworkSettings holds network information
// during the 2 release deprecation period.
// It will be removed in Docker 1.11.
type DefaultNetworkSettings struct {
	EndpointID          string
	Gateway             string
	GlobalIPv6Address   string
	GlobalIPv6PrefixLen int
	IPAddress           string
	IPPrefixLen         int
	IPv6Gateway         string
	MacAddress          string
}

// MountPoint represents a mount point configuration inside the container.
type MountPoint struct {
	Name        string `json:",omitempty"`
	Source      string
	Destination string
	Driver      string `json:",omitempty"`
	Mode        string
	RW          bool
	Propagation string
}

// Volume represents the configuration of a volume for the remote API
type Volume struct {
	Name       string // Name is the name of the volume
	Driver     string // Driver is the Driver name used to create the volume
	Mountpoint string // Mountpoint is the location on disk of the volume
}

// VolumesListResponse contains the response for the remote API:
// GET "/volumes"
type VolumesListResponse struct {
	Volumes  []*Volume // Volumes is the list of volumes being returned
	Warnings []string  // Warnings is a list of warnings that occurred when getting the list from the volume drivers
}

// VolumeCreateRequest contains the response for the remote API:
// POST "/volumes/create"
type VolumeCreateRequest struct {
	Name       string            // Name is the requested name of the volume
	Driver     string            // Driver is the name of the driver that should be used to create the volume
	DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
}

// NetworkResource is the body of the "get network" http response message
type NetworkResource struct {
	Name       string
	ID         string `json:"Id"`
	Scope      string
	Driver     string
	IPAM       network.IPAM
	Containers map[string]EndpointResource
	Options    map[string]string
}

// EndpointResource contains network resources allocated and used for a container in a network
type EndpointResource struct {
	Name        string
	EndpointID  string
	MacAddress  string
	IPv4Address string
	IPv6Address string
}

// NetworkCreate is the expected body of the "create network" http request message
type NetworkCreate struct {
	Name           string
	CheckDuplicate bool
	Driver         string
	IPAM           network.IPAM
	Internal       bool
	Options        map[string]string
}

// NetworkCreateResponse is the response message sent by the server for network create call
type NetworkCreateResponse struct {
	ID      string `json:"Id"`
	Warning string
}

// NetworkConnect represents the data to be used to connect a container to the network
type NetworkConnect struct {
	Container      string
	EndpointConfig *network.EndpointSettings `json:",omitempty"`
}

// NetworkDisconnect represents the data to be used to disconnect a container from the network
type NetworkDisconnect struct {
	Container string
	Force     bool
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/types"
)

// jobHost unlocks the wallet, mines some currency, and starts a host offering
// storage to the ant farm.
func (j *JobRunner) jobHost() {
	err := j.client.Post("/wallet/unlock", fmt.Sprintf("encryptionpassword=%s&dictionary=%s", j.walletPassword, "english"), nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR: %v\n", j.siaDirectory, err)
		return
	}

	err = j.client.Get("/miner/start", nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR: %v\n", j.siaDirectory, err)
		return
	}

	// Mine at least 50,000 SC
	desiredbalance := types.NewCurrency64(50000).Mul(types.SiacoinPrecision)
	balance := types.NewCurrency64(0)
	for {
		var walletInfo api.WalletGET
		err = j.client.Get("/wallet", &walletInfo)
		if err != nil {
			log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
			return
		}
		if balance.Cmp(desiredbalance) > 0 {
			break
		}
		balance = walletInfo.ConfirmedSiacoinBalance
		time.Sleep(time.Second)
	}

	// Create a temporary folder for hosting
	hostdir, err := ioutil.TempDir("", "hostdata")
	if err != nil {
		log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
		return
	}
	defer os.RemoveAll(hostdir)

	// For now, hard code some sane host settings for this job.  In the future,
	// this job can take args to determine these settings.
	err = j.client.Post("/host/storage/folders/add", fmt.Sprintf("path=%s&size=30000000000"), nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	// Announce the host to the network
	err = j.client.Post("/host/announce", "", nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	// Poll the host API and log total storage
	for {
		var hostInfo api.HostGET
		err = j.client.Get("/host", &hostInfo)
		if err != nil {
			log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
			return
		}
		log.Printf("[%v jobHost INFO]: %v", j.siaDirectory, hostInfo.NetworkMetrics)
		time.Sleep(time.Second * 5)
	}
}
<commit_msg>add check for decreasing StorageRevenue<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/types"
)

// jobHost unlocks the wallet, mines some currency, and starts a host offering
// storage to the ant farm.
func (j *JobRunner) jobHost() {
	err := j.client.Post("/wallet/unlock", fmt.Sprintf("encryptionpassword=%s&dictionary=%s", j.walletPassword, "english"), nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR: %v\n", j.siaDirectory, err)
		return
	}

	err = j.client.Get("/miner/start", nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR: %v\n", j.siaDirectory, err)
		return
	}

	// Mine at least 50,000 SC
	desiredbalance := types.NewCurrency64(50000).Mul(types.SiacoinPrecision)
	balance := types.NewCurrency64(0)
	for {
		var walletInfo api.WalletGET
		err = j.client.Get("/wallet", &walletInfo)
		if err != nil {
			log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
			return
		}
		if balance.Cmp(desiredbalance) > 0 {
			break
		}
		balance = walletInfo.ConfirmedSiacoinBalance
		time.Sleep(time.Second)
	}

	// Create a temporary folder for hosting
	hostdir, err := ioutil.TempDir("", "hostdata")
	if err != nil {
		log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
		return
	}
	defer os.RemoveAll(hostdir)

	// For now, hard code some sane host settings for this job.  In the future,
	// this job can take args to determine these settings.
	err = j.client.Post("/host/storage/folders/add", fmt.Sprintf("path=%s&size=30000000000"), nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	// Announce the host to the network
	err = j.client.Post("/host/announce", "", nil)
	if err != nil {
		log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	maxRevenue := types.NewCurrency64(0)
	for {
		var hostInfo api.HostGET
		err = j.client.Get("/host", &hostInfo)
		if err != nil {
			log.Printf("[%v jobHost ERROR]: %v\n", j.siaDirectory, err)
			return
		}
		log.Printf("[%v jobHost INFO]: %v", j.siaDirectory, hostInfo.NetworkMetrics)

		// Print an error if storage revenue has decreased
		if hostInfo.FinancialMetrics.StorageRevenue.Cmp(maxRevenue) > 0 {
			maxRevenue = hostInfo.FinancialMetrics.StorageRevenue
		} else {
			// Storage revenue has decreased!
			log.Printf("[%v jobHost ERROR]: StorageRevenue decreased!  was %v is now %v\n", j.siaDirectory, maxRevenue, hostInfo.FinancialMetrics.StorageRevenue)
		}

		time.Sleep(time.Second * 5)
	}
}
<|endoftext|>


<commit_before>package sidecarhttp

import (
	"net/http"
	_ "net/http/pprof"
	"time"

	"github.com/Nitro/memberlist"
	"github.com/Nitro/sidecar/catalog"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"
)

type HttpConfig struct {
	BindIP       string
	UseHostnames bool
}

func makeHandler(fn func(http.ResponseWriter, *http.Request,
	*memberlist.Memberlist, *catalog.ServicesState, map[string]string),
	list *memberlist.Memberlist, state *catalog.ServicesState) http.HandlerFunc {

	return func(response http.ResponseWriter, req *http.Request) {
		fn(response, req, list, state, mux.Vars(req))
	}
}

func serversHandler(response http.ResponseWriter, req *http.Request, list *memberlist.Memberlist, state *catalog.ServicesState, params map[string]string) {
	defer req.Body.Close()

	response.Header().Set("Content-Type", "text/html")
	state.RLock()
	defer state.RUnlock()

	response.Write(
		[]byte(`

 			<meta http-equiv="refresh" content="4">

` + state.Format(list) + "
"))
}

type Member struct {
	Node    *memberlist.Node
	Updated time.Time
}

func uiRedirectHandler(response http.ResponseWriter, req *http.Request) {
	http.Redirect(response, req, "/ui/", 301)
}

func ServeHttp(list *memberlist.Memberlist, state *catalog.ServicesState, config *HttpConfig) {
	srvrsHandle := makeHandler(serversHandler, list, state)
	staticFs := http.FileServer(http.Dir("views/static"))
	uiFs := http.FileServer(http.Dir("ui/app"))

	api := &SidecarApi{state: state, list: list}
	envoyApi := &EnvoyApi{state: state, list: list, config: config}

	router := mux.NewRouter()
	router.HandleFunc("/", uiRedirectHandler).Methods("GET")
	router.HandleFunc("/servers", srvrsHandle).Methods("GET")
	router.PathPrefix("/static").Handler(http.StripPrefix("/static", staticFs))
	router.PathPrefix("/ui").Handler(http.StripPrefix("/ui", uiFs))
	router.PathPrefix("/api").Handler(http.StripPrefix("/api", api.HttpMux()))
	router.PathPrefix("/v1").Handler(http.StripPrefix("/v1", envoyApi.HttpMux()))

	// DEPRECATED - to be removed once common clients are updated
	router.HandleFunc("/state.{extension}", wrap(api.stateHandler)).Methods("GET")
	router.HandleFunc("/watch", wrap(api.watchHandler)).Methods("GET")
	// ------------------------------------------------------------

	http.Handle("/", router)

	err := http.ListenAndServe("0.0.0.0:7777", nil)
	if err != nil {
		log.Fatalf("Can't start HTTP server: %s", err)
	}
}
<commit_msg>Turns out this was still required<commit_after>package sidecarhttp

import (
	"net/http"
	_ "net/http/pprof"
	"time"

	"github.com/Nitro/memberlist"
	"github.com/Nitro/sidecar/catalog"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"
)

type HttpConfig struct {
	BindIP       string
	UseHostnames bool
}

func makeHandler(fn func(http.ResponseWriter, *http.Request,
	*memberlist.Memberlist, *catalog.ServicesState, map[string]string),
	list *memberlist.Memberlist, state *catalog.ServicesState) http.HandlerFunc {

	return func(response http.ResponseWriter, req *http.Request) {
		fn(response, req, list, state, mux.Vars(req))
	}
}

func serversHandler(response http.ResponseWriter, req *http.Request, list *memberlist.Memberlist, state *catalog.ServicesState, params map[string]string) {
	defer req.Body.Close()

	response.Header().Set("Content-Type", "text/html")
	state.RLock()
	defer state.RUnlock()

	response.Write(
		[]byte(`

 			<meta http-equiv="refresh" content="4">

` + state.Format(list) + "
"))
}

type Member struct {
	Node    *memberlist.Node
	Updated time.Time
}

func uiRedirectHandler(response http.ResponseWriter, req *http.Request) {
	http.Redirect(response, req, "/ui/", 301)
}

func ServeHttp(list *memberlist.Memberlist, state *catalog.ServicesState, config *HttpConfig) {
	srvrsHandle := makeHandler(serversHandler, list, state)
	staticFs := http.FileServer(http.Dir("views/static"))
	uiFs := http.FileServer(http.Dir("ui/app"))

	api := &SidecarApi{state: state, list: list}
	envoyApi := &EnvoyApi{state: state, list: list, config: config}

	router := mux.NewRouter()
	router.HandleFunc("/", uiRedirectHandler).Methods("GET")
	router.HandleFunc("/servers", srvrsHandle).Methods("GET")
	router.PathPrefix("/static").Handler(http.StripPrefix("/static", staticFs))
	router.PathPrefix("/ui").Handler(http.StripPrefix("/ui", uiFs))
	router.PathPrefix("/api").Handler(http.StripPrefix("/api", api.HttpMux()))
	router.PathPrefix("/v1").Handler(http.StripPrefix("/v1", envoyApi.HttpMux()))

	// DEPRECATED - to be removed once common clients are updated
	router.HandleFunc("/services.{extension}", wrap(api.servicesHandler)).Methods("GET")
	router.HandleFunc("/state.{extension}", wrap(api.stateHandler)).Methods("GET")
	router.HandleFunc("/watch", wrap(api.watchHandler)).Methods("GET")
	// ------------------------------------------------------------

	http.Handle("/", router)

	err := http.ListenAndServe("0.0.0.0:7777", nil)
	if err != nil {
		log.Fatalf("Can't start HTTP server: %s", err)
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/estafette/estafette-ci-contracts"

	"github.com/estafette/estafette-ci-api/cockroach"
	"github.com/estafette/estafette-ci-api/config"
	crypt "github.com/estafette/estafette-ci-crypt"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/rs/zerolog/log"
)

// EventHandler handles http events for Slack integration
type EventHandler interface {
	Handle(*gin.Context)
	HasValidVerificationToken(SlashCommand) bool
}

type eventHandlerImpl struct {
	secretHelper                 crypt.SecretHelper
	config                       config.SlackConfig
	cockroachDBClient            cockroach.DBClient
	apiConfig                    config.APIServerConfig
	prometheusInboundEventTotals *prometheus.CounterVec
}

// NewSlackEventHandler returns a new slack.EventHandler
func NewSlackEventHandler(secretHelper crypt.SecretHelper, config config.SlackConfig, cockroachDBClient cockroach.DBClient, apiConfig config.APIServerConfig, prometheusInboundEventTotals *prometheus.CounterVec) EventHandler {
	return &eventHandlerImpl{
		secretHelper:                 secretHelper,
		config:                       config,
		cockroachDBClient:            cockroachDBClient,
		apiConfig:                    apiConfig,
		prometheusInboundEventTotals: prometheusInboundEventTotals,
	}
}

func (h *eventHandlerImpl) Handle(c *gin.Context) {

	// https://api.slack.com/slash-commands

	h.prometheusInboundEventTotals.With(prometheus.Labels{"event": "", "source": "slack"}).Inc()

	var slashCommand SlashCommand
	// This will infer what binder to use depending on the content-type header.
	err := c.Bind(&slashCommand)
	if err != nil {
		log.Error().Err(err).Msg("Binding form data from Slack command webhook failed")
		c.String(http.StatusInternalServerError, "Binding form data from Slack command webhook failed")
		return
	}

	log.Debug().Interface("slashCommand", slashCommand).Msg("Deserialized slash command")

	hasValidVerificationToken := h.HasValidVerificationToken(slashCommand)
	if !hasValidVerificationToken {
		log.Warn().Str("expectedToken", h.config.AppVerificationToken).Str("actualToken", slashCommand.Token).Msg("Verification token for Slack command is invalid")
		c.String(http.StatusBadRequest, "Verification token for Slack command is invalid")
		return
	}

	if slashCommand.Command == "/estafette" {
		if slashCommand.Text != "" {
			splittedText := strings.Split(slashCommand.Text, " ")
			if splittedText != nil && len(splittedText) > 0 {
				command := splittedText[0]
				arguments := splittedText[1:len(splittedText)]
				switch command {
				case "encrypt":

					encryptedString, err := h.secretHelper.Encrypt(strings.Join(arguments, " "))
					if err != nil {
						log.Error().Err(err).Interface("slashCommand", slashCommand).Msg("Failed to encrypt secret")
						c.String(http.StatusOK, "Incorrect usage of /estafette encrypt!")
						return
					}

					c.String(http.StatusOK, fmt.Sprintf("estafette.secret(%v)", encryptedString))
					return

				case "release":

					// # release latest green version (for release branch?)
					// /estafette release github.com/estafette/estafette-ci-builder beta
					// /estafette release github.com/estafette/estafette-ci-api tooling

					// # release latest green version for particular branch
					// /estafette release github.com/estafette/estafette-ci-builder beta master

					// # release specific version
					// /estafette release github.com/estafette/estafette-ci-builder beta 0.0.47
					// /estafette release github.com/estafette/estafette-ci-api beta 0.0.130

					if len(arguments) < 3 {
						c.String(http.StatusOK, "You have to few arguments, the command has to be of type /estafette release <repo> <release> <version>")
						return
					}

					// retrieve pipeline with first argument
					fullRepoName := arguments[0]
					releaseName := arguments[1]
					buildVersion := arguments[2]

					fullRepoNameArray := strings.Split(fullRepoName, "/")

					pipeline, err := h.cockroachDBClient.GetPipeline(fullRepoNameArray[0], fullRepoNameArray[1], fullRepoNameArray[2])
					if err != nil {
						c.String(http.StatusOK, fmt.Sprintf("The repo %v in your command does not have any estafette builds", fullRepoName))
						return
					}

					// check if release target exists
					releaseExists := false
					for _, release := range pipeline.Releases {
						if release.Name == releaseName {
							releaseExists = true
							break
						}
					}

					if !releaseExists {
						c.String(http.StatusOK, fmt.Sprintf("The release %v in your command is not defined in the manifest", releaseName))
						return
					}

					// check if version exists
					build, err := h.cockroachDBClient.GetPipelineBuildByVersion(fullRepoNameArray[0], fullRepoNameArray[1], fullRepoNameArray[2], buildVersion)
					if err != nil {
						c.String(http.StatusOK, fmt.Sprintf("The version %v in your command does not exist", buildVersion))
						return
					}

					// create release in database
					release := contracts.Release{
						Name:           releaseName,
						RepoSource:     build.RepoSource,
						RepoOwner:      build.RepoOwner,
						RepoName:       build.RepoName,
						ReleaseVersion: buildVersion,
						ReleaseStatus:  "running",
						TriggeredBy:    slashCommand.UserName,
					}
					insertedRelease, err := h.cockroachDBClient.InsertRelease(release)
					if err != nil {
						c.String(http.StatusOK, fmt.Sprintf("Inserting the release into the database failed: %v", err))
						return
					}

					// start release job

					c.String(http.StatusOK, fmt.Sprintf("Started releasing version %v to %v: %vpipelines/%v/releases/%v", buildVersion, releaseName, h.apiConfig.BaseURL, fullRepoName, insertedRelease.ID))
					return
				}
			}
		}
	}

	c.String(http.StatusOK, "Aye aye!")
}

func (h *eventHandlerImpl) HasValidVerificationToken(slashCommand SlashCommand) bool {
	return slashCommand.Token == h.config.AppVerificationToken
}
<commit_msg>improved error checking for estafette release integration in slack<commit_after>package slack

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/estafette/estafette-ci-contracts"

	"github.com/estafette/estafette-ci-api/cockroach"
	"github.com/estafette/estafette-ci-api/config"
	crypt "github.com/estafette/estafette-ci-crypt"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/rs/zerolog/log"
)

// EventHandler handles http events for Slack integration
type EventHandler interface {
	Handle(*gin.Context)
	HasValidVerificationToken(SlashCommand) bool
}

type eventHandlerImpl struct {
	secretHelper                 crypt.SecretHelper
	config                       config.SlackConfig
	cockroachDBClient            cockroach.DBClient
	apiConfig                    config.APIServerConfig
	prometheusInboundEventTotals *prometheus.CounterVec
}

// NewSlackEventHandler returns a new slack.EventHandler
func NewSlackEventHandler(secretHelper crypt.SecretHelper, config config.SlackConfig, cockroachDBClient cockroach.DBClient, apiConfig config.APIServerConfig, prometheusInboundEventTotals *prometheus.CounterVec) EventHandler {
	return &eventHandlerImpl{
		secretHelper:                 secretHelper,
		config:                       config,
		cockroachDBClient:            cockroachDBClient,
		apiConfig:                    apiConfig,
		prometheusInboundEventTotals: prometheusInboundEventTotals,
	}
}

func (h *eventHandlerImpl) Handle(c *gin.Context) {

	// https://api.slack.com/slash-commands

	h.prometheusInboundEventTotals.With(prometheus.Labels{"event": "", "source": "slack"}).Inc()

	var slashCommand SlashCommand
	// This will infer what binder to use depending on the content-type header.
	err := c.Bind(&slashCommand)
	if err != nil {
		log.Error().Err(err).Msg("Binding form data from Slack command webhook failed")
		c.String(http.StatusInternalServerError, "Binding form data from Slack command webhook failed")
		return
	}

	log.Debug().Interface("slashCommand", slashCommand).Msg("Deserialized slash command")

	hasValidVerificationToken := h.HasValidVerificationToken(slashCommand)
	if !hasValidVerificationToken {
		log.Warn().Str("expectedToken", h.config.AppVerificationToken).Str("actualToken", slashCommand.Token).Msg("Verification token for Slack command is invalid")
		c.String(http.StatusBadRequest, "Verification token for Slack command is invalid")
		return
	}

	if slashCommand.Command == "/estafette" {
		if slashCommand.Text != "" {
			splittedText := strings.Split(slashCommand.Text, " ")
			if splittedText != nil && len(splittedText) > 0 {
				command := splittedText[0]
				arguments := splittedText[1:len(splittedText)]
				switch command {
				case "encrypt":

					encryptedString, err := h.secretHelper.Encrypt(strings.Join(arguments, " "))
					if err != nil {
						log.Error().Err(err).Interface("slashCommand", slashCommand).Msg("Failed to encrypt secret")
						c.String(http.StatusOK, "Incorrect usage of /estafette encrypt!")
						return
					}

					c.String(http.StatusOK, fmt.Sprintf("estafette.secret(%v)", encryptedString))
					return

				case "release":

					// # release latest green version (for release branch?)
					// /estafette release github.com/estafette/estafette-ci-builder beta
					// /estafette release github.com/estafette/estafette-ci-api tooling

					// # release latest green version for particular branch
					// /estafette release github.com/estafette/estafette-ci-builder beta master

					// # release specific version
					// /estafette release github.com/estafette/estafette-ci-builder beta 0.0.47
					// /estafette release github.com/estafette/estafette-ci-api beta 0.0.130

					if len(arguments) < 3 {
						c.String(http.StatusOK, "You have to few arguments, the command has to be of type /estafette release <repo> <release> <version>")
						return
					}

					// retrieve pipeline with first argument
					fullRepoName := arguments[0]
					releaseName := arguments[1]
					buildVersion := arguments[2]

					fullRepoNameArray := strings.Split(fullRepoName, "/")

					pipeline, err := h.cockroachDBClient.GetPipeline(fullRepoNameArray[0], fullRepoNameArray[1], fullRepoNameArray[2])
					if err != nil || pipeline == nil {
						c.String(http.StatusOK, fmt.Sprintf("The repo %v in your command does not have any estafette builds", fullRepoName))
						return
					}

					// check if release target exists
					releaseExists := false
					for _, release := range pipeline.Releases {
						if release.Name == releaseName {
							releaseExists = true
							break
						}
					}

					if !releaseExists {
						c.String(http.StatusOK, fmt.Sprintf("The release %v in your command is not defined in the manifest", releaseName))
						return
					}

					// check if version exists
					build, err := h.cockroachDBClient.GetPipelineBuildByVersion(fullRepoNameArray[0], fullRepoNameArray[1], fullRepoNameArray[2], buildVersion)
					if err != nil || build == nil {
						c.String(http.StatusOK, fmt.Sprintf("The version %v in your command does not exist", buildVersion))
						return
					}

					// create release in database
					release := contracts.Release{
						Name:           releaseName,
						RepoSource:     build.RepoSource,
						RepoOwner:      build.RepoOwner,
						RepoName:       build.RepoName,
						ReleaseVersion: buildVersion,
						ReleaseStatus:  "running",
						TriggeredBy:    slashCommand.UserName,
					}
					insertedRelease, err := h.cockroachDBClient.InsertRelease(release)
					if err != nil {
						c.String(http.StatusOK, fmt.Sprintf("Inserting the release into the database failed: %v", err))
						return
					}

					// start release job

					c.String(http.StatusOK, fmt.Sprintf("Started releasing version %v to %v: %vpipelines/%v/releases/%v", buildVersion, releaseName, h.apiConfig.BaseURL, fullRepoName, insertedRelease.ID))
					return
				}
			}
		}
	}

	c.String(http.StatusOK, "Aye aye!")
}

func (h *eventHandlerImpl) HasValidVerificationToken(slashCommand SlashCommand) bool {
	return slashCommand.Token == h.config.AppVerificationToken
}
<|endoftext|>


<commit_before>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// 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 builder

import (
	"testing"
)

func TestInitialization(t *testing.T) {
	ng := NewNgramBuffer(3)
	if ng.begin != 0 {
		t.Errorf("ng.begin != 0, value = %d", ng.begin)
	}
	if ng.write != -1 {
		t.Errorf("ng.write != -1, value = %d", ng.write)
	}
	if len(ng.data) != 3 {
		t.Error("ng.data length != 3")
	}
	if cap(ng.data) != 3 {
		t.Error("ng.data capacity != 3")
	}
}

func TestInsertFirst(t *testing.T) {
	ng := NewNgramBuffer(3)
	ng.AddToken("foo")
	if ng.begin != 1 {
		t.Error("ng.begin != 1")
	}
	if ng.write != 0 {
		t.Error("ng.write != 0")
	}
}

func TestInsertedValues(t *testing.T) {
	ng := NewNgramBuffer(3)
	ng.AddToken("foo")
	ng.AddToken("bar")
	ng.AddToken("baz")
	v := ng.GetValue()
	if v[0] != "foo" || v[1] != "bar" || v[2] != "baz" {
		t.Errorf("ng.GetValue() != [foo, bar, baz], value: %s", v)
	}
}

func TestInsertedValues2(t *testing.T) {
	ng := NewNgramBuffer(3)
	ng.AddToken("foo")
	ng.AddToken("bar")
	ng.AddToken("baz")
	ng.AddToken("dex")
	v := ng.GetValue()
	if v[0] != "bar" || v[1] != "baz" || v[2] != "dex" {
		t.Errorf("ng.GetValue() != [bar, baz, dex], value: %s", v)
	}
}

func TestReset(t *testing.T) {
	ng := NewNgramBuffer(3)
	ng.AddToken("foo")
	ng.AddToken("bar")
	ng.AddToken("baz")
	ng.Reset()
	if ng.begin != 0 {
		t.Errorf("ng.begin != 0, value = %d", ng.begin)
	}
	if ng.write != -1 {
		t.Errorf("ng.write != -1, value = %d", ng.write)
	}
	if ng.data[0] != "" || ng.data[1] != "" || ng.data[2] != "" {
		t.Errorf("ng.data != ['', '', ''], value: %s", ng.data)
	}
}
<commit_msg>Fix test (broken by type changes)<commit_after>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// 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 builder

import (
	"testing"
)

func TestInitialization(t *testing.T) {
	ng := NewStdNgramBuffer(3)
	if ng.begin != 0 {
		t.Errorf("ng.begin != 0, value = %d", ng.begin)
	}
	if ng.write != -1 {
		t.Errorf("ng.write != -1, value = %d", ng.write)
	}
	if len(ng.data) != 3 {
		t.Error("ng.data length != 3")
	}
	if cap(ng.data) != 3 {
		t.Error("ng.data capacity != 3")
	}
}

func TestInsertFirst(t *testing.T) {
	ng := NewStdNgramBuffer(3)
	ng.AddToken("foo")
	if ng.begin != 1 {
		t.Error("ng.begin != 1")
	}
	if ng.write != 0 {
		t.Error("ng.write != 0")
	}
}

func TestInsertedValues(t *testing.T) {
	ng := NewStdNgramBuffer(3)
	ng.AddToken("foo")
	ng.AddToken("bar")
	ng.AddToken("baz")
	v := ng.GetValue()
	if v[0] != "foo" || v[1] != "bar" || v[2] != "baz" {
		t.Errorf("ng.GetValue() != [foo, bar, baz], value: %s", v)
	}
}

func TestInsertedValues2(t *testing.T) {
	ng := NewStdNgramBuffer(3)
	ng.AddToken("foo")
	ng.AddToken("bar")
	ng.AddToken("baz")
	ng.AddToken("dex")
	v := ng.GetValue()
	if v[0] != "bar" || v[1] != "baz" || v[2] != "dex" {
		t.Errorf("ng.GetValue() != [bar, baz, dex], value: %s", v)
	}
}

func TestReset(t *testing.T) {
	ng := NewStdNgramBuffer(3)
	ng.AddToken("foo")
	ng.AddToken("bar")
	ng.AddToken("baz")
	ng.Reset()
	if ng.begin != 0 {
		t.Errorf("ng.begin != 0, value = %d", ng.begin)
	}
	if ng.write != -1 {
		t.Errorf("ng.write != -1, value = %d", ng.write)
	}
	if ng.data[0] != "" || ng.data[1] != "" || ng.data[2] != "" {
		t.Errorf("ng.data != ['', '', ''], value: %s", ng.data)
	}
}
<|endoftext|>


<commit_before>package integration

import (
	"os"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("kismatic upgrade tests", func() {
	BeforeEach(func() {
		dir := setupTestWorkingDirWithVersion("v1.2.2")
		os.Chdir(dir)
	})

	Describe("Doing an offline upgrade of a kubernetes cluster", func() {
		Context("Using a minikube layout", func() {
			Context("Using Ubuntu 16.04", func() {
				ItOnAWS("should result in an upgraded cluster [slow] [upgrade]", func(aws infrastructureProvisioner) {
					WithMiniInfrastructure(Ubuntu1604LTS, aws, func(node NodeDeets, sshKey string) {
						// Install previous version cluster
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())

						// Extract current version of kismatic
						pwd, err := os.Getwd()
						Expect(err).ToNot(HaveOccurred())
						err = extractCurrentKismatic(pwd)
						Expect(err).ToNot(HaveOccurred())

						// Perform upgrade
						cmd := exec.Command("./kismatic", "upgrade", "offline", "-f", "kismatic-testing.yaml")
						cmd.Stderr = os.Stderr
						cmd.Stdout = os.Stdout
						err = cmd.Run()
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			PContext("Using CentOS 7", func() {
				ItOnAWS("should result in an upgraded cluster [slow] [upgrade]", func(aws infrastructureProvisioner) {
					WithMiniInfrastructure(CentOS7, aws, func(node NodeDeets, sshKey string) {
						// Install previous version cluster
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())

						// Extract new version of kismatic
						pwd, err := os.Getwd()
						Expect(err).ToNot(HaveOccurred())
						err = extractCurrentKismatic(pwd)
						Expect(err).ToNot(HaveOccurred())

						// Perform upgrade
						cmd := exec.Command("./kismatic", "upgrade", "offline", "-f", "kismatic-testing.yaml", "--skip-preflight")
						cmd.Stderr = os.Stderr
						cmd.Stdout = os.Stdout
						err = cmd.Run()
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})

	})
})
<commit_msg>Enable centos upgrade test<commit_after>package integration

import (
	"os"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("kismatic upgrade tests", func() {
	BeforeEach(func() {
		dir := setupTestWorkingDirWithVersion("v1.2.2")
		os.Chdir(dir)
	})

	Describe("Doing an offline upgrade of a kubernetes cluster", func() {
		Context("Using a minikube layout", func() {
			Context("Using Ubuntu 16.04", func() {
				ItOnAWS("should result in an upgraded cluster [slow] [upgrade]", func(aws infrastructureProvisioner) {
					WithMiniInfrastructure(Ubuntu1604LTS, aws, func(node NodeDeets, sshKey string) {
						// Install previous version cluster
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())

						// Extract current version of kismatic
						pwd, err := os.Getwd()
						Expect(err).ToNot(HaveOccurred())
						err = extractCurrentKismatic(pwd)
						Expect(err).ToNot(HaveOccurred())

						// Perform upgrade
						cmd := exec.Command("./kismatic", "upgrade", "offline", "-f", "kismatic-testing.yaml")
						cmd.Stderr = os.Stderr
						cmd.Stdout = os.Stdout
						err = cmd.Run()
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			Context("Using CentOS 7", func() {
				ItOnAWS("should result in an upgraded cluster [slow] [upgrade]", func(aws infrastructureProvisioner) {
					WithMiniInfrastructure(CentOS7, aws, func(node NodeDeets, sshKey string) {
						// Install previous version cluster
						err := installKismaticMini(node, sshKey)
						Expect(err).ToNot(HaveOccurred())

						// Extract new version of kismatic
						pwd, err := os.Getwd()
						Expect(err).ToNot(HaveOccurred())
						err = extractCurrentKismatic(pwd)
						Expect(err).ToNot(HaveOccurred())

						// Perform upgrade
						cmd := exec.Command("./kismatic", "upgrade", "offline", "-f", "kismatic-testing.yaml")
						cmd.Stderr = os.Stderr
						cmd.Stdout = os.Stdout
						err = cmd.Run()
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})
		})

	})
})
<|endoftext|>


<commit_before>package projects

import "time"

const (
	// GetSbomEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomEndpoint = "v1/project/getSBOM"
	// GetSbomsEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomsEndpoint = "v1/project/getSBOMs"
)

// SBOM represents a software list containing zero or more SBOMEntry objects.
type SBOM struct {
	ID         string      `json:"id"`
	Name       string      `json:"sbom_name"`
	Version    string      `json:"sbom_version"`
	Supplier   string      `json:"supplier_name"`
	SbomType   string      `json:"sbom_type"`
	SbomStatus string      `json:"sbom_status"`
	CreatedAt  time.Time   `json:"created_at"`
	UpdatedAt  time.Time   `json:"updated_at"`
	EntryCount int         `json:"entry_count"`
	Entries    []SBOMEntry `json:"entries"`
	TeamID     string      `json:"team_id"`
	OrgID      string      `json:"org_id"`
	RulesetID  string      `json:"ruleset_id"`
}
<commit_msg>Remove sbom_type field as it's being deprecated.<commit_after>package projects

import "time"

const (
	// GetSbomEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomEndpoint = "v1/project/getSBOM"
	// GetSbomsEndpoint is the current endpoint for retrieving an organization's SBOMs
	GetSbomsEndpoint = "v1/project/getSBOMs"
)

// SBOM represents a software list containing zero or more SBOMEntry objects.
type SBOM struct {
	ID         string      `json:"id"`
	Name       string      `json:"sbom_name"`
	Version    string      `json:"sbom_version"`
	Supplier   string      `json:"supplier_name"`
	SbomStatus string      `json:"sbom_status"`
	CreatedAt  time.Time   `json:"created_at"`
	UpdatedAt  time.Time   `json:"updated_at"`
	EntryCount int         `json:"entry_count"`
	Entries    []SBOMEntry `json:"entries"`
	TeamID     string      `json:"team_id"`
	OrgID      string      `json:"org_id"`
	RulesetID  string      `json:"ruleset_id"`
}
<|endoftext|>


<commit_before>// Copyright 2019 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 derrors implements some common error semantics to be used by other
// internal packages.
package derrors

// errorMessage is type that can be embedded to implement the error interface.
type errorMessage string

func (m errorMessage) Error() string {
	return string(m)
}

type notFound struct {
	errorMessage
}

// NotFound creates a new error message that indicates the requested entity is
// not found.
func NotFound(msg string) error {
	return notFound{
		errorMessage: errorMessage(msg),
	}
}

// IsNotFound reports whether err is a NotFound error.
func IsNotFound(err error) bool {
	if err == nil {
		return false
	}
	_, ok := err.(notFound)
	return ok
}

type invalidArgument struct {
	errorMessage
}

// InvalidArgument creates a new error that indicates the given arguments are
// invalid.
func InvalidArgument(msg string) error {
	return invalidArgument{
		errorMessage: errorMessage(msg),
	}
}

// IsInvalidArgument reports whether err is an InvalidArguments error.
func IsInvalidArgument(err error) bool {
	if err == nil {
		return false
	}
	_, ok := err.(invalidArgument)
	return ok
}
<commit_msg>internal/derrors: remove if err == nil check<commit_after>// Copyright 2019 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 derrors implements some common error semantics to be used by other
// internal packages.
package derrors

// errorMessage is type that can be embedded to implement the error interface.
type errorMessage string

func (m errorMessage) Error() string {
	return string(m)
}

type notFound struct {
	errorMessage
}

// NotFound creates a new error message that indicates the requested entity is
// not found.
func NotFound(msg string) error {
	return notFound{
		errorMessage: errorMessage(msg),
	}
}

// IsNotFound reports whether err is a NotFound error.
func IsNotFound(err error) bool {
	_, ok := err.(notFound)
	return ok
}

type invalidArgument struct {
	errorMessage
}

// InvalidArgument creates a new error that indicates the given arguments are
// invalid.
func InvalidArgument(msg string) error {
	return invalidArgument{
		errorMessage: errorMessage(msg),
	}
}

// IsInvalidArgument reports whether err is an InvalidArguments error.
func IsInvalidArgument(err error) bool {
	_, ok := err.(invalidArgument)
	return ok
}
<|endoftext|>


<commit_before>package fakes

import (
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/pivotal-golang/conceal"
)

type Tokenizer struct {
	key   []byte
	cloak conceal.Cloak
}

func NewTokenizer(key string) Tokenizer {
	cloak, err := conceal.NewCloak([]byte(key))
	if err != nil {
		panic(err)
	}

	return Tokenizer{
		key:   []byte(key),
		cloak: cloak,
	}
}

func (t Tokenizer) Encrypt(token Token) string {
	crypt := jwt.New(jwt.SigningMethodHS256)
	crypt.Claims = token.ToClaims()
	encrypted, err := crypt.SignedString(t.key)
	if err != nil {
		panic(err)
	}

	return encrypted
}

func (t Tokenizer) Decrypt(encryptedToken string) Token {
	token, err := jwt.Parse(encryptedToken, jwt.Keyfunc(func(*jwt.Token) (interface{}, error) {
		return t.key, nil
	}))
	if err != nil {
		panic(err)
	}

	return NewTokenFromClaims(token.Claims)
}

func (t Tokenizer) Validate(token, expected Token) bool {
	if ok := token.HasAudiences(expected.Audiences); !ok {
		return false
	}

	if ok := token.HasScopes(expected.Scopes); !ok {
		return false
	}

	return true
}

type Token struct {
	UserID    string
	ClientID  string
	Scopes    []string
	Audiences []string
}

func NewTokenFromClaims(claims map[string]interface{}) Token {
	token := Token{}

	if userID, ok := claims["user_id"].(string); ok {
		token.UserID = userID
	}

	if clientID, ok := claims["client_id"].(string); ok {
		token.ClientID = clientID
	}

	if scopes, ok := claims["scope"].([]interface{}); ok {
		var s []string
		for _, scope := range scopes {
			s = append(s, scope.(string))
		}

		token.Scopes = s
	}

	if audiences, ok := claims["aud"].(string); ok {
		token.Audiences = strings.Split(audiences, " ")
	}

	return token
}

func (t Token) ToClaims() map[string]interface{} {
	claims := make(map[string]interface{})

	if len(t.UserID) > 0 {
		claims["user_id"] = t.UserID
	}

	if len(t.ClientID) > 0 {
		claims["client_id"] = t.ClientID
	}

	claims["scope"] = t.Scopes
	claims["aud"] = strings.Join(t.Audiences, " ")

	return claims
}

func (t Token) HasScopes(scopes []string) bool {
	for _, scope := range scopes {
		if !contains(t.Scopes, scope) {
			return false
		}
	}
	return true
}

func (t Token) HasAudiences(audiences []string) bool {
	for _, audience := range audiences {
		if !contains(t.Audiences, audience) {
			return false
		}
	}
	return true
}

func contains(collection []string, item string) bool {
	for _, elem := range collection {
		if elem == item {
			return true
		}
	}

	return false
}
<commit_msg>Removes conceal dependency<commit_after>package fakes

import (
	"strings"

	"github.com/dgrijalva/jwt-go"
)

type Tokenizer struct {
	key []byte
}

func NewTokenizer(key string) Tokenizer {
	return Tokenizer{
		key: []byte(key),
	}
}

func (t Tokenizer) Encrypt(token Token) string {
	crypt := jwt.New(jwt.SigningMethodHS256)
	crypt.Claims = token.ToClaims()
	encrypted, err := crypt.SignedString(t.key)
	if err != nil {
		panic(err)
	}

	return encrypted
}

func (t Tokenizer) Decrypt(encryptedToken string) Token {
	token, err := jwt.Parse(encryptedToken, jwt.Keyfunc(func(*jwt.Token) (interface{}, error) {
		return t.key, nil
	}))
	if err != nil {
		panic(err)
	}

	return NewTokenFromClaims(token.Claims)
}

func (t Tokenizer) Validate(token, expected Token) bool {
	if ok := token.HasAudiences(expected.Audiences); !ok {
		return false
	}

	if ok := token.HasScopes(expected.Scopes); !ok {
		return false
	}

	return true
}

type Token struct {
	UserID    string
	ClientID  string
	Scopes    []string
	Audiences []string
}

func NewTokenFromClaims(claims map[string]interface{}) Token {
	token := Token{}

	if userID, ok := claims["user_id"].(string); ok {
		token.UserID = userID
	}

	if clientID, ok := claims["client_id"].(string); ok {
		token.ClientID = clientID
	}

	if scopes, ok := claims["scope"].([]interface{}); ok {
		var s []string
		for _, scope := range scopes {
			s = append(s, scope.(string))
		}

		token.Scopes = s
	}

	if audiences, ok := claims["aud"].(string); ok {
		token.Audiences = strings.Split(audiences, " ")
	}

	return token
}

func (t Token) ToClaims() map[string]interface{} {
	claims := make(map[string]interface{})

	if len(t.UserID) > 0 {
		claims["user_id"] = t.UserID
	}

	if len(t.ClientID) > 0 {
		claims["client_id"] = t.ClientID
	}

	claims["scope"] = t.Scopes
	claims["aud"] = strings.Join(t.Audiences, " ")

	return claims
}

func (t Token) HasScopes(scopes []string) bool {
	for _, scope := range scopes {
		if !contains(t.Scopes, scope) {
			return false
		}
	}
	return true
}

func (t Token) HasAudiences(audiences []string) bool {
	for _, audience := range audiences {
		if !contains(t.Audiences, audience) {
			return false
		}
	}
	return true
}

func contains(collection []string, item string) bool {
	for _, elem := range collection {
		if elem == item {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Copyright 2019 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 frontend

import (
	"context"
	"errors"
	"fmt"
	"math"
	"net/http"
	"path"
	"strings"
	"time"

	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/postgres"
)

const defaultSearchLimit = 10

// SearchPage contains all of the data that the search template needs to
// populate.
type SearchPage struct {
	basePage
	Pagination pagination
	Results    []*SearchResult
}

// SearchResult contains data needed to display a single search result.
type SearchResult struct {
	Name           string
	PackagePath    string
	ModulePath     string
	Synopsis       string
	DisplayVersion string
	Licenses       []string
	CommitTime     string
	NumImportedBy  int
	Approximate    bool
}

// fetchSearchPage fetches data matching the search query from the database and
// returns a SearchPage.
func fetchSearchPage(ctx context.Context, db *postgres.DB, query string, pageParams paginationParams) (*SearchPage, error) {
	maxResultCount := maxSearchOffset + pageParams.limit
	dbresults, err := db.Search(ctx, query, pageParams.limit, pageParams.offset(), maxResultCount)
	if err != nil {
		return nil, err
	}

	var results []*SearchResult
	for _, r := range dbresults {
		results = append(results, &SearchResult{
			Name:           r.Name,
			PackagePath:    r.PackagePath,
			ModulePath:     r.ModulePath,
			Synopsis:       r.Synopsis,
			DisplayVersion: displayVersion(r.Version, r.ModulePath),
			Licenses:       r.Licenses,
			CommitTime:     elapsedTime(r.CommitTime),
			NumImportedBy:  int(r.NumImportedBy),
		})
	}

	var (
		numResults  int
		approximate bool
	)
	if len(dbresults) > 0 {
		numResults = int(dbresults[0].NumResults)
		if dbresults[0].Approximate {
			// 128 buckets corresponds to a standard error of 10%.
			// http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf
			numResults = approximateNumber(numResults, 0.1)
			approximate = true
		}
	}

	pgs := newPagination(pageParams, len(results), numResults)
	pgs.Approximate = approximate
	return &SearchPage{
		Results:    results,
		Pagination: pgs,
	}, nil
}

// approximateNumber returns an approximation of the estimate, calibrated by
// the statistical estimate of standard error.
// i.e., a number that isn't misleading when we say '1-10 of approximately N
// results', but that is still close to our estimate.
func approximateNumber(estimate int, sigma float64) int {
	expectedErr := sigma * float64(estimate)
	// Compute the unit by rounding the error the logarithmically closest power
	// of 10, so that 300->100, but 400->1000.
	unit := math.Pow(10, math.Round(math.Log10(expectedErr)))
	// Now round the estimate to the nearest unit.
	return int(unit * math.Round(float64(estimate)/unit))
}

// Search constraints.
const (
	// maxSearchQueryLength represents the max number of characters that a search
	// query can be. For PostgreSQL 11, there is a max length of 2K bytes:
	// https://www.postgresql.org/docs/11/textsearch-limitations.html. No valid
	// searches on pkg.go.dev will need more than the maxSearchQueryLength.
	maxSearchQueryLength = 500

	// maxSearchOffset is the maximum allowed offset into the search results.
	// This prevents some very CPU-intensive queries from running.
	maxSearchOffset = 90

	// maxSearchPageSize is the maximum allowed limit for search results.
	maxSearchPageSize = 100
)

// serveSearch applies database data to the search template. Handles endpoint
// /search?q=<query>. If <query> is an exact match for a package path, the user
// will be redirected to the details page.
func (s *Server) serveSearch(w http.ResponseWriter, r *http.Request, ds internal.DataSource) error {
	if r.Method != http.MethodGet && r.Method != http.MethodHead {
		return &serverError{status: http.StatusMethodNotAllowed}
	}
	db, ok := ds.(*postgres.DB)
	if !ok {
		// The proxydatasource does not support the imported by page.
		return proxydatasourceNotSupportedErr()
	}

	ctx := r.Context()
	query := searchQuery(r)
	if len(query) > maxSearchQueryLength {
		return &serverError{
			status: http.StatusBadRequest,
			epage: &errorPage{
				messageTemplate: template.MakeTrustedTemplate(
					`<h3 class="Error-message">Search query too long.</h3>`),
			},
		}
	}
	if query == "" {
		http.Redirect(w, r, "/", http.StatusFound)
		return nil
	}
	pageParams := newPaginationParams(r, defaultSearchLimit)
	if pageParams.offset() > maxSearchOffset {
		return &serverError{
			status: http.StatusBadRequest,
			epage: &errorPage{
				messageTemplate: template.MakeTrustedTemplate(
					`<h3 class="Error-message">Search page number too large.</h3>`),
			},
		}
	}
	if pageParams.limit > maxSearchPageSize {
		return &serverError{
			status: http.StatusBadRequest,
			epage: &errorPage{
				messageTemplate: template.MakeTrustedTemplate(
					`<h3 class="Error-message">Search page size too large.</h3>`),
			},
		}
	}

	if path := searchRequestRedirectPath(ctx, ds, query); path != "" {
		http.Redirect(w, r, path, http.StatusFound)
		return nil
	}
	page, err := fetchSearchPage(ctx, db, query, pageParams)
	if err != nil {
		return fmt.Errorf("fetchSearchPage(ctx, db, %q): %v", query, err)
	}
	page.basePage = s.newBasePage(r, fmt.Sprintf("%s - Search Results", query))
	s.servePage(ctx, w, "search.tmpl", page)
	return nil
}

// searchRequestRedirectPath returns the path that a search request should be
// redirected to, or the empty string if there is no such path. If the user
// types an existing package path into the search bar, we will redirect the
// user to the details page. Standard library packages that only contain one
// element (such as fmt, errors, etc.) will not redirect, to allow users to
// search by those terms.
func searchRequestRedirectPath(ctx context.Context, ds internal.DataSource, query string) string {
	urlSchemeIdx := strings.Index(query, "://")
	if urlSchemeIdx > -1 {
		query = query[urlSchemeIdx+3:]
	}
	requestedPath := path.Clean(query)
	if !strings.Contains(requestedPath, "/") {
		return ""
	}
	_, err := ds.GetUnitMeta(ctx, requestedPath, internal.UnknownModulePath, internal.LatestVersion)
	if err != nil {
		if !errors.Is(err, derrors.NotFound) {
			log.Errorf(ctx, "searchRequestRedirectPath(%q): %v", requestedPath, err)
		}
		return ""
	}
	return fmt.Sprintf("/%s", requestedPath)
}

// searchQuery extracts a search query from the request.
func searchQuery(r *http.Request) string {
	return strings.TrimSpace(r.FormValue("q"))
}

// elapsedTime takes a date and returns returns human-readable,
// relative timestamps based on the following rules:
// (1) 'X hours ago' when X < 6
// (2) 'today' between 6 hours and 1 day ago
// (3) 'Y days ago' when Y < 6
// (4) A date formatted like "Jan 2, 2006" for anything further back
func elapsedTime(date time.Time) string {
	elapsedHours := int(time.Since(date).Hours())
	if elapsedHours == 1 {
		return "1 hour ago"
	} else if elapsedHours < 6 {
		return fmt.Sprintf("%d hours ago", elapsedHours)
	}

	elapsedDays := elapsedHours / 24
	if elapsedDays < 1 {
		return "today"
	} else if elapsedDays == 1 {
		return "1 day ago"
	} else if elapsedDays < 6 {
		return fmt.Sprintf("%d days ago", elapsedDays)
	}

	return date.Format("Jan _2, 2006")
}
<commit_msg>internal/frontend: return 400 on non-UTF8 queries<commit_after>// Copyright 2019 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 frontend

import (
	"context"
	"errors"
	"fmt"
	"math"
	"net/http"
	"path"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/postgres"
)

const defaultSearchLimit = 10

// SearchPage contains all of the data that the search template needs to
// populate.
type SearchPage struct {
	basePage
	Pagination pagination
	Results    []*SearchResult
}

// SearchResult contains data needed to display a single search result.
type SearchResult struct {
	Name           string
	PackagePath    string
	ModulePath     string
	Synopsis       string
	DisplayVersion string
	Licenses       []string
	CommitTime     string
	NumImportedBy  int
	Approximate    bool
}

// fetchSearchPage fetches data matching the search query from the database and
// returns a SearchPage.
func fetchSearchPage(ctx context.Context, db *postgres.DB, query string, pageParams paginationParams) (*SearchPage, error) {
	maxResultCount := maxSearchOffset + pageParams.limit
	dbresults, err := db.Search(ctx, query, pageParams.limit, pageParams.offset(), maxResultCount)
	if err != nil {
		return nil, err
	}

	var results []*SearchResult
	for _, r := range dbresults {
		results = append(results, &SearchResult{
			Name:           r.Name,
			PackagePath:    r.PackagePath,
			ModulePath:     r.ModulePath,
			Synopsis:       r.Synopsis,
			DisplayVersion: displayVersion(r.Version, r.ModulePath),
			Licenses:       r.Licenses,
			CommitTime:     elapsedTime(r.CommitTime),
			NumImportedBy:  int(r.NumImportedBy),
		})
	}

	var (
		numResults  int
		approximate bool
	)
	if len(dbresults) > 0 {
		numResults = int(dbresults[0].NumResults)
		if dbresults[0].Approximate {
			// 128 buckets corresponds to a standard error of 10%.
			// http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf
			numResults = approximateNumber(numResults, 0.1)
			approximate = true
		}
	}

	pgs := newPagination(pageParams, len(results), numResults)
	pgs.Approximate = approximate
	return &SearchPage{
		Results:    results,
		Pagination: pgs,
	}, nil
}

// approximateNumber returns an approximation of the estimate, calibrated by
// the statistical estimate of standard error.
// i.e., a number that isn't misleading when we say '1-10 of approximately N
// results', but that is still close to our estimate.
func approximateNumber(estimate int, sigma float64) int {
	expectedErr := sigma * float64(estimate)
	// Compute the unit by rounding the error the logarithmically closest power
	// of 10, so that 300->100, but 400->1000.
	unit := math.Pow(10, math.Round(math.Log10(expectedErr)))
	// Now round the estimate to the nearest unit.
	return int(unit * math.Round(float64(estimate)/unit))
}

// Search constraints.
const (
	// maxSearchQueryLength represents the max number of characters that a search
	// query can be. For PostgreSQL 11, there is a max length of 2K bytes:
	// https://www.postgresql.org/docs/11/textsearch-limitations.html. No valid
	// searches on pkg.go.dev will need more than the maxSearchQueryLength.
	maxSearchQueryLength = 500

	// maxSearchOffset is the maximum allowed offset into the search results.
	// This prevents some very CPU-intensive queries from running.
	maxSearchOffset = 90

	// maxSearchPageSize is the maximum allowed limit for search results.
	maxSearchPageSize = 100
)

// serveSearch applies database data to the search template. Handles endpoint
// /search?q=<query>. If <query> is an exact match for a package path, the user
// will be redirected to the details page.
func (s *Server) serveSearch(w http.ResponseWriter, r *http.Request, ds internal.DataSource) error {
	if r.Method != http.MethodGet && r.Method != http.MethodHead {
		return &serverError{status: http.StatusMethodNotAllowed}
	}
	db, ok := ds.(*postgres.DB)
	if !ok {
		// The proxydatasource does not support the imported by page.
		return proxydatasourceNotSupportedErr()
	}

	ctx := r.Context()
	query := searchQuery(r)
	if !utf8.ValidString(query) {
		return &serverError{status: http.StatusBadRequest}
	}
	if len(query) > maxSearchQueryLength {
		return &serverError{
			status: http.StatusBadRequest,
			epage: &errorPage{
				messageTemplate: template.MakeTrustedTemplate(
					`<h3 class="Error-message">Search query too long.</h3>`),
			},
		}
	}
	if query == "" {
		http.Redirect(w, r, "/", http.StatusFound)
		return nil
	}
	pageParams := newPaginationParams(r, defaultSearchLimit)
	if pageParams.offset() > maxSearchOffset {
		return &serverError{
			status: http.StatusBadRequest,
			epage: &errorPage{
				messageTemplate: template.MakeTrustedTemplate(
					`<h3 class="Error-message">Search page number too large.</h3>`),
			},
		}
	}
	if pageParams.limit > maxSearchPageSize {
		return &serverError{
			status: http.StatusBadRequest,
			epage: &errorPage{
				messageTemplate: template.MakeTrustedTemplate(
					`<h3 class="Error-message">Search page size too large.</h3>`),
			},
		}
	}

	if path := searchRequestRedirectPath(ctx, ds, query); path != "" {
		http.Redirect(w, r, path, http.StatusFound)
		return nil
	}
	page, err := fetchSearchPage(ctx, db, query, pageParams)
	if err != nil {
		return fmt.Errorf("fetchSearchPage(ctx, db, %q): %v", query, err)
	}
	page.basePage = s.newBasePage(r, fmt.Sprintf("%s - Search Results", query))
	s.servePage(ctx, w, "search.tmpl", page)
	return nil
}

// searchRequestRedirectPath returns the path that a search request should be
// redirected to, or the empty string if there is no such path. If the user
// types an existing package path into the search bar, we will redirect the
// user to the details page. Standard library packages that only contain one
// element (such as fmt, errors, etc.) will not redirect, to allow users to
// search by those terms.
func searchRequestRedirectPath(ctx context.Context, ds internal.DataSource, query string) string {
	urlSchemeIdx := strings.Index(query, "://")
	if urlSchemeIdx > -1 {
		query = query[urlSchemeIdx+3:]
	}
	requestedPath := path.Clean(query)
	if !strings.Contains(requestedPath, "/") {
		return ""
	}
	_, err := ds.GetUnitMeta(ctx, requestedPath, internal.UnknownModulePath, internal.LatestVersion)
	if err != nil {
		if !errors.Is(err, derrors.NotFound) {
			log.Errorf(ctx, "searchRequestRedirectPath(%q): %v", requestedPath, err)
		}
		return ""
	}
	return fmt.Sprintf("/%s", requestedPath)
}

// searchQuery extracts a search query from the request.
func searchQuery(r *http.Request) string {
	return strings.TrimSpace(r.FormValue("q"))
}

// elapsedTime takes a date and returns returns human-readable,
// relative timestamps based on the following rules:
// (1) 'X hours ago' when X < 6
// (2) 'today' between 6 hours and 1 day ago
// (3) 'Y days ago' when Y < 6
// (4) A date formatted like "Jan 2, 2006" for anything further back
func elapsedTime(date time.Time) string {
	elapsedHours := int(time.Since(date).Hours())
	if elapsedHours == 1 {
		return "1 hour ago"
	} else if elapsedHours < 6 {
		return fmt.Sprintf("%d hours ago", elapsedHours)
	}

	elapsedDays := elapsedHours / 24
	if elapsedDays < 1 {
		return "today"
	} else if elapsedDays == 1 {
		return "1 day ago"
	} else if elapsedDays < 6 {
		return fmt.Sprintf("%d days ago", elapsedDays)
	}

	return date.Format("Jan _2, 2006")
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package initfunc

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"

	"github.com/issue9/logs/writers"
)

const (
	b int64 = 1 << (10 * iota)
	kb
	mb
	gb
)

// Rotate 的初始化函数。
func Rotate(args map[string]string) (io.Writer, error) {
	prefix, found := args["prefix"]
	if !found {
		prefix = ""
	}

	dir, found := args["dir"]
	if !found {
		return nil, argNotFoundErr("rotate", "dir")
	}

	sizeStr, found := args["size"]
	if !found {
		return nil, argNotFoundErr("rotate", "size")
	}

	size, err := toByte(sizeStr)
	if err != nil {
		return nil, err
	}

	return writers.NewRotate(prefix, dir, int(size))
}

// 将字符串转换成以字节为单位的数值。
// 粗略计算，并不 100% 正确，小数只取整数部分。
// 支持以下格式：
//  1024
//  1k
//  1M
//  1G
// 后缀单位只支持 k,g,m，不区分大小写。
func toByte(str string) (int64, error) {
	if len(str) == 0 {
		return -1, errors.New("不能传递空值")
	}

	str = strings.ToLower(str)

	scale := b
	unit := str[len(str)-1]
	switch {
	case unit >= '0' && unit <= '9':
		scale = b
	case unit == 'b':
		scale = b
	case unit == 'k':
		scale = kb
	case unit == 'm':
		scale = mb
	case unit == 'g':
		scale = gb
	default:
		return -1, fmt.Errorf("无法识别的单位:[%v]", unit)
	}

	if scale > 1 {
		str = str[:len(str)-1]
	}

	if len(str) == 0 {
		return -1, errors.New("传递了一个空值")
	}

	size, err := strconv.ParseFloat(str, 32)
	if err != nil {
		return -1, err
	}

	if size <= 0 {
		return -1, fmt.Errorf("大小不能小于 0，当前值为:[%d]", size)
	}

	return int64(size) * scale, nil
}
<commit_msg>修正测试错误<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package initfunc

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"

	"github.com/issue9/logs/writers"
)

const (
	b int64 = 1 << (10 * iota)
	kb
	mb
	gb
)

// Rotate 的初始化函数。
func Rotate(args map[string]string) (io.Writer, error) {
	prefix, found := args["prefix"]
	if !found {
		prefix = ""
	}

	dir, found := args["dir"]
	if !found {
		return nil, argNotFoundErr("rotate", "dir")
	}

	sizeStr, found := args["size"]
	if !found {
		return nil, argNotFoundErr("rotate", "size")
	}

	size, err := toByte(sizeStr)
	if err != nil {
		return nil, err
	}

	return writers.NewRotate(prefix, dir, int(size))
}

// 将字符串转换成以字节为单位的数值。
// 粗略计算，并不 100% 正确，小数只取整数部分。
// 支持以下格式：
//  1024
//  1k
//  1M
//  1G
// 后缀单位只支持 k,g,m，不区分大小写。
func toByte(str string) (int64, error) {
	if len(str) == 0 {
		return -1, errors.New("不能传递空值")
	}

	str = strings.ToLower(str)

	scale := b
	unit := str[len(str)-1]
	switch {
	case unit >= '0' && unit <= '9':
		scale = b
	case unit == 'b':
		scale = b
	case unit == 'k':
		scale = kb
	case unit == 'm':
		scale = mb
	case unit == 'g':
		scale = gb
	default:
		return -1, fmt.Errorf("无法识别的单位:[%v]", unit)
	}

	if scale > 1 {
		str = str[:len(str)-1]
	}

	if len(str) == 0 {
		return -1, errors.New("传递了一个空值")
	}

	size, err := strconv.ParseFloat(str, 32)
	if err != nil {
		return -1, err
	}

	if size <= 0 {
		return -1, fmt.Errorf("大小不能小于 0，当前值为:[%.4f]", size)
	}

	return int64(size) * scale, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 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 cmd

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"sort"

	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/tool"
)

// symbols implements the symbols verb for gopls
type symbols struct {
	app *Application
}

func (r *symbols) Name() string      { return "symbols" }
func (r *symbols) Usage() string     { return "<file>" }
func (r *symbols) ShortHelp() string { return "display selected file's symbols" }
func (r *symbols) DetailedHelp(f *flag.FlagSet) {
	fmt.Fprint(f.Output(), `
Example:
  $ gopls symbols helper/helper.go
`)
	f.PrintDefaults()
}
func (r *symbols) Run(ctx context.Context, args ...string) error {
	if len(args) != 1 {
		return tool.CommandLineErrorf("symbols expects 1 argument (position)")
	}

	conn, err := r.app.connect(ctx)
	if err != nil {
		return err
	}
	defer conn.terminate(ctx)

	from := span.Parse(args[0])
	p := protocol.DocumentSymbolParams{
		TextDocument: protocol.TextDocumentIdentifier{
			URI: protocol.URIFromSpanURI(from.URI()),
		},
	}
	symbols, err := conn.DocumentSymbol(ctx, &p)
	if err != nil {
		return err
	}
	for _, s := range symbols {
		s, ok := s.(map[string]interface{})
		if !ok {
			continue
		}
		bytes, err := json.Marshal(s)
		if err != nil {
			return err
		}
		if _, ok := s["selectionRange"]; ok {
			if err := parseDocumentSymbol(bytes); err != nil {
				return err
			}
			continue
		}
		if err := parseSymbolInformation(bytes); err != nil {
			return err
		}
	}
	return nil
}

func parseDocumentSymbol(bytes []byte) error {
	var s protocol.DocumentSymbol
	if err := json.Unmarshal(bytes, &s); err != nil {
		return err
	}
	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.SelectionRange))
	// Sort children for consistency
	sort.Slice(s.Children, func(i, j int) bool {
		return s.Children[i].Name < s.Children[j].Name
	})
	for _, c := range s.Children {
		fmt.Printf("\t%s %s %s\n", c.Name, c.Kind, positionToString(c.SelectionRange))
	}
	return nil
}

func parseSymbolInformation(bytes []byte) error {
	var s protocol.SymbolInformation
	if err := json.Unmarshal(bytes, &s); err != nil {
		return err
	}
	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.Location.Range))
	return nil
}

func positionToString(r protocol.Range) string {
	return fmt.Sprintf("%v:%v-%v:%v",
		r.Start.Line+1,
		r.Start.Character+1,
		r.End.Line+1,
		r.End.Character+1,
	)
}
<commit_msg>internal/lsp/cmd: fix not displaying symbols result<commit_after>// Copyright 2019 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 cmd

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"sort"

	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/tool"
)

// symbols implements the symbols verb for gopls
type symbols struct {
	app *Application
}

func (r *symbols) Name() string      { return "symbols" }
func (r *symbols) Usage() string     { return "<file>" }
func (r *symbols) ShortHelp() string { return "display selected file's symbols" }
func (r *symbols) DetailedHelp(f *flag.FlagSet) {
	fmt.Fprint(f.Output(), `
Example:
  $ gopls symbols helper/helper.go
`)
	f.PrintDefaults()
}
func (r *symbols) Run(ctx context.Context, args ...string) error {
	if len(args) != 1 {
		return tool.CommandLineErrorf("symbols expects 1 argument (position)")
	}

	conn, err := r.app.connect(ctx)
	if err != nil {
		return err
	}
	defer conn.terminate(ctx)

	from := span.Parse(args[0])
	p := protocol.DocumentSymbolParams{
		TextDocument: protocol.TextDocumentIdentifier{
			URI: protocol.URIFromSpanURI(from.URI()),
		},
	}
	symbols, err := conn.DocumentSymbol(ctx, &p)
	if err != nil {
		return err
	}
	for _, s := range symbols {
		if m, ok := s.(map[string]interface{}); ok {
			s, err = mapToSymbol(m)
			if err != nil {
				return err
			}
		}
		switch t := s.(type) {
		case protocol.DocumentSymbol:
			printDocumentSymbol(t)
		case protocol.SymbolInformation:
			printSymbolInformation(t)
		}
	}
	return nil
}

func mapToSymbol(m map[string]interface{}) (interface{}, error) {
	b, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}

	if _, ok := m["selectionRange"]; ok {
		var s protocol.DocumentSymbol
		if err := json.Unmarshal(b, &s); err != nil {
			return nil, err
		}
		return s, nil
	}

	var s protocol.SymbolInformation
	if err := json.Unmarshal(b, &s); err != nil {
		return nil, err
	}
	return s, nil
}

func printDocumentSymbol(s protocol.DocumentSymbol) {
	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.SelectionRange))
	// Sort children for consistency
	sort.Slice(s.Children, func(i, j int) bool {
		return s.Children[i].Name < s.Children[j].Name
	})
	for _, c := range s.Children {
		fmt.Printf("\t%s %s %s\n", c.Name, c.Kind, positionToString(c.SelectionRange))
	}
}

func printSymbolInformation(s protocol.SymbolInformation) {
	fmt.Printf("%s %s %s\n", s.Name, s.Kind, positionToString(s.Location.Range))
}

func positionToString(r protocol.Range) string {
	return fmt.Sprintf("%v:%v-%v:%v",
		r.Start.Line+1,
		r.Start.Character+1,
		r.End.Line+1,
		r.End.Character+1,
	)
}
<|endoftext|>