<commit_before>// 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 daead

import (
	"fmt"

	"github.com/google/tink/go/format"
	"github.com/google/tink/go/tink"
)

// New returns a DeterministicAEAD primitive from the given keyset handle.
func New(handle *tink.KeysetHandle) (tink.DeterministicAEAD, error) {
	return newWithKeyManager(handle, nil /*keyManager*/)
}

// newWithKeyManager returns a DeterministicAEAD primitive from the given keyset handle and custom key manager.
func newWithKeyManager(kh *tink.KeysetHandle, km tink.KeyManager) (tink.DeterministicAEAD, error) {
	ps, err := tink.PrimitivesWithKeyManager(kh, km)
	if err != nil {
		return nil, fmt.Errorf("daead_factory: cannot obtain primitive set: %s", err)
	}
	ret := new(primitiveSet)
	ret.ps = ps
	return tink.DeterministicAEAD(ret), nil
}

// primitiveSet is an DeterministicAEAD implementation that uses the underlying primitive set
// for deterministic encryption and decryption.
type primitiveSet struct {
	ps *tink.PrimitiveSet
}

// Asserts that primitiveSet implements the DeterministicAEAD interface.
var _ tink.DeterministicAEAD = (*primitiveSet)(nil)

// EncryptDeterministically deterministically encrypts plaintext with additionalData as additional authenticated data.
// It returns the concatenation of the primary's identifier and the ciphertext.
func (d *primitiveSet) EncryptDeterministically(pt, aad []byte) ([]byte, error) {
	primary := d.ps.Primary
	p := (primary.Primitive).(tink.DeterministicAEAD)
	ct, err := p.EncryptDeterministically(pt, aad)
	if err != nil {
		return nil, err
	}

	var ret []byte
	ret = append(ret, primary.Prefix...)
	ret = append(ret, ct...)
	return ret, nil
}

// DecryptDeterministically deterministically decrypts ciphertext with additionalData as
// additional authenticated data. It returns the corresponding plaintext if the
// ciphertext is authenticated.
func (d *primitiveSet) DecryptDeterministically(ct, aad []byte) ([]byte, error) {
	// try non-raw keys
	prefixSize := format.NonRawPrefixSize
	if len(ct) > prefixSize {
		prefix := ct[:prefixSize]
		ctNoPrefix := ct[prefixSize:]
		entries, err := d.ps.EntriesForPrefix(string(prefix))
		if err == nil {
			for i := 0; i < len(entries); i++ {
				p := (entries[i].Primitive).(tink.DeterministicAEAD)
				pt, err := p.DecryptDeterministically(ctNoPrefix, aad)
				if err == nil {
					return pt, nil
				}
			}
		}
	}
	// try raw keys
	entries, err := d.ps.RawEntries()
	if err == nil {
		for i := 0; i < len(entries); i++ {
			p := (entries[i].Primitive).(tink.DeterministicAEAD)
			pt, err := p.DecryptDeterministically(ct, aad)
			if err == nil {
				return pt, nil
			}
		}
	}
	// nothing worked
	return nil, fmt.Errorf("daead_factory: decryption failed")
}
<commit_msg>Golang: make daead.NewWithKeyManager public<commit_after>// 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 daead

import (
	"fmt"

	"github.com/google/tink/go/format"
	"github.com/google/tink/go/tink"
)

// New returns a DeterministicAEAD primitive from the given keyset handle.
func New(handle *tink.KeysetHandle) (tink.DeterministicAEAD, error) {
	return NewWithKeyManager(handle, nil /*keyManager*/)
}

// NewWithKeyManager returns a DeterministicAEAD primitive from the given keyset handle and custom key manager.
func NewWithKeyManager(kh *tink.KeysetHandle, km tink.KeyManager) (tink.DeterministicAEAD, error) {
	ps, err := tink.PrimitivesWithKeyManager(kh, km)
	if err != nil {
		return nil, fmt.Errorf("daead_factory: cannot obtain primitive set: %s", err)
	}
	ret := new(primitiveSet)
	ret.ps = ps
	return tink.DeterministicAEAD(ret), nil
}

// primitiveSet is an DeterministicAEAD implementation that uses the underlying primitive set
// for deterministic encryption and decryption.
type primitiveSet struct {
	ps *tink.PrimitiveSet
}

// Asserts that primitiveSet implements the DeterministicAEAD interface.
var _ tink.DeterministicAEAD = (*primitiveSet)(nil)

// EncryptDeterministically deterministically encrypts plaintext with additionalData as additional authenticated data.
// It returns the concatenation of the primary's identifier and the ciphertext.
func (d *primitiveSet) EncryptDeterministically(pt, aad []byte) ([]byte, error) {
	primary := d.ps.Primary
	p := (primary.Primitive).(tink.DeterministicAEAD)
	ct, err := p.EncryptDeterministically(pt, aad)
	if err != nil {
		return nil, err
	}

	var ret []byte
	ret = append(ret, primary.Prefix...)
	ret = append(ret, ct...)
	return ret, nil
}

// DecryptDeterministically deterministically decrypts ciphertext with additionalData as
// additional authenticated data. It returns the corresponding plaintext if the
// ciphertext is authenticated.
func (d *primitiveSet) DecryptDeterministically(ct, aad []byte) ([]byte, error) {
	// try non-raw keys
	prefixSize := format.NonRawPrefixSize
	if len(ct) > prefixSize {
		prefix := ct[:prefixSize]
		ctNoPrefix := ct[prefixSize:]
		entries, err := d.ps.EntriesForPrefix(string(prefix))
		if err == nil {
			for i := 0; i < len(entries); i++ {
				p := (entries[i].Primitive).(tink.DeterministicAEAD)
				pt, err := p.DecryptDeterministically(ctNoPrefix, aad)
				if err == nil {
					return pt, nil
				}
			}
		}
	}
	// try raw keys
	entries, err := d.ps.RawEntries()
	if err == nil {
		for i := 0; i < len(entries); i++ {
			p := (entries[i].Primitive).(tink.DeterministicAEAD)
			pt, err := p.DecryptDeterministically(ct, aad)
			if err == nil {
				return pt, nil
			}
		}
	}
	// nothing worked
	return nil, fmt.Errorf("daead_factory: decryption failed")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"logs_direct", // name
		"direct",      // type
		true,          // durable
		false,         // auto-deleted
		false,         // internal
		false,         // no-wait
		nil,           // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	for _, s := range os.Args {
		log.Printf("Binding queue %s to exchange %s with routing key %s", q.Name, "logs_direct", s)
		err = ch.QueueBind(
			q.Name,        // queue name
			s,             // routing key
			"logs_direct", // exchange
			false,
			nil)
		failOnError(err, "Failed to bind a queue")
	}

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto ack
		false,  // exclusive
		false,  // no local
		false,  // no wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {
		for d := range msgs {
			log.Printf(" [x] %s", d.Body)
		}
	}()

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	<-forever
}
<commit_msg>Added check to verify that sufficient args are supplied. Corrected the range to skip Args[0] (which is the command itself)<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"logs_direct", // name
		"direct",      // type
		true,          // durable
		false,         // auto-deleted
		false,         // internal
		false,         // no-wait
		nil,           // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	if len(os.Args) < 2 {
		log.Printf("Usage: %s [info] [warning] [error]", os.Args[0])
		os.Exit(0)
	}
	for _, s := range os.Args[1:] {
		log.Printf("Binding queue %s to exchange %s with routing key %s", q.Name, "logs_direct", s)
		err = ch.QueueBind(
			q.Name,        // queue name
			s,             // routing key
			"logs_direct", // exchange
			false,
			nil)
		failOnError(err, "Failed to bind a queue")
	}

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto ack
		false,  // exclusive
		false,  // no local
		false,  // no wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {
		for d := range msgs {
			log.Printf(" [x] %s", d.Body)
		}
	}()

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	<-forever
}
<|endoftext|>


<commit_before>package space

// Planet names
type Planet string

const secondsInEarthYear = 31557600

var orbitalPeriods = map[Planet]float64{
	"Mercury": 0.2408467,
	"Venus":   0.61519726,
	"Mars":    1.8808158,
	"Jupiter": 11.862615,
	"Saturn":  29.447498,
	"Uranus":  84.016846,
	"Neptune": 164.79132,
}

// Age returns the age in years of someone on planet given their age in seconds.
func Age(ageInSeconds float64, planet Planet) float64 {
	if planet == "Earth" {
		return ageOnEarth(ageInSeconds)
	}
	return ageOnEarth(ageInSeconds) / orbitalPeriods[planet]
}

// AgeOnEarth returns the age of somone on Earth given their age in seconds.
func ageOnEarth(ageInSeconds float64) float64 {
	return ageInSeconds / secondsInEarthYear
}
<commit_msg>Remove special case for Earth<commit_after>package space

// Planet names
type Planet string

const secondsInEarthYear = 31557600

var orbitalPeriods = map[Planet]float64{
	"Mercury": 0.2408467,
	"Venus":   0.61519726,
	"Earth":   1,
	"Mars":    1.8808158,
	"Jupiter": 11.862615,
	"Saturn":  29.447498,
	"Uranus":  84.016846,
	"Neptune": 164.79132,
}

// Age returns the age in years of someone on planet given their age in seconds.
func Age(ageInSeconds float64, planet Planet) float64 {
	return ageOnEarth(ageInSeconds) / orbitalPeriods[planet]
}

// AgeOnEarth returns the age of somone on Earth given their age in seconds.
func ageOnEarth(ageInSeconds float64) float64 {
	return ageInSeconds / secondsInEarthYear
}
<|endoftext|>


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

package argo

import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
	"sqlflow.org/sqlflow/go/log"
	pb "sqlflow.org/sqlflow/go/proto"
	wfrsp "sqlflow.org/sqlflow/go/workflow/response"
)

func getStepIdx(wf *v1alpha1.Workflow, targetStepGroup string) (int, error) {
	stepIdx := 1
	stepGroupName, e := getFirstStepGroup(wf, wf.ObjectMeta.Name)
	if e != nil {
		return -1, e
	}
	for {
		if stepGroupName == targetStepGroup {
			return stepIdx, nil
		}
		stepGroupName, e = getNextStepGroup(wf, stepGroupName)
		stepIdx++
		if e != nil {
			return -1, e
		}
	}
}

func logViewURL(ns, wfID, podID string) (string, error) {
	// if we are using DTM log collection, construct a different url pattern
	dtmEp := os.Getenv("SQLFLOW_WORKFLOW_LOGVIEW_DTM_ENDPOINT")
	if dtmEp != "" {
		return fmt.Sprintf("%s?jobName=%s&taskName=%s&__envName=PROD", dtmEp, wfID, podID), nil
	}
	// argo UI log view panel
	ep := os.Getenv("SQLFLOW_WORKFLOW_LOGVIEW_ENDPOINT")
	return fmt.Sprintf("%s/workflows/%s/%s?tab=workflow&nodeId=%s&sidePanel=logs:%s:main", ep, ns, wfID, podID, podID), nil
}

// Fetch fetches the workflow log and status,
// design doc: https://github.com/sql-machine-learning/sqlflow/blob/develop/doc/design/argo_workflow_on_sqlflow.md
//
// An example of Fetched Responses from the server logs:
// Step [1/3] Execute Code: echo hello1
// Step [1/3] Log: http://localhost:8001/workflows/default/steps-bdpff?nodeId=steps-bdpff-xx1
// Step [1/3] Status: Pending
// Step [1/3] Status: Running
// Step [1/3] Status: Succeed/Failed
// Step [2/3] Execute Code: echo hello2
// Step [2/3] Log: http://localhost:8001/workflows/default/steps-bdpff?nodeId=steps-bdpff-xx2
// ...
func (w *Workflow) Fetch(req *pb.FetchRequest) (*pb.FetchResponse, error) {
	logger := log.WithFields(log.Fields{
		"requestID": log.UUID(),
		"jobID":     req.Job.Id,
		"stepID":    req.StepId,
		"event":     "fetch",
	})

	wf, err := k8sReadWorkflow(req.Job.Id)
	if err != nil {
		logger.Errorf("workflowFailed/k8sRead, error: %v", err)
		return nil, err
	}
	logger.Infof("phase:%s", wf.Status.Phase)

	if isWorkflowPending(wf) {
		return wfrsp.New().Response(req.Job.Id, "", "", false), nil
	}
	stepGroupName, err := getStepGroup(wf, req.Job.Id, req.StepId)
	if err != nil {
		return nil, err
	}

	stepCnt := len(wf.Spec.Templates[0].Steps)
	stepIdx, err := getStepIdx(wf, stepGroupName)
	if err != nil {
		return nil, err
	}
	logPrefix := fmt.Sprintf("SQLFlow Step: [%d/%d]", stepIdx, stepCnt)

	pod, err := getPodByStepGroup(wf, stepGroupName)
	if err != nil {
		return nil, err
	}
	eof := false // true if finish fetching the workflow logs
	r := wfrsp.New()
	newStepPhase := req.StepPhase
	logURL, e := logViewURL(wf.ObjectMeta.Namespace, wf.ObjectMeta.Name, pod.ObjectMeta.Name)
	if e != nil {
		return nil, e
	}

	if req.StepPhase == "" {
		// the 1st container execute `argoexec wait` to wait the priority step, so package the 2nd container's command code.
		execCode := fmt.Sprintf("%s %s", strings.Join(pod.Spec.Containers[1].Command, " "), strings.Join(pod.Spec.Containers[1].Args, " "))
		r.AppendMessage(fmt.Sprintf("%s Execute Code: %s", logPrefix, execCode))
		r.AppendMessage(fmt.Sprintf("%s Log: %s", logPrefix, logURL))
	}

	// note(yancey1989): record the Pod phase to avoid output the duplicated logs at the next fetch request.
	if req.StepPhase != string(pod.Status.Phase) {
		r.AppendMessage(fmt.Sprintf("%s Status: %s", logPrefix, pod.Status.Phase))
		newStepPhase = string(pod.Status.Phase)
	}

	if isPodCompleted(pod) {
		// snip the pod logs when it complete
		// TODO(yancey1989): fetch the pod logs using an iteration way
		// to avoid the memory overflow
		podLogs, e := k8sReadPodLogs(pod.ObjectMeta.Name, "main", "", false)
		if e != nil {
			return nil, e
		}

		if e := r.AppendProtoMessages(podLogs); e != nil {
			return nil, e
		}

		// TODO(yancey1989): add duration time for the eoeResponse
		// eoe just used to simplify the client code which can be consistent with non-argo mode.
		if isPodFailed(pod) {
			e = fmt.Errorf("%s Failed, Log: %s\n%s", logPrefix, logURL, r.ErrorMessage())
			logger.Errorf("workflowFailed, %v, spent:%d", e, time.Now().Second()-wf.CreationTimestamp.Second())
			return r.ResponseWithStepComplete(req.Job.Id, "", newStepPhase, eof), e
		}
		logger.Infof("workflowSucceed, spent:%d", time.Now().Second()-wf.CreationTimestamp.Second())

		// move to the next step
		nextStepGroup, err := getNextStepGroup(wf, stepGroupName)
		if err != nil {
			return nil, err
		}
		// set the EOF to true if no next step in the workflow
		if nextStepGroup == "" && stepIdx == stepCnt {
			eof = true
		}

		if nextStepGroup != "" {
			newStepPhase = ""
			stepGroupName = nextStepGroup
		}
		return r.ResponseWithStepComplete(req.Job.Id, stepGroupName, newStepPhase, eof), nil
	}
	return r.Response(req.Job.Id, stepGroupName, newStepPhase, eof), nil
}

func parseOffset(content string) (string, string, error) {
	reTimestamps := regexp.MustCompile(`([^\s]+)\s(.*)$`)
	msg := reTimestamps.FindStringSubmatch(content)
	if len(msg) != 3 {
		return "", "", fmt.Errorf("Parse offset failed: %s", content)
	}
	return msg[1], msg[2], nil
}

func getOffsetAndContentFromLogs(logs []string, oldOffset string) ([]string, string, error) {
	// NOTE(yancey1989): using `kubectl --since-time <offset>` to get logs
	// from the offset which is the timestamps. the accuracy can only be achieved at the second level,
	// `kubectl` may return some duplicated logs as the previous fetch, and we need to skip them.
	buffer := []string{}
	skipOlderLogs := false
	offset := oldOffset
	for _, msg := range logs {
		newOffset, content, e := parseOffset(msg)
		if e != nil {
			break
		}
		if newOffset == oldOffset {
			skipOlderLogs = true
		} else {
			if skipOlderLogs || oldOffset == "" {
				buffer = append(buffer, content)
				offset = newOffset
			} else {
				// skip the duplicated logs as the previous fetch
				continue
			}
		}
	}
	return buffer, offset, nil
}

func getPodLogs(podName string, offset string) ([]string, string, error) {
	// NOTE(tony): A workflow pod usually contains two container: main and wait
	// I believe wait is used for management by Argo, so we only need to care about main.
	logs, err := k8sReadPodLogs(podName, "main", offset, true)
	if err != nil {
		return nil, "", err
	}

	return getOffsetAndContentFromLogs(logs, offset)
}
<commit_msg>Collect step log (#2943)<commit_after>// Copyright 2020 The SQLFlow Authors. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package argo

import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
	"sqlflow.org/sqlflow/go/log"
	pb "sqlflow.org/sqlflow/go/proto"
	wfrsp "sqlflow.org/sqlflow/go/workflow/response"
)

func getStepIdx(wf *v1alpha1.Workflow, targetStepGroup string) (int, error) {
	stepIdx := 1
	stepGroupName, e := getFirstStepGroup(wf, wf.ObjectMeta.Name)
	if e != nil {
		return -1, e
	}
	for {
		if stepGroupName == targetStepGroup {
			return stepIdx, nil
		}
		stepGroupName, e = getNextStepGroup(wf, stepGroupName)
		stepIdx++
		if e != nil {
			return -1, e
		}
	}
}

func logViewURL(ns, wfID, podID string) (string, error) {
	// if we are using DTM log collection, construct a different url pattern
	dtmEp := os.Getenv("SQLFLOW_WORKFLOW_LOGVIEW_DTM_ENDPOINT")
	if dtmEp != "" {
		// Set time interval to 24 hour for the first page, users can adjust
		// this time on logview page
		startTime, endTime := time.Now().Unix()*1000, time.Now().Add(24*time.Hour).Unix()*1000
		return fmt.Sprintf("%s?jobName=%s&taskName=%s&__envName=PROD&startTime=%d&endTime=%d",
			dtmEp, wfID, podID, startTime, endTime), nil
	}
	// argo UI log view panel
	ep := os.Getenv("SQLFLOW_WORKFLOW_LOGVIEW_ENDPOINT")
	return fmt.Sprintf("%s/workflows/%s/%s?tab=workflow&nodeId=%s&sidePanel=logs:%s:main", ep, ns, wfID, podID, podID), nil
}

// Fetch fetches the workflow log and status,
// design doc: https://github.com/sql-machine-learning/sqlflow/blob/develop/doc/design/argo_workflow_on_sqlflow.md
//
// An example of Fetched Responses from the server logs:
// Step [1/3] Execute Code: echo hello1
// Step [1/3] Log: http://localhost:8001/workflows/default/steps-bdpff?nodeId=steps-bdpff-xx1
// Step [1/3] Status: Pending
// Step [1/3] Status: Running
// Step [1/3] Status: Succeed/Failed
// Step [2/3] Execute Code: echo hello2
// Step [2/3] Log: http://localhost:8001/workflows/default/steps-bdpff?nodeId=steps-bdpff-xx2
// ...
func (w *Workflow) Fetch(req *pb.FetchRequest) (*pb.FetchResponse, error) {
	logger := log.WithFields(log.Fields{
		"requestID": log.UUID(),
		"jobID":     req.Job.Id,
		"stepID":    req.StepId,
		"event":     "fetch",
	})

	wf, err := k8sReadWorkflow(req.Job.Id)
	if err != nil {
		logger.Errorf("workflowFailed/k8sRead, error: %v", err)
		return nil, err
	}
	logger.Infof("phase:%s", wf.Status.Phase)

	if isWorkflowPending(wf) {
		return wfrsp.New().Response(req.Job.Id, "", "", false), nil
	}
	stepGroupName, err := getStepGroup(wf, req.Job.Id, req.StepId)
	if err != nil {
		return nil, err
	}

	stepCnt := len(wf.Spec.Templates[0].Steps)
	stepIdx, err := getStepIdx(wf, stepGroupName)
	if err != nil {
		return nil, err
	}
	logPrefix := fmt.Sprintf("SQLFlow Step: [%d/%d]", stepIdx, stepCnt)

	pod, err := getPodByStepGroup(wf, stepGroupName)
	if err != nil {
		return nil, err
	}
	eof := false // true if finish fetching the workflow logs
	r := wfrsp.New()
	newStepPhase := req.StepPhase
	logURL, e := logViewURL(wf.ObjectMeta.Namespace, wf.ObjectMeta.Name, pod.ObjectMeta.Name)
	if e != nil {
		return nil, e
	}

	if req.StepPhase == "" {
		// the 1st container execute `argoexec wait` to wait the priority step, so package the 2nd container's command code.
		execCode := fmt.Sprintf("%s %s", strings.Join(pod.Spec.Containers[1].Command, " "), strings.Join(pod.Spec.Containers[1].Args, " "))
		r.AppendMessage(fmt.Sprintf("%s Execute Code: %s", logPrefix, execCode))
		r.AppendMessage(fmt.Sprintf("%s Log: %s", logPrefix, logURL))
	}

	// note(yancey1989): record the Pod phase to avoid output the duplicated logs at the next fetch request.
	if req.StepPhase != string(pod.Status.Phase) {
		r.AppendMessage(fmt.Sprintf("%s Status: %s", logPrefix, pod.Status.Phase))
		newStepPhase = string(pod.Status.Phase)
	}

	if isPodCompleted(pod) {
		// snip the pod logs when it complete
		// TODO(yancey1989): fetch the pod logs using an iteration way
		// to avoid the memory overflow
		podLogs, e := k8sReadPodLogs(pod.ObjectMeta.Name, "main", "", false)
		if e != nil {
			return nil, e
		}

		if e := r.AppendProtoMessages(podLogs); e != nil {
			return nil, e
		}

		// TODO(yancey1989): add duration time for the eoeResponse
		// eoe just used to simplify the client code which can be consistent with non-argo mode.
		if isPodFailed(pod) {
			e = fmt.Errorf("%s Failed, Log: %s\n%s", logPrefix, logURL, r.ErrorMessage())
			logger.Errorf("workflowFailed, %v, spent:%d", e, time.Now().Second()-wf.CreationTimestamp.Second())
			return r.ResponseWithStepComplete(req.Job.Id, "", newStepPhase, eof), e
		}
		logger.Infof("workflowSucceed, spent:%d", time.Now().Second()-wf.CreationTimestamp.Second())

		// move to the next step
		nextStepGroup, err := getNextStepGroup(wf, stepGroupName)
		if err != nil {
			return nil, err
		}
		// set the EOF to true if no next step in the workflow
		if nextStepGroup == "" && stepIdx == stepCnt {
			eof = true
		}

		if nextStepGroup != "" {
			newStepPhase = ""
			stepGroupName = nextStepGroup
		}
		return r.ResponseWithStepComplete(req.Job.Id, stepGroupName, newStepPhase, eof), nil
	}
	return r.Response(req.Job.Id, stepGroupName, newStepPhase, eof), nil
}

func parseOffset(content string) (string, string, error) {
	reTimestamps := regexp.MustCompile(`([^\s]+)\s(.*)$`)
	msg := reTimestamps.FindStringSubmatch(content)
	if len(msg) != 3 {
		return "", "", fmt.Errorf("Parse offset failed: %s", content)
	}
	return msg[1], msg[2], nil
}

func getOffsetAndContentFromLogs(logs []string, oldOffset string) ([]string, string, error) {
	// NOTE(yancey1989): using `kubectl --since-time <offset>` to get logs
	// from the offset which is the timestamps. the accuracy can only be achieved at the second level,
	// `kubectl` may return some duplicated logs as the previous fetch, and we need to skip them.
	buffer := []string{}
	skipOlderLogs := false
	offset := oldOffset
	for _, msg := range logs {
		newOffset, content, e := parseOffset(msg)
		if e != nil {
			break
		}
		if newOffset == oldOffset {
			skipOlderLogs = true
		} else {
			if skipOlderLogs || oldOffset == "" {
				buffer = append(buffer, content)
				offset = newOffset
			} else {
				// skip the duplicated logs as the previous fetch
				continue
			}
		}
	}
	return buffer, offset, nil
}

func getPodLogs(podName string, offset string) ([]string, string, error) {
	// NOTE(tony): A workflow pod usually contains two container: main and wait
	// I believe wait is used for management by Argo, so we only need to care about main.
	logs, err := k8sReadPodLogs(podName, "main", offset, true)
	if err != nil {
		return nil, "", err
	}

	return getOffsetAndContentFromLogs(logs, offset)
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Álvaro Vilanova Vidal
// Use of this source code is governed by a BSD 3-Clause license that can be
// found in the LICENSE file.

package main

import (
	"flag"
	"fmt"

	"github.com/alvivi/go-dropbox/dropbox"
	"golang.org/x/oauth2"
)

var token = flag.String("token", "", "Dropbox OAuth2 bearer token")

func init() {
	flag.Parse()
}

func main() {
	if token == nil || *token == "" {
		flag.PrintDefaults()
		return
	}
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *token})
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	c := dropbox.NewClient(tc)

	account, _, err := c.Users.GetAccount()
	if err != nil {
		fmt.Printf("Error: %s\n", err.Error())
		return
	}

	fmt.Printf("ID: %s\n"+
		"Name:\n"+
		"\tGiven name: %s\n"+
		"\tSurname: %s\n"+
		"\tFamiliar name: %s\n"+
		"\tDisplay name: %s\n"+
		"Email: %s\n"+
		"Country: %s\n"+
		"Locale: %s\n"+
		"Referral link: %s\n"+
		"Is paired: %t\n", account.ID, account.Name.GivenName, account.Name.Surname,
		account.Name.FamiliarName, account.Name.DisplayName, account.Email,
		account.Country, account.Locale, account.ReferralLink, account.IsPaired)
}
<commit_msg>Added a comment to clarify the purpose of about_account example<commit_after>// Copyright (c) 2015, Álvaro Vilanova Vidal
// Use of this source code is governed by a BSD 3-Clause license that can be
// found in the LICENSE file.

// This example shows how to get current user account information.
package main

import (
	"flag"
	"fmt"

	"github.com/alvivi/go-dropbox/dropbox"
	"golang.org/x/oauth2"
)

var token = flag.String("token", "", "Dropbox OAuth2 bearer token")

func init() {
	flag.Parse()
}

func main() {
	if token == nil || *token == "" {
		flag.PrintDefaults()
		return
	}
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *token})
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	c := dropbox.NewClient(tc)

	account, _, err := c.Users.GetAccount()
	if err != nil {
		fmt.Printf("Error: %s\n", err.Error())
		return
	}
	fmt.Printf("ID: %s\n"+
		"Name:\n"+
		"\tGiven name: %s\n"+
		"\tSurname: %s\n"+
		"\tFamiliar name: %s\n"+
		"\tDisplay name: %s\n"+
		"Email: %s\n"+
		"Country: %s\n"+
		"Locale: %s\n"+
		"Referral link: %s\n"+
		"Is paired: %t\n", account.ID, account.Name.GivenName, account.Name.Surname,
		account.Name.FamiliarName, account.Name.DisplayName, account.Email,
		account.Country, account.Locale, account.ReferralLink, account.IsPaired)
}
<|endoftext|>


<commit_before>package sslLabsClientSupport

import (
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/mozilla/scribe"
	"github.com/mozilla/tls-observatory/connection"
	"github.com/mozilla/tls-observatory/constants"
	"github.com/mozilla/tls-observatory/logger"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName           = "sslLabsClientSupport"
	workerDesc           = "Determines client compatibility with a given target based on server certificate and ciphersuites."
	sslLabsClientDataURL = "https://api.ssllabs.com/api/v3/getClients"
	log                  = logger.GetLogger()
)

func init() {
	runner := new(slabscrunner)
	cs, err := getConffromURL(sslLabsClientDataURL)
	if err != nil {
		log.Printf("Failed to initialize %s: %v", workerName, err)
		return
	}
	runner.Clients = cs
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

// getClientDataFrom retrieves the json containing the sslLabs client data
func getConffromURL(url string) (cs []Client, err error) {
	r, err := http.Get(url)
	if err != nil {
		return
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(&cs)
	return
}

type slabscrunner struct {
	Clients []Client
}

// Client is a definition of a TLS client with all the parameters it supports
type Client struct {
	ID                       int      `json:"id"`
	Name                     string   `json:"name"`
	Platform                 string   `json:"platform"`
	Version                  string   `json:"version"`
	HandshakeFormat          string   `json:"handshakeFormat"`
	LowestProtocol           int      `json:"lowestProtocol"`
	HighestProtocol          int      `json:"highestProtocol"`
	UserAgent                string   `json:"userAgent"`
	IsGrade0                 bool     `json:"isGrade0"`
	MaxDhBits                int      `json:"maxDhBits"`
	AbortsOnUnrecognizedName bool     `json:"abortsOnUnrecognizedName"`
	MaxRsaBits               int      `json:"maxRsaBits"`
	MinDhBits                int      `json:"minDhBits"`
	RequiresSha2             bool     `json:"requiresSha2"`
	MinRsaBits               int      `json:"minRsaBits"`
	MinEcdsaBits             int      `json:"minEcdsaBits"`
	SuiteIds                 []int    `json:"suiteIds"`
	SuiteNames               []string `json:"suiteNames"`
	SupportsSni              bool     `json:"supportsSni"`
	SupportsCompression      bool     `json:"supportsCompression"`
	SupportsStapling         bool     `json:"supportsStapling"`
	SupportsTickets          bool     `json:"supportsTickets"`
	SupportsRi               bool     `json:"supportsRi"`
	SignatureAlgorithms      []int    `json:"signatureAlgorithms"`
	EllipticCurves           []int    `json:"ellipticCurves"`
	SupportsNpn              bool     `json:"supportsNpn"`
	NpnProtocols             []string `json:"npnProtocols"`
	AlpnProtocols            []string `json:"alpnProtocols"`
}

// CipherSuite represent a ciphersuite generated and recognised by OpenSSL
type CipherSuite struct {
	IANAName   string     `json:"iana_name"`
	GnuTLSName string     `json:"gnutls_name"`
	NSSName    string     `json:"nss_name"`
	Proto      string     `json:"proto"`
	Kx         string     `json:"kx"`
	Au         string     `json:"au"`
	Enc        Encryption `json:"encryption"`
	Mac        string     `json:"mac"`
	Code       uint64     `json:"code"`
}

//Encryption represents the encryption aspects of a Ciphersuite
type Encryption struct {
	Cipher string `json:"cipher"`
	Bits   int    `json:"key"`
}

type ClientSupport struct {
	Name            string `json:"name"`
	Version         string `json:"version"`
	Platform        string `json:"platform"`
	IsSupported     bool   `json:"is_supported"`
	Ciphersuite     string `json:"ciphersuite,omitempty"`
	CiphersuiteCode uint64 `json:"ciphersuite_code,omitempty"`
	Curve           string `json:"curve,omitempty"`
	CurveCode       uint64 `json:"curve_code"`
	Protocol        string `json:"protocol,omitempty"`
	ProtocolCode    int    `json:"protocol_code"`
}

type ClientsSupport []ClientSupport

func (slice ClientsSupport) Len() int {
	return len(slice)
}

func (slice ClientsSupport) Less(i, j int) bool {
	return slice[i].Name < slice[j].Name
}

func (slice ClientsSupport) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func (w slabscrunner) Run(in worker.Input, res chan worker.Result) {
	var clients ClientsSupport
	dedupClients := make(map[string]bool)
	// Loop over every client defined in the sslLabs document and check if they can
	// negotiate one of the ciphersuite measured on the server
	for _, client := range w.Clients {
		// if we already processed a client with this name, version and platform, skip it
		if _, ok := dedupClients[fmt.Sprintf("%s%s%s", client.Name, client.Version, client.Platform)]; ok {
			continue
		}
		for _, clientCiphersuite := range client.SuiteIds {
			for _, serverCiphersuite := range in.Connection.CipherSuite {
				serverCiphersuiteCode := constants.CipherSuites[serverCiphersuite.Cipher].Code
				if clientCiphersuite == int(serverCiphersuiteCode) {
					var (
						curve, protocol string
						curveCode       uint64
						protocolCode    int
					)
					// if the ciphersuite is DHE, verify that the client support the DH size
					if strings.HasPrefix(serverCiphersuite.Cipher, "DHE-") &&
						client.MaxDhBits > 0 &&
						!clientSupportsDHE(client, serverCiphersuite) {
						continue
					}
					// if the ciphersuite is ECDHE, verify that the client supports the curve
					if strings.HasPrefix(serverCiphersuite.Cipher, "ECDHE-") && len(client.EllipticCurves) > 0 {
						curve, curveCode = findClientCurve(client, serverCiphersuite)
						if curve == "" {
							continue
						}
					}
					protocol, protocolCode = findClientProtocol(client, serverCiphersuite)
					if protocol == "" {
						continue
					}
					// if we reached this point, the client is able to establish a connection
					// to the server. we flag it as supported and go to the next client.
					clients = append(clients, ClientSupport{
						Name:            client.Name,
						Version:         client.Version,
						Platform:        client.Platform,
						IsSupported:     true,
						Ciphersuite:     serverCiphersuite.Cipher,
						CiphersuiteCode: serverCiphersuite.Code,
						Curve:           curve,
						CurveCode:       curveCode,
						Protocol:        protocol,
						ProtocolCode:    protocolCode,
					})
					goto nextClient
				}
			}
		}
		// if we reach this point, it means no support was found for this client
		clients = append(clients, ClientSupport{
			Name:        client.Name,
			Version:     client.Version,
			IsSupported: false,
		})
	nextClient:
	}
	out, err := json.Marshal(clients)
	if err != nil {
		w.error(res, "Failed to marshal results: %v", err)
	}
	res <- worker.Result{
		Success:    true,
		WorkerName: workerName,
		Errors:     nil,
		Result:     out,
	}
}

func clientSupportsDHE(client Client, serverCiphersuite connection.Ciphersuite) bool {
	// extract the dhe bits from the pfs string
	split := strings.Split(serverCiphersuite.PFS, ",")
	if len(split) < 2 {
		return false
	}
	split = strings.Split(split[1], "b")
	if len(split) < 2 {
		return false
	}
	dhsize, err := strconv.Atoi(split[0])
	if err != nil {
		return false
	}
	if client.MaxDhBits < dhsize {
		return false
	}
	return true
}

func findClientCurve(client Client, serverCiphersuite connection.Ciphersuite) (string, uint64) {
	for _, code := range client.EllipticCurves {
		// convert curve code to name
		for _, curveRef := range constants.Curves {
			if int(curveRef.Code) == code {
				for _, serverCurves := range serverCiphersuite.Curves {
					if curveRef.Name == serverCurves || curveRef.OpenSSLName == serverCurves {
						return curveRef.Name, curveRef.Code
					}
				}
			}
		}
	}
	return "", 0
}

func findClientProtocol(client Client, serverCiphersuite connection.Ciphersuite) (string, int) {
	for _, serverProto := range serverCiphersuite.Protocols {
		var spcode int
		for _, proto := range constants.Protocols {
			if proto.OpenSSLName == serverProto {
				spcode = proto.Code
			}
		}
		if client.LowestProtocol <= spcode && client.HighestProtocol >= spcode {
			return serverProto, spcode
		}
	}
	return "", 0
}

func (w slabscrunner) error(res chan worker.Result, messageFormat string, args ...interface{}) {
	out, _ := json.Marshal(fmt.Sprintf(messageFormat, args...))
	res <- worker.Result{
		Success:    false,
		WorkerName: workerName,
		Result:     out,
	}
}

var number = regexp.MustCompile(`^[0-9]+$`)

func (w slabscrunner) AnalysisPrinter(r []byte, printAll interface{}) (results []string, err error) {
	var cs ClientsSupport
	err = json.Unmarshal(r, &cs)
	if err != nil {
		err = fmt.Errorf("SSLLabs Client Support: failed to parse results: %v", err)
		return
	}
	if printAll != nil && printAll.(bool) == true {
		results = append(results, "* SSLLabs Client Support: showing all clients compatibility")
	} else {
		results = append(results, "* SSLLabs Client Support: showing oldest known clients")
	}
	var productsSupport = make(map[string]ClientSupport)
	// sort the list of clients, it's nicer to display
	sort.Sort(cs)
	for _, client := range cs {
		// if we want all clients, store the result and go to next entry
		if printAll != nil && printAll.(bool) == true {
			result := fmt.Sprintf("  - %s %s", client.Name, client.Version)
			if client.Platform != "" {
				result += fmt.Sprintf(" (%s)", client.Platform)
			}
			if client.IsSupported {
				result += fmt.Sprintf(": yes, %s %s %s", client.Protocol, client.Ciphersuite, client.Curve)
			} else {
				result += fmt.Sprintf(": no")
			}
			results = append(results, result)
			continue
		}
		// Once we reach this point, we only want supported clients
		if !client.IsSupported {
			continue
		}
		// if we only want the oldest compatible client, some dark magic is required
		// to parse the version of each client, which varies in format, and figure out
		// the oldest
		if _, ok := productsSupport[client.Name]; !ok {
			// this is the first supported client of this name that we encounter,
			// no comparison needed, simply store it
			productsSupport[client.Name] = client
			continue
		}
		prevClient := productsSupport[client.Name]
		// compare the version of the previous and current clients,
		// if the current client is older, store it instead of the previous one
		isOlder, err := scribe.TestEvrCompare(scribe.EVROP_LESS_THAN, client.Version, prevClient.Version)
		if err != nil {
			log.Printf("Failed to compare version %s with version %s for client %s: %v",
				client.Version, prevClient.Version, client.Name, err)
		}
		if isOlder {
			productsSupport[client.Name] = client
		}
	}
	if printAll != nil && printAll.(bool) == true {
		// if we just want to print all clients, return here
		return
	}
	// if we only want the oldest client, build the list here
	var supportedClients []string
	for _, clientName := range []string{"Firefox", "Chrome", "Edge", "IE", "Safari", "Opera", "Android", "OpenSSL", "Java"} {
		client := productsSupport[clientName]
		result := fmt.Sprintf("%s %s", client.Name, client.Version)
		supportedClients = append(supportedClients, result)
	}
	results = append(results, fmt.Sprintf("  - %s", strings.Join(supportedClients, ", ")))
	return
}
<commit_msg>update reference to scribe EVROP_LESS_THAN which changed<commit_after>package sslLabsClientSupport

import (
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/mozilla/scribe"
	"github.com/mozilla/tls-observatory/connection"
	"github.com/mozilla/tls-observatory/constants"
	"github.com/mozilla/tls-observatory/logger"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName           = "sslLabsClientSupport"
	workerDesc           = "Determines client compatibility with a given target based on server certificate and ciphersuites."
	sslLabsClientDataURL = "https://api.ssllabs.com/api/v3/getClients"
	log                  = logger.GetLogger()
)

func init() {
	runner := new(slabscrunner)
	cs, err := getConffromURL(sslLabsClientDataURL)
	if err != nil {
		log.Printf("Failed to initialize %s: %v", workerName, err)
		return
	}
	runner.Clients = cs
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

// getClientDataFrom retrieves the json containing the sslLabs client data
func getConffromURL(url string) (cs []Client, err error) {
	r, err := http.Get(url)
	if err != nil {
		return
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(&cs)
	return
}

type slabscrunner struct {
	Clients []Client
}

// Client is a definition of a TLS client with all the parameters it supports
type Client struct {
	ID                       int      `json:"id"`
	Name                     string   `json:"name"`
	Platform                 string   `json:"platform"`
	Version                  string   `json:"version"`
	HandshakeFormat          string   `json:"handshakeFormat"`
	LowestProtocol           int      `json:"lowestProtocol"`
	HighestProtocol          int      `json:"highestProtocol"`
	UserAgent                string   `json:"userAgent"`
	IsGrade0                 bool     `json:"isGrade0"`
	MaxDhBits                int      `json:"maxDhBits"`
	AbortsOnUnrecognizedName bool     `json:"abortsOnUnrecognizedName"`
	MaxRsaBits               int      `json:"maxRsaBits"`
	MinDhBits                int      `json:"minDhBits"`
	RequiresSha2             bool     `json:"requiresSha2"`
	MinRsaBits               int      `json:"minRsaBits"`
	MinEcdsaBits             int      `json:"minEcdsaBits"`
	SuiteIds                 []int    `json:"suiteIds"`
	SuiteNames               []string `json:"suiteNames"`
	SupportsSni              bool     `json:"supportsSni"`
	SupportsCompression      bool     `json:"supportsCompression"`
	SupportsStapling         bool     `json:"supportsStapling"`
	SupportsTickets          bool     `json:"supportsTickets"`
	SupportsRi               bool     `json:"supportsRi"`
	SignatureAlgorithms      []int    `json:"signatureAlgorithms"`
	EllipticCurves           []int    `json:"ellipticCurves"`
	SupportsNpn              bool     `json:"supportsNpn"`
	NpnProtocols             []string `json:"npnProtocols"`
	AlpnProtocols            []string `json:"alpnProtocols"`
}

// CipherSuite represent a ciphersuite generated and recognised by OpenSSL
type CipherSuite struct {
	IANAName   string     `json:"iana_name"`
	GnuTLSName string     `json:"gnutls_name"`
	NSSName    string     `json:"nss_name"`
	Proto      string     `json:"proto"`
	Kx         string     `json:"kx"`
	Au         string     `json:"au"`
	Enc        Encryption `json:"encryption"`
	Mac        string     `json:"mac"`
	Code       uint64     `json:"code"`
}

//Encryption represents the encryption aspects of a Ciphersuite
type Encryption struct {
	Cipher string `json:"cipher"`
	Bits   int    `json:"key"`
}

type ClientSupport struct {
	Name            string `json:"name"`
	Version         string `json:"version"`
	Platform        string `json:"platform"`
	IsSupported     bool   `json:"is_supported"`
	Ciphersuite     string `json:"ciphersuite,omitempty"`
	CiphersuiteCode uint64 `json:"ciphersuite_code,omitempty"`
	Curve           string `json:"curve,omitempty"`
	CurveCode       uint64 `json:"curve_code"`
	Protocol        string `json:"protocol,omitempty"`
	ProtocolCode    int    `json:"protocol_code"`
}

type ClientsSupport []ClientSupport

func (slice ClientsSupport) Len() int {
	return len(slice)
}

func (slice ClientsSupport) Less(i, j int) bool {
	return slice[i].Name < slice[j].Name
}

func (slice ClientsSupport) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func (w slabscrunner) Run(in worker.Input, res chan worker.Result) {
	var clients ClientsSupport
	dedupClients := make(map[string]bool)
	// Loop over every client defined in the sslLabs document and check if they can
	// negotiate one of the ciphersuite measured on the server
	for _, client := range w.Clients {
		// if we already processed a client with this name, version and platform, skip it
		if _, ok := dedupClients[fmt.Sprintf("%s%s%s", client.Name, client.Version, client.Platform)]; ok {
			continue
		}
		for _, clientCiphersuite := range client.SuiteIds {
			for _, serverCiphersuite := range in.Connection.CipherSuite {
				serverCiphersuiteCode := constants.CipherSuites[serverCiphersuite.Cipher].Code
				if clientCiphersuite == int(serverCiphersuiteCode) {
					var (
						curve, protocol string
						curveCode       uint64
						protocolCode    int
					)
					// if the ciphersuite is DHE, verify that the client support the DH size
					if strings.HasPrefix(serverCiphersuite.Cipher, "DHE-") &&
						client.MaxDhBits > 0 &&
						!clientSupportsDHE(client, serverCiphersuite) {
						continue
					}
					// if the ciphersuite is ECDHE, verify that the client supports the curve
					if strings.HasPrefix(serverCiphersuite.Cipher, "ECDHE-") && len(client.EllipticCurves) > 0 {
						curve, curveCode = findClientCurve(client, serverCiphersuite)
						if curve == "" {
							continue
						}
					}
					protocol, protocolCode = findClientProtocol(client, serverCiphersuite)
					if protocol == "" {
						continue
					}
					// if we reached this point, the client is able to establish a connection
					// to the server. we flag it as supported and go to the next client.
					clients = append(clients, ClientSupport{
						Name:            client.Name,
						Version:         client.Version,
						Platform:        client.Platform,
						IsSupported:     true,
						Ciphersuite:     serverCiphersuite.Cipher,
						CiphersuiteCode: serverCiphersuite.Code,
						Curve:           curve,
						CurveCode:       curveCode,
						Protocol:        protocol,
						ProtocolCode:    protocolCode,
					})
					goto nextClient
				}
			}
		}
		// if we reach this point, it means no support was found for this client
		clients = append(clients, ClientSupport{
			Name:        client.Name,
			Version:     client.Version,
			IsSupported: false,
		})
	nextClient:
	}
	out, err := json.Marshal(clients)
	if err != nil {
		w.error(res, "Failed to marshal results: %v", err)
	}
	res <- worker.Result{
		Success:    true,
		WorkerName: workerName,
		Errors:     nil,
		Result:     out,
	}
}

func clientSupportsDHE(client Client, serverCiphersuite connection.Ciphersuite) bool {
	// extract the dhe bits from the pfs string
	split := strings.Split(serverCiphersuite.PFS, ",")
	if len(split) < 2 {
		return false
	}
	split = strings.Split(split[1], "b")
	if len(split) < 2 {
		return false
	}
	dhsize, err := strconv.Atoi(split[0])
	if err != nil {
		return false
	}
	if client.MaxDhBits < dhsize {
		return false
	}
	return true
}

func findClientCurve(client Client, serverCiphersuite connection.Ciphersuite) (string, uint64) {
	for _, code := range client.EllipticCurves {
		// convert curve code to name
		for _, curveRef := range constants.Curves {
			if int(curveRef.Code) == code {
				for _, serverCurves := range serverCiphersuite.Curves {
					if curveRef.Name == serverCurves || curveRef.OpenSSLName == serverCurves {
						return curveRef.Name, curveRef.Code
					}
				}
			}
		}
	}
	return "", 0
}

func findClientProtocol(client Client, serverCiphersuite connection.Ciphersuite) (string, int) {
	for _, serverProto := range serverCiphersuite.Protocols {
		var spcode int
		for _, proto := range constants.Protocols {
			if proto.OpenSSLName == serverProto {
				spcode = proto.Code
			}
		}
		if client.LowestProtocol <= spcode && client.HighestProtocol >= spcode {
			return serverProto, spcode
		}
	}
	return "", 0
}

func (w slabscrunner) error(res chan worker.Result, messageFormat string, args ...interface{}) {
	out, _ := json.Marshal(fmt.Sprintf(messageFormat, args...))
	res <- worker.Result{
		Success:    false,
		WorkerName: workerName,
		Result:     out,
	}
}

var number = regexp.MustCompile(`^[0-9]+$`)

func (w slabscrunner) AnalysisPrinter(r []byte, printAll interface{}) (results []string, err error) {
	var cs ClientsSupport
	err = json.Unmarshal(r, &cs)
	if err != nil {
		err = fmt.Errorf("SSLLabs Client Support: failed to parse results: %v", err)
		return
	}
	if printAll != nil && printAll.(bool) == true {
		results = append(results, "* SSLLabs Client Support: showing all clients compatibility")
	} else {
		results = append(results, "* SSLLabs Client Support: showing oldest known clients")
	}
	var productsSupport = make(map[string]ClientSupport)
	// sort the list of clients, it's nicer to display
	sort.Sort(cs)
	for _, client := range cs {
		// if we want all clients, store the result and go to next entry
		if printAll != nil && printAll.(bool) == true {
			result := fmt.Sprintf("  - %s %s", client.Name, client.Version)
			if client.Platform != "" {
				result += fmt.Sprintf(" (%s)", client.Platform)
			}
			if client.IsSupported {
				result += fmt.Sprintf(": yes, %s %s %s", client.Protocol, client.Ciphersuite, client.Curve)
			} else {
				result += fmt.Sprintf(": no")
			}
			results = append(results, result)
			continue
		}
		// Once we reach this point, we only want supported clients
		if !client.IsSupported {
			continue
		}
		// if we only want the oldest compatible client, some dark magic is required
		// to parse the version of each client, which varies in format, and figure out
		// the oldest
		if _, ok := productsSupport[client.Name]; !ok {
			// this is the first supported client of this name that we encounter,
			// no comparison needed, simply store it
			productsSupport[client.Name] = client
			continue
		}
		prevClient := productsSupport[client.Name]
		// compare the version of the previous and current clients,
		// if the current client is older, store it instead of the previous one
		isOlder, err := scribe.TestEvrCompare(scribe.EvropLessThan, client.Version, prevClient.Version)
		if err != nil {
			log.Printf("Failed to compare version %s with version %s for client %s: %v",
				client.Version, prevClient.Version, client.Name, err)
		}
		if isOlder {
			productsSupport[client.Name] = client
		}
	}
	if printAll != nil && printAll.(bool) == true {
		// if we just want to print all clients, return here
		return
	}
	// if we only want the oldest client, build the list here
	var supportedClients []string
	for _, clientName := range []string{"Firefox", "Chrome", "Edge", "IE", "Safari", "Opera", "Android", "OpenSSL", "Java"} {
		client := productsSupport[clientName]
		result := fmt.Sprintf("%s %s", client.Name, client.Version)
		supportedClients = append(supportedClients, result)
	}
	results = append(results, fmt.Sprintf("  - %s", strings.Join(supportedClients, ", ")))
	return
}
<|endoftext|>


<commit_before>package ccv3

import (
	"bytes"
	"fmt"

	"code.cloudfoundry.org/cli/api/cloudcontroller"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
)

type ApplicationFeature struct {
	// Name of the application feature
	Name    string
	Enabled bool
	//Reason  string `json:omitempty`
}

type SSHEnabled struct {
	Enabled bool
	Reason  string
}

func (client *Client) GetAppFeature(appGUID string, featureName string) (ApplicationFeature, Warnings, error) {
	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.GetApplicationFeaturesRequest,
		URIParams:   map[string]string{"app_guid": appGUID, "name": featureName},
	})

	if err != nil {
		return ApplicationFeature{}, nil, err
	}

	var applicationFeature ApplicationFeature
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &applicationFeature,
	}

	err = client.connection.Make(request, &response)

	return applicationFeature, response.Warnings, err
}

func (client *Client) GetSSHEnabled(appGUID string) (SSHEnabled, Warnings, error) {
	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.GetSSHEnabled,
		URIParams:   map[string]string{"app_guid": appGUID},
	})

	if err != nil {
		return SSHEnabled{}, nil, err
	}

	var sshEnabled SSHEnabled
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &sshEnabled,
	}

	err = client.connection.Make(request, &response)

	return sshEnabled, response.Warnings, err
}

// UpdateAppFeature enables/disables the ability to ssh for a given application.
func (client *Client) UpdateAppFeature(appGUID string, enabled bool, featureName string) (Warnings, error) {
	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.PatchApplicationFeaturesRequest,
		Body:        bytes.NewReader([]byte(fmt.Sprintf(`{"enabled":%t}`, enabled))),
		URIParams:   map[string]string{"app_guid": appGUID, "name": featureName},
	})

	if err != nil {
		return nil, err
	}

	response := cloudcontroller.Response{}
	err = client.connection.Make(request, &response)

	return response.Warnings, err
}
<commit_msg>fix make format error<commit_after>package ccv3

import (
	"bytes"
	"fmt"

	"code.cloudfoundry.org/cli/api/cloudcontroller"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
)

type ApplicationFeature struct {
	// Name of the application feature
	Name    string
	Enabled bool
	//Reason  string `json:omitempty`
}

type SSHEnabled struct {
	Enabled bool
	Reason  string
}

func (client *Client) GetAppFeature(appGUID string, featureName string) (ApplicationFeature, Warnings, error) {
	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.GetApplicationFeaturesRequest,
		URIParams:   map[string]string{"app_guid": appGUID, "name": featureName},
	})

	if err != nil {
		return ApplicationFeature{}, nil, err
	}

	var applicationFeature ApplicationFeature
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &applicationFeature,
	}

	err = client.connection.Make(request, &response)

	return applicationFeature, response.Warnings, err
}

func (client *Client) GetSSHEnabled(appGUID string) (SSHEnabled, Warnings, error) {
	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.GetSSHEnabled,
		URIParams:   map[string]string{"app_guid": appGUID},
	})

	if err != nil {
		return SSHEnabled{}, nil, err
	}

	var sshEnabled SSHEnabled
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &sshEnabled,
	}

	err = client.connection.Make(request, &response)

	return sshEnabled, response.Warnings, err
}

// UpdateAppFeature enables/disables the ability to ssh for a given application.
func (client *Client) UpdateAppFeature(appGUID string, enabled bool, featureName string) (Warnings, error) {
	request, err := client.newHTTPRequest(requestOptions{
		RequestName: internal.PatchApplicationFeaturesRequest,
		Body:        bytes.NewReader([]byte(fmt.Sprintf(`{"enabled":%t}`, enabled))),
		URIParams:   map[string]string{"app_guid": appGUID, "name": featureName},
	})

	if err != nil {
		return nil, err
	}

	response := cloudcontroller.Response{}
	err = client.connection.Make(request, &response)

	return response.Warnings, err
}
<|endoftext|>


<commit_before>// (c) 2012 Alexander Solovyov
// under terms of ISC license

package gostatic

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

const (
	StateUnknown = iota
	StateChanged
	StateUnchanged
	StateIgnored
)

type Page struct {
	PageHeader

	Site    *Site `json:"-"`
	Rule    *Rule
	Pattern string
	Deps    PageSlice `json:"-"`

	Source  string
	Path    string
	ModTime time.Time

	processed bool
	state     int
	raw       string
	content   string
	wasread   bool // if content was read already
}

type PageSlice []*Page

func NewPages(site *Site, path string) PageSlice {
	stat, err := os.Stat(path)
	errhandle(err)

	relpath, err := filepath.Rel(site.Source, path)
	errhandle(err)

	// convert windows path separators to unix style
	relpath = strings.Replace(relpath, "\\", "/", -1)

	pattern, rules := site.Rules.MatchedRules(relpath)
	if rules == nil {
		rules = make([]*Rule, 1)
	}

	pages := make(PageSlice, 0)

	for _, rule := range rules {
		page := &Page{
			Site:    site,
			Rule:    rule,
			Pattern: pattern,
			Source:  relpath,
			Path:    relpath,
			ModTime: stat.ModTime(),
		}
		page.Peek()
		debug("Found page: %s; rule: %v\n",
			page.Source, page.Rule)
		pages = append(pages, page)
	}
	return pages
}

func (page *Page) Raw() string {
	if !page.wasread {
		data, err := ioutil.ReadFile(page.FullPath())
		errhandle(err)
		page.raw = string(data)
		page.wasread = true
		debug("Page '%s' was read, is of length %d\n", page.FullPath(), len(page.raw))
	}
	return page.raw
}

func (page *Page) Content() string {
	if page.content == "" {
		return page.Raw()
	}
	return page.content
}

func (page *Page) SetContent(content string) {
	page.content = content
}

func (page *Page) SetState(state int) {
	page.state = state
}

func (page *Page) FullPath() string {
	return filepath.Join(page.Site.Source, page.Source)
}

func (page *Page) OutputPath() string {
	return filepath.Join(page.Site.Output, page.Path)
}

func (page *Page) Url() string {
	if page == nil {
		errexit(fmt.Errorf(".Url called on a Page which does not exist"))
	}
	url := strings.Replace(page.Path, string(filepath.Separator), "/", -1)
	if url == "index.html" {
		return ""
	}
	if strings.HasSuffix(url, "/index.html") {
		return strings.TrimSuffix(url, "/index.html") + "/"
	}
	return url
}

func (page *Page) Name() string {
	return filepath.Base(page.Url())
}

func (page *Page) UrlTo(other *Page) string {
	return page.Rel(other.Url())
}

func (page *Page) Rel(path string) string {
	root := strings.Repeat("../", strings.Count(page.Url(), "/"))
	if root == "" {
		root = "./"
	}
	if path[0] == '/' {
		return root + path[1:]
	}
	return root + path
}

func (page *Page) Is(path string) bool {
	return page.Url() == path || page.Path == path
}

// Is used for dynamically created pages
func (page *Page) SetWasRead(wasread bool) {
	page.wasread = wasread
}

func (page *Page) WasRead() bool {
	return page.wasread
}

// Peek is used to run those processors which should be done before others can
// find out about us. Two actual examples include 'config' and 'rename'
// processors.
func (page *Page) Peek() error {
	if page.Rule == nil {
		return nil
	}

	for _, cmd := range page.Rule.Commands {
		err := page.Site.ProcessCommand(page, &cmd, true)
		if err != nil {
			return err
		}
	}

	// Raw is page content after preprocessors, but before preprocessors
	if page.content != "" {
		page.raw = page.content
	}
	return nil
}

func (page *Page) findDeps() {
	if page.Rule == nil {
		return
	}

	deps := make(PageSlice, 0)
	for _, other := range page.Site.Pages {
		if other != page && page.Rule.IsDep(other) {
			deps = append(deps, other)
		}
	}
	page.Deps = deps
}

func (page *Page) Changed() bool {
	if page.Site.ForceRefresh {
		return true
	}

	if page.state == StateUnknown {
		page.state = StateUnchanged
		dest, err := os.Stat(page.OutputPath())

		if err != nil ||
			dest.ModTime().Before(page.ModTime) ||
			dest.ModTime().Before(page.Site.ChangedAt) {
			page.state = StateChanged
		} else {
			for _, dep := range page.Deps {
				if dep.Changed() {
					page.state = StateChanged
				}
			}
		}
	}

	return page.state == StateChanged
}

func (page *Page) Process() (*Page, error) {
	if page.processed || page.Rule == nil {
		return page, nil
	}

	page.processed = true
	if page.Rule.Commands != nil {
		for _, cmd := range page.Rule.Commands {
			err := page.Site.ProcessCommand(page, &cmd, false)
			if err != nil {
				return nil, err
			}
		}
	}

	return page, nil
}

func (page *Page) WriteTo(writer io.Writer) (n int64, err error) {
	if page.Rule == nil {
		return 0, nil
	}

	if !page.processed {
		page.Process()
	}

	nint, err := writer.Write([]byte(page.Content()))
	return int64(nint), err
}

func (page *Page) Render() (n int64, err error) {
	if page.Rule == nil {
		return CopyFile(page.FullPath(), page.OutputPath())
	}

	file, err := os.Create(page.OutputPath())
	if err != nil {
		return 0, err
	}
	defer file.Close()

	return page.WriteTo(file)
}

func (page *Page) UrlMatches(regex string) bool {
	re, err := regexp.Compile(regex)
	if err != nil {
		errhandle(fmt.Errorf("Incorrect regex given to Page.UrlMatches: '%s' ", regex))
	}
	return re.Match([]byte(page.Url()))
}

func (page *Page) Prev() *Page {
	return page.Site.Pages.Prev(page)
}

func (page *Page) Next() *Page {
	return page.Site.Pages.Next(page)
}

// PageSlice manipulation

func (pages PageSlice) Get(i int) *Page { return pages[i] }
func (pages PageSlice) First() *Page    { return pages.Get(0) }
func (pages PageSlice) Last() *Page     { return pages.Get(len(pages) - 1) }

func (pages PageSlice) Prev(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == pages.Len()-1 {
				return nil
			}
			return pages[i+1]
		}
	}
	return nil
}

func (pages PageSlice) Next(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == 0 {
				return nil
			}
			return pages[i-1]
		}
	}
	return nil
}

func (pages PageSlice) Slice(from int, to int) PageSlice {
	length := len(pages)

	if from > length {
		from = length
	}
	if to > length {
		to = length
	}

	return pages[from:to]
}

// Sorting interface
func (pages PageSlice) Len() int {
	return len(pages)
}

func (pages PageSlice) Less(i, j int) bool {
	left := pages.Get(i)
	right := pages.Get(j)
	if left.Date.Unix() == right.Date.Unix() {
		return left.Path > right.Path
	}
	return left.Date.Unix() > right.Date.Unix()
}

func (pages PageSlice) Swap(i, j int) {
	pages[i], pages[j] = pages[j], pages[i]
}

func (pages PageSlice) Sort() {
	sort.Sort(pages)
}

func (pages PageSlice) Reverse() {
	sort.Sort(sort.Reverse(pages))
}

func (pages PageSlice) Children(root string) *PageSlice {
	children := make(PageSlice, 0)

	for _, page := range pages {
		if !page.Hide &&
			strings.HasPrefix(page.Source, root) &&
			page.Url() != root {
			children = append(children, page)
		}
	}

	return &children
}

func (pages PageSlice) WithTag(tag string) *PageSlice {
	tagged := make(PageSlice, 0)

	for _, page := range pages {
		if !page.Hide &&
			page.Tags != nil &&
			SliceStringIndexOf(page.Tags, tag) != -1 {
			tagged = append(tagged, page)
		}
	}

	return &tagged
}

func (pages PageSlice) HasPage(check func(page *Page) bool) bool {
	for _, page := range pages {
		if check(page) {
			return true
		}
	}
	return false
}

func (pages PageSlice) BySource(s string) *Page {
	for _, page := range pages {
		if page.Source == s {
			return page
		}
	}
	return nil
}

func (pages PageSlice) GlobSource(pattern string) *PageSlice {
	found := make(PageSlice, 0)

	for _, page := range pages {
		if matched, _ := path.Match(pattern, page.Source); matched {
			found = append(found, page)
		}
	}

	return &found
}

func (pages PageSlice) ByPath(s string) *Page {
	for _, page := range pages {
		if page.Path == s {
			return page
		}
	}
	return nil
}
<commit_msg>Added UTF-8 with BOM support #79<commit_after>// (c) 2012 Alexander Solovyov
// under terms of ISC license

package gostatic

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
)

const (
	StateUnknown = iota
	StateChanged
	StateUnchanged
	StateIgnored
)

type Page struct {
	PageHeader

	Site    *Site `json:"-"`
	Rule    *Rule
	Pattern string
	Deps    PageSlice `json:"-"`

	Source  string
	Path    string
	ModTime time.Time

	processed bool
	state     int
	raw       string
	content   string
	wasread   bool // if content was read already
}

type PageSlice []*Page

func NewPages(site *Site, path string) PageSlice {
	stat, err := os.Stat(path)
	errhandle(err)

	relpath, err := filepath.Rel(site.Source, path)
	errhandle(err)

	// convert windows path separators to unix style
	relpath = strings.Replace(relpath, "\\", "/", -1)

	pattern, rules := site.Rules.MatchedRules(relpath)
	if rules == nil {
		rules = make([]*Rule, 1)
	}

	pages := make(PageSlice, 0)

	for _, rule := range rules {
		page := &Page{
			Site:    site,
			Rule:    rule,
			Pattern: pattern,
			Source:  relpath,
			Path:    relpath,
			ModTime: stat.ModTime(),
		}
		page.Peek()
		debug("Found page: %s; rule: %v\n",
			page.Source, page.Rule)
		pages = append(pages, page)
	}
	return pages
}

func (page *Page) Raw() string {
	if !page.wasread {
		data, err := ioutil.ReadFile(page.FullPath())
		errhandle(err)

		if bytes.HasPrefix(data, []byte{0xEF, 0xBB, 0xBF}) {
			data = data[3:]
		}

		page.raw = string(data)
		page.wasread = true
		debug("Page '%s' was read, is of length %d\n", page.FullPath(), len(page.raw))
	}
	return page.raw
}

func (page *Page) Content() string {
	if page.content == "" {
		return page.Raw()
	}
	return page.content
}

func (page *Page) SetContent(content string) {
	page.content = content
}

func (page *Page) SetState(state int) {
	page.state = state
}

func (page *Page) FullPath() string {
	return filepath.Join(page.Site.Source, page.Source)
}

func (page *Page) OutputPath() string {
	return filepath.Join(page.Site.Output, page.Path)
}

func (page *Page) Url() string {
	if page == nil {
		errexit(fmt.Errorf(".Url called on a Page which does not exist"))
	}
	url := strings.Replace(page.Path, string(filepath.Separator), "/", -1)
	if url == "index.html" {
		return ""
	}
	if strings.HasSuffix(url, "/index.html") {
		return strings.TrimSuffix(url, "/index.html") + "/"
	}
	return url
}

func (page *Page) Name() string {
	return filepath.Base(page.Url())
}

func (page *Page) UrlTo(other *Page) string {
	return page.Rel(other.Url())
}

func (page *Page) Rel(path string) string {
	root := strings.Repeat("../", strings.Count(page.Url(), "/"))
	if root == "" {
		root = "./"
	}
	if path[0] == '/' {
		return root + path[1:]
	}
	return root + path
}

func (page *Page) Is(path string) bool {
	return page.Url() == path || page.Path == path
}

// Is used for dynamically created pages
func (page *Page) SetWasRead(wasread bool) {
	page.wasread = wasread
}

func (page *Page) WasRead() bool {
	return page.wasread
}

// Peek is used to run those processors which should be done before others can
// find out about us. Two actual examples include 'config' and 'rename'
// processors.
func (page *Page) Peek() error {
	if page.Rule == nil {
		return nil
	}

	for _, cmd := range page.Rule.Commands {
		err := page.Site.ProcessCommand(page, &cmd, true)
		if err != nil {
			return err
		}
	}

	// Raw is page content after preprocessors, but before preprocessors
	if page.content != "" {
		page.raw = page.content
	}
	return nil
}

func (page *Page) findDeps() {
	if page.Rule == nil {
		return
	}

	deps := make(PageSlice, 0)
	for _, other := range page.Site.Pages {
		if other != page && page.Rule.IsDep(other) {
			deps = append(deps, other)
		}
	}
	page.Deps = deps
}

func (page *Page) Changed() bool {
	if page.Site.ForceRefresh {
		return true
	}

	if page.state == StateUnknown {
		page.state = StateUnchanged
		dest, err := os.Stat(page.OutputPath())

		if err != nil ||
			dest.ModTime().Before(page.ModTime) ||
			dest.ModTime().Before(page.Site.ChangedAt) {
			page.state = StateChanged
		} else {
			for _, dep := range page.Deps {
				if dep.Changed() {
					page.state = StateChanged
				}
			}
		}
	}

	return page.state == StateChanged
}

func (page *Page) Process() (*Page, error) {
	if page.processed || page.Rule == nil {
		return page, nil
	}

	page.processed = true
	if page.Rule.Commands != nil {
		for _, cmd := range page.Rule.Commands {
			err := page.Site.ProcessCommand(page, &cmd, false)
			if err != nil {
				return nil, err
			}
		}
	}

	return page, nil
}

func (page *Page) WriteTo(writer io.Writer) (n int64, err error) {
	if page.Rule == nil {
		return 0, nil
	}

	if !page.processed {
		page.Process()
	}

	nint, err := writer.Write([]byte(page.Content()))
	return int64(nint), err
}

func (page *Page) Render() (n int64, err error) {
	if page.Rule == nil {
		return CopyFile(page.FullPath(), page.OutputPath())
	}

	file, err := os.Create(page.OutputPath())
	if err != nil {
		return 0, err
	}
	defer file.Close()

	return page.WriteTo(file)
}

func (page *Page) UrlMatches(regex string) bool {
	re, err := regexp.Compile(regex)
	if err != nil {
		errhandle(fmt.Errorf("Incorrect regex given to Page.UrlMatches: '%s' ", regex))
	}
	return re.Match([]byte(page.Url()))
}

func (page *Page) Prev() *Page {
	return page.Site.Pages.Prev(page)
}

func (page *Page) Next() *Page {
	return page.Site.Pages.Next(page)
}

// PageSlice manipulation

func (pages PageSlice) Get(i int) *Page { return pages[i] }
func (pages PageSlice) First() *Page    { return pages.Get(0) }
func (pages PageSlice) Last() *Page     { return pages.Get(len(pages) - 1) }

func (pages PageSlice) Prev(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == pages.Len()-1 {
				return nil
			}
			return pages[i+1]
		}
	}
	return nil
}

func (pages PageSlice) Next(cur *Page) *Page {
	for i, page := range pages {
		if page == cur {
			if i == 0 {
				return nil
			}
			return pages[i-1]
		}
	}
	return nil
}

func (pages PageSlice) Slice(from int, to int) PageSlice {
	length := len(pages)

	if from > length {
		from = length
	}
	if to > length {
		to = length
	}

	return pages[from:to]
}

// Sorting interface
func (pages PageSlice) Len() int {
	return len(pages)
}

func (pages PageSlice) Less(i, j int) bool {
	left := pages.Get(i)
	right := pages.Get(j)
	if left.Date.Unix() == right.Date.Unix() {
		return left.Path > right.Path
	}
	return left.Date.Unix() > right.Date.Unix()
}

func (pages PageSlice) Swap(i, j int) {
	pages[i], pages[j] = pages[j], pages[i]
}

func (pages PageSlice) Sort() {
	sort.Sort(pages)
}

func (pages PageSlice) Reverse() {
	sort.Sort(sort.Reverse(pages))
}

func (pages PageSlice) Children(root string) *PageSlice {
	children := make(PageSlice, 0)

	for _, page := range pages {
		if !page.Hide &&
			strings.HasPrefix(page.Source, root) &&
			page.Url() != root {
			children = append(children, page)
		}
	}

	return &children
}

func (pages PageSlice) WithTag(tag string) *PageSlice {
	tagged := make(PageSlice, 0)

	for _, page := range pages {
		if !page.Hide &&
			page.Tags != nil &&
			SliceStringIndexOf(page.Tags, tag) != -1 {
			tagged = append(tagged, page)
		}
	}

	return &tagged
}

func (pages PageSlice) HasPage(check func(page *Page) bool) bool {
	for _, page := range pages {
		if check(page) {
			return true
		}
	}
	return false
}

func (pages PageSlice) BySource(s string) *Page {
	for _, page := range pages {
		if page.Source == s {
			return page
		}
	}
	return nil
}

func (pages PageSlice) GlobSource(pattern string) *PageSlice {
	found := make(PageSlice, 0)

	for _, page := range pages {
		if matched, _ := path.Match(pattern, page.Source); matched {
			found = append(found, page)
		}
	}

	return &found
}

func (pages PageSlice) ByPath(s string) *Page {
	for _, page := range pages {
		if page.Path == s {
			return page
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/* github.com/rubicks/trygo/hello/hello.go */

package main

import (
	"fmt"
	"runtime"
)

func main() {
	pc, file, line, ok := runtime.Caller(0)
	fmt.Println("pc	 : ", pc)
	fmt.Println("file : ", file)
	fmt.Println("line : ", line)
	fmt.Println("ok	 : ", ok)
	fmt.Println("Hello, Go!")
}
<commit_msg>edits hello to use stringutil<commit_after>/* github.com/rubicks/trygo/hello/hello.go */

package main

import (
	"fmt"
	"github.com/rubicks/trygo"
	"runtime"
)

func main() {
	pc, file, line, ok := runtime.Caller(0)
	fmt.Println("pc	 : ", pc)
	fmt.Println("file : ", file)
	fmt.Println("line : ", line)
	fmt.Println("ok	 : ", ok)
	fmt.Println("Hello, Go!")
	fmt.Printf("stringutil.Reverse(\"race car\") == \"%s\"\n",
		stringutil.Reverse("race car"))
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc.

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 main

import (
	"fmt"

	"github.com/golang/example/stringutil"
)

func main() {
	fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))
}
<commit_msg>Correct dependencies.<commit_after>/*
Copyright 2014 Google Inc.

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 main

import (
	"fmt"

	"github.com/robertosgm/example/stringutil"
)

func main() {
	fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))
}
<|endoftext|>


<commit_before>package projector

import (
	"code.google.com/p/goprotobuf/proto"
	"fmt"
	"strconv"
	"strings"

	ap "github.com/couchbase/indexing/secondary/adminport"
	c "github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/protobuf"
	"github.com/couchbaselabs/go-couchbase"
)

func GetVbmap(
	client ap.Client,
	pooln, bucketn string,
	kvaddrs []string) (*protobuf.VbmapResponse, error) {

	req := &protobuf.VbmapRequest{
		Pool:    proto.String(pooln),
		Bucket:  proto.String(bucketn),
		Kvaddrs: kvaddrs,
	}
	res := &protobuf.VbmapResponse{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	}
	return res, nil
}

// Based on the latest vbmap gather the list of kvnode-address and return the
// same.
func GetKVAddrs(cluster, pooln, bucketn string) ([]string, error) {
	b, err := c.ConnectBucket(cluster, pooln, bucketn)
	if err != nil {
		return nil, err
	}
	b.Close()

	b.Refresh()
	m, err := b.GetVBmap(nil)
	if err != nil {
		return nil, err
	}

	kvaddrs := make([]string, 0, len(m))
	for kvaddr := range m {
		kvaddrs = append(kvaddrs, kvaddr)
	}
	return kvaddrs, nil
}

func GetFailoverLogs(
	client ap.Client, pooln, bucketn string,
	vbnos []uint32) (*protobuf.FailoverLogResponse, error) {

	req := &protobuf.FailoverLogRequest{
		Pool:   proto.String(pooln),
		Bucket: proto.String(bucketn),
		Vbnos:  vbnos,
	}
	res := &protobuf.FailoverLogResponse{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	}
	return res, nil
}

// Start Initial stream for a set of buckets hosted by `kvaddr`.
func InitialMutationStream(
	client ap.Client,
	topic, pooln, kvaddr string, buckets []string,
	instances []*protobuf.IndexInst) (*protobuf.MutationStreamResponse, error) {

	req := &protobuf.MutationStreamRequest{
		Topic:             proto.String(topic),
		Pools:             []string{},
		Buckets:           []string{},
		RestartTimestamps: []*protobuf.TsVbuuid{},
		Instances:         instances,
	}
	for _, bucketn := range buckets {
		vbnos, flogs, err := flogsFromKV(client, pooln, bucketn, kvaddr)
		if err != nil {
			return nil, err
		}
		ts := protobuf.NewTsVbuuid(bucketn, c.MaxVbuckets)
		ts = ts.InitialRestartTs(vbnos).ComputeRestartTs(flogs)

		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucketn)
		req.RestartTimestamps = append(req.RestartTimestamps, ts)
	}
	req.SetStartFlag()
	res := &protobuf.MutationStreamResponse{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	} else if err := res.GetErr(); err != nil {
		return nil, fmt.Errorf(err.GetError())
	}
	return res, nil
}

func RestartMutationStream(
	client ap.Client,
	topic, pooln string, tss map[string]*protobuf.TsVbuuid,
	instances []*protobuf.IndexInst,
	callb func(*protobuf.MutationStreamResponse, error) bool) {

	req := &protobuf.UpdateMutationStreamRequest{
		Topic:             proto.String(topic),
		Pools:             []string{},
		Buckets:           []string{},
		RestartTimestamps: []*protobuf.TsVbuuid{},
		Instances:         instances,
	}
	for bucketn, ts := range tss {
		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucketn)
		req.RestartTimestamps = append(req.RestartTimestamps, ts)
	}
	req.SetRestartFlag()
	res := &protobuf.MutationStreamResponse{}
	if err := client.Request(req, res); err != nil {
		callb(nil, err)
	} else {
		callb(res, nil)
	}
}

func RepairEndpoints(
	client ap.Client,
	topic string, endpoints []string) (*protobuf.Error, error) {

	req := &protobuf.RepairDownstreamEndpoints{
		Topic: proto.String(topic), Endpoints: endpoints}
	res := &protobuf.Error{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	} else if err := res.GetError(); err != "" {
		return nil, fmt.Errorf(err)
	}
	return res, nil
}

func ShutdownStream(client ap.Client, topic string) (*protobuf.Error, error) {
	req := &protobuf.ShutdownStreamRequest{Topic: proto.String(topic)}
	res := &protobuf.Error{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	} else if err := res.GetError(); err != "" {
		return nil, fmt.Errorf(err)
	}
	return res, nil
}

// SpawnProjectors for each kvaddrs, provided they are not already spawned and
// remembered in `projectors` parameter. return the new set of projectors that
// were spawned.
// adminport of the projector will be 500+kvport.
func SpawnProjectors(
	cluster string, kvaddrs []string,
	projectors map[string]ap.Client) (map[string]ap.Client, error) {

	newprojectors := make(map[string]ap.Client)
	// create a projector instance for each kvnode
	for _, kvaddr := range kvaddrs {
		if _, ok := projectors[kvaddr]; ok {
			continue
		}
		ss := strings.Split(kvaddr, ":")
		kport, err := strconv.Atoi(ss[1])
		if err != nil {
			return nil, err
		}
		adminport := ss[0] + ":" + strconv.Itoa(kport+500)
		NewProjector(cluster, []string{kvaddr}, adminport)
		c := ap.NewHTTPClient("http://"+adminport, c.AdminportURLPrefix)
		projectors[kvaddr] = c
		newprojectors[kvaddr] = c
	}
	return newprojectors, nil
}

func ShutdownProjectors(
	cluster, pooln string, buckets []string,
	projectors map[string]ap.Client) (map[string]ap.Client, error) {

	var b *couchbase.Bucket
	var err error

	for _, bucketn := range buckets {
		if b, err = c.ConnectBucket(cluster, pooln, bucketn); err != nil {
			return nil, err
		} else {
			break
		}
	}

	defer b.Close()

	b.Refresh()
	m, err := b.GetVBmap(nil)
	if err != nil {
		return nil, err
	}

	newProjectors := make(map[string]ap.Client)
	for kvaddr, c := range projectors {
		if vbnos, ok := m[kvaddr]; !ok || (vbnos != nil && len(vbnos) == 0) {
			ShutdownStream(c, "backfill")
		} else if ok {
			newProjectors[kvaddr] = c
		}
	}
	projectors = newProjectors
	return newProjectors, nil
}

func AddBuckets(
	client ap.Client,
	topic, kvaddr, pooln string, buckets []string,
	tss map[string]*protobuf.TsVbuuid,
	instances []*protobuf.IndexInst) error {

	req := &protobuf.UpdateMutationStreamRequest{
		Topic:             proto.String(topic),
		Pools:             []string{},
		Buckets:           []string{},
		RestartTimestamps: []*protobuf.TsVbuuid{},
		Instances:         instances,
	}
	for _, bucketn := range buckets {
		var ts *protobuf.TsVbuuid
		if tss == nil || tss[bucketn] == nil {
			vbnos, flogs, err := flogsFromKV(client, pooln, bucketn, kvaddr)
			if err != nil {
				return err
			}
			ts = protobuf.NewTsVbuuid(bucketn, c.MaxVbuckets)
			ts = ts.InitialRestartTs(vbnos).ComputeRestartTs(flogs)
		} else {
			ts = tss[bucketn]
		}
		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucketn)
		req.RestartTimestamps = append(req.RestartTimestamps, ts)
	}
	req.SetRestartFlag()
	req.SetAddBucketFlag()
	res := &protobuf.MutationStreamResponse{}
	return client.Request(req, res)
}

func DelBuckets(client ap.Client, topic, pooln string, buckets []string) error {
	req := &protobuf.UpdateMutationStreamRequest{
		Topic:   proto.String(topic),
		Pools:   []string{},
		Buckets: []string{},
	}
	for _, bucket := range buckets {
		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucket)
	}
	req.SetRestartFlag()
	req.SetDelBucketFlag()
	res := &protobuf.MutationStreamResponse{}
	return client.Request(req, res)
}

func flogsFromKV(
	client ap.Client,
	pooln, bucketn, kvaddr string) ([]uint16, couchbase.FailoverLog, error) {

	// get subset of vbuckets hosted by `kvaddr`
	vbmap, err := GetVbmap(client, pooln, bucketn, []string{kvaddr})
	if err != nil {
		return nil, nil, err
	}
	// get failover logs
	flogs, err := GetFailoverLogs(client, pooln, bucketn, vbmap.Vbuckets32())
	if err != nil {
		return nil, nil, err
	}
	return vbmap.Vbuckets16(), flogs.ToFailoverLog(vbmap.Vbuckets16()), nil
}
<commit_msg>Added comment to adminlib.go<commit_after>package projector

import (
	"code.google.com/p/goprotobuf/proto"
	"fmt"
	"strconv"
	"strings"

	ap "github.com/couchbase/indexing/secondary/adminport"
	c "github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/protobuf"
	"github.com/couchbaselabs/go-couchbase"
)

func GetVbmap(
	client ap.Client,
	pooln, bucketn string,
	kvaddrs []string) (*protobuf.VbmapResponse, error) {

	req := &protobuf.VbmapRequest{
		Pool:    proto.String(pooln),
		Bucket:  proto.String(bucketn),
		Kvaddrs: kvaddrs,
	}
	res := &protobuf.VbmapResponse{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	}
	return res, nil
}

// Based on the latest vbmap gather the list of kvnode-address and return the
// same.
func GetKVAddrs(cluster, pooln, bucketn string) ([]string, error) {
	b, err := c.ConnectBucket(cluster, pooln, bucketn)
	if err != nil {
		return nil, err
	}
	b.Close()

	b.Refresh()
	m, err := b.GetVBmap(nil)
	if err != nil {
		return nil, err
	}

	kvaddrs := make([]string, 0, len(m))
	for kvaddr := range m {
		kvaddrs = append(kvaddrs, kvaddr)
	}
	return kvaddrs, nil
}

func GetFailoverLogs(
	client ap.Client, pooln, bucketn string,
	vbnos []uint32) (*protobuf.FailoverLogResponse, error) {

	req := &protobuf.FailoverLogRequest{
		Pool:   proto.String(pooln),
		Bucket: proto.String(bucketn),
		Vbnos:  vbnos,
	}
	res := &protobuf.FailoverLogResponse{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	}
	return res, nil
}

// Start Initial stream for a set of buckets hosted by `kvaddr`.
func InitialMutationStream(
	client ap.Client,
	topic, pooln, kvaddr string, buckets []string,
	instances []*protobuf.IndexInst) (*protobuf.MutationStreamResponse, error) {

	req := &protobuf.MutationStreamRequest{
		Topic:             proto.String(topic),
		Pools:             []string{},
		Buckets:           []string{},
		RestartTimestamps: []*protobuf.TsVbuuid{},
		Instances:         instances,
	}
	for _, bucketn := range buckets {
		vbnos, flogs, err := flogsFromKV(client, pooln, bucketn, kvaddr)
		if err != nil {
			return nil, err
		}
		ts := protobuf.NewTsVbuuid(bucketn, c.MaxVbuckets)
		ts = ts.InitialRestartTs(vbnos).ComputeRestartTs(flogs)

		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucketn)
		req.RestartTimestamps = append(req.RestartTimestamps, ts)
	}
	req.SetStartFlag()
	res := &protobuf.MutationStreamResponse{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	} else if err := res.GetErr(); err != nil {
		return nil, fmt.Errorf(err.GetError())
	}
	return res, nil
}

func RestartMutationStream(
	client ap.Client,
	topic, pooln string, tss map[string]*protobuf.TsVbuuid,
	instances []*protobuf.IndexInst,
	callb func(*protobuf.MutationStreamResponse, error) bool) {

	req := &protobuf.UpdateMutationStreamRequest{
		Topic:             proto.String(topic),
		Pools:             []string{},
		Buckets:           []string{},
		RestartTimestamps: []*protobuf.TsVbuuid{},
		Instances:         instances,
	}
	for bucketn, ts := range tss {
		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucketn)
		req.RestartTimestamps = append(req.RestartTimestamps, ts)
	}
	req.SetRestartFlag()
	res := &protobuf.MutationStreamResponse{}
	if err := client.Request(req, res); err != nil {
		callb(nil, err)
	} else {
		callb(res, nil)
	}
}

func RepairEndpoints(
	client ap.Client,
	topic string, endpoints []string) (*protobuf.Error, error) {

	req := &protobuf.RepairDownstreamEndpoints{
		Topic: proto.String(topic), Endpoints: endpoints}
	res := &protobuf.Error{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	} else if err := res.GetError(); err != "" {
		return nil, fmt.Errorf(err)
	}
	return res, nil
}

func ShutdownStream(client ap.Client, topic string) (*protobuf.Error, error) {
	req := &protobuf.ShutdownStreamRequest{Topic: proto.String(topic)}
	res := &protobuf.Error{}
	if err := client.Request(req, res); err != nil {
		return nil, err
	} else if err := res.GetError(); err != "" {
		return nil, fmt.Errorf(err)
	}
	return res, nil
}

// SpawnProjectors for each kvaddrs, provided they are not already spawned and
// remembered in `projectors` parameter. return the new set of projectors that
// were spawned.
// adminport of the projector will be 500+kvport.
func SpawnProjectors(
	cluster string, kvaddrs []string,
	projectors map[string]ap.Client) (map[string]ap.Client, error) {

	// kvaddr -> adminport-client
	newprojectors := make(map[string]ap.Client)
	// create a projector instance for each kvnode
	for _, kvaddr := range kvaddrs {
		if _, ok := projectors[kvaddr]; ok {
			continue
		}
		ss := strings.Split(kvaddr, ":")
		kport, err := strconv.Atoi(ss[1])
		if err != nil {
			return nil, err
		}
		adminport := ss[0] + ":" + strconv.Itoa(kport+500)
		NewProjector(cluster, []string{kvaddr}, adminport)
		c := ap.NewHTTPClient("http://"+adminport, c.AdminportURLPrefix)
		projectors[kvaddr] = c
		newprojectors[kvaddr] = c
	}
	return newprojectors, nil
}

func ShutdownProjectors(
	cluster, pooln string, buckets []string,
	projectors map[string]ap.Client) (map[string]ap.Client, error) {

	var b *couchbase.Bucket
	var err error

	for _, bucketn := range buckets {
		if b, err = c.ConnectBucket(cluster, pooln, bucketn); err != nil {
			return nil, err
		} else {
			break
		}
	}

	defer b.Close()

	b.Refresh()
	m, err := b.GetVBmap(nil)
	if err != nil {
		return nil, err
	}

	newProjectors := make(map[string]ap.Client)
	for kvaddr, c := range projectors {
		if vbnos, ok := m[kvaddr]; !ok || (vbnos != nil && len(vbnos) == 0) {
			ShutdownStream(c, "backfill")
		} else if ok {
			newProjectors[kvaddr] = c
		}
	}
	projectors = newProjectors
	return newProjectors, nil
}

func AddBuckets(
	client ap.Client,
	topic, kvaddr, pooln string, buckets []string,
	tss map[string]*protobuf.TsVbuuid,
	instances []*protobuf.IndexInst) error {

	req := &protobuf.UpdateMutationStreamRequest{
		Topic:             proto.String(topic),
		Pools:             []string{},
		Buckets:           []string{},
		RestartTimestamps: []*protobuf.TsVbuuid{},
		Instances:         instances,
	}
	for _, bucketn := range buckets {
		var ts *protobuf.TsVbuuid
		if tss == nil || tss[bucketn] == nil {
			vbnos, flogs, err := flogsFromKV(client, pooln, bucketn, kvaddr)
			if err != nil {
				return err
			}
			ts = protobuf.NewTsVbuuid(bucketn, c.MaxVbuckets)
			ts = ts.InitialRestartTs(vbnos).ComputeRestartTs(flogs)
		} else {
			ts = tss[bucketn]
		}
		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucketn)
		req.RestartTimestamps = append(req.RestartTimestamps, ts)
	}
	req.SetRestartFlag()
	req.SetAddBucketFlag()
	res := &protobuf.MutationStreamResponse{}
	return client.Request(req, res)
}

func DelBuckets(client ap.Client, topic, pooln string, buckets []string) error {
	req := &protobuf.UpdateMutationStreamRequest{
		Topic:   proto.String(topic),
		Pools:   []string{},
		Buckets: []string{},
	}
	for _, bucket := range buckets {
		req.Pools = append(req.Pools, pooln)
		req.Buckets = append(req.Buckets, bucket)
	}
	req.SetRestartFlag()
	req.SetDelBucketFlag()
	res := &protobuf.MutationStreamResponse{}
	return client.Request(req, res)
}

func flogsFromKV(
	client ap.Client,
	pooln, bucketn, kvaddr string) ([]uint16, couchbase.FailoverLog, error) {

	// get subset of vbuckets hosted by `kvaddr`
	vbmap, err := GetVbmap(client, pooln, bucketn, []string{kvaddr})
	if err != nil {
		return nil, nil, err
	}
	// get failover logs
	flogs, err := GetFailoverLogs(client, pooln, bucketn, vbmap.Vbuckets32())
	if err != nil {
		return nil, nil, err
	}
	return vbmap.Vbuckets16(), flogs.ToFailoverLog(vbmap.Vbuckets16()), nil
}
<|endoftext|>


<commit_before>package httphelper

import (
	"context"
	"encoding/json"
	"net/http"

	"dmitryfrank.com/geekmarks/server/interror"
	"dmitryfrank.com/geekmarks/server/middleware"

	"github.com/golang/glog"
	"github.com/juju/errors"
)

var (
	internalServerError error
	unauthorizedError   error
	forbiddenError      error
)

func init() {
	internalServerError = errors.New("internal server error")
	unauthorizedError = errors.New("unauthorized")
	forbiddenError = errors.New("forbidden")
}

type ErrorResponse struct {
	Status  int    `json:"status"`
	Message string `json:"message"`
}

const (
	DesiredContentTypeKey = "desiredContentType"
)

func RespondWithError(w http.ResponseWriter, r *http.Request, errResp error) {
	httpErrorCode := getHTTPErrorCode(errResp)

	desiredContentType := "text/html"

	if errors.Cause(errResp) == internalServerError {
		glog.Errorf("INTERNAL SERVER ERROR:\n" + interror.ErrorStack(errResp))
	} else {
		glog.V(2).Infof(errors.ErrorStack(errResp))
	}

	v := r.Context().Value(DesiredContentTypeKey)
	if v != nil {
		var ok bool
		desiredContentType, ok = v.(string)
		if !ok {
			glog.Errorf("wrong type of desiredContentType: %T (%v)",
				desiredContentType, desiredContentType)
			w.WriteHeader(http.StatusInternalServerError)
		}
	}

	switch desiredContentType {
	case "application/json":
		resp := ErrorResponse{
			Status:  httpErrorCode,
			Message: errResp.Error(),
		}
		d, err := json.Marshal(resp)
		if err != nil {
			panic(err)
		}

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(httpErrorCode)
		_, err = w.Write(d)
		if err != nil {
			panic(err)
		}
	case "text/html":
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.WriteHeader(httpErrorCode)
		_, err := w.Write([]byte("Error: " + errResp.Error()))
		if err != nil {
			panic(err)
		}
	default:
		glog.Errorf("wrong desiredContentType: %q", desiredContentType)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func MakeAPIHandler(
	f func(r *http.Request) (resp interface{}, err error),
) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		resp, err := f(r)
		if err != nil {
			RespondWithError(w, r, errors.Trace(err))
			return
		}

		d, err := json.Marshal(resp)
		if err != nil {
			RespondWithError(w, r, MakeInternalServerError(
				errors.Annotatef(err, "marshalling resp"),
			))
		}

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(d)
		if err != nil {
			panic(err)
		}
	}
}

// MakeInternalServerError logs the given error and returns internalServerError
// annotated with the message, which does NOT wrap the original error, since we
// don't want internal server error details to percolate to clients.
func MakeInternalServerError(intError error) error {
	if errors.Cause(intError) != internalServerError {
		return interror.WrapInternalError(intError, internalServerError)
	}
	return errors.Trace(intError)
}

func MakeInternalServerErrorf(
	intError error, format string, args ...interface{},
) error {
	return interror.WrapInternalError(
		intError,
		errors.Annotatef(internalServerError, format, args...),
	)
}

func MakeUnauthorizedError() error {
	return unauthorizedError
}

func MakeForbiddenError() error {
	return forbiddenError
}

func getHTTPErrorCode(err error) int {
	status := http.StatusBadRequest

	switch errors.Cause(err) {
	case internalServerError:
		status = http.StatusInternalServerError
	case unauthorizedError:
		status = http.StatusUnauthorized
	case forbiddenError:
		status = http.StatusForbidden
	}

	return status
}

func MakeDesiredContentTypeMiddleware(
	contentType string,
) func(inner http.Handler) http.Handler {
	return func(inner http.Handler) http.Handler {
		mw := func(w http.ResponseWriter, r *http.Request) {
			// Process request
			inner.ServeHTTP(w, r.WithContext(context.WithValue(
				r.Context(), DesiredContentTypeKey, contentType,
			)))
		}
		return middleware.MkMiddleware(mw)
	}
}
<commit_msg>Emit indented JSON<commit_after>package httphelper

import (
	"context"
	"encoding/json"
	"net/http"

	"dmitryfrank.com/geekmarks/server/interror"
	"dmitryfrank.com/geekmarks/server/middleware"

	"github.com/golang/glog"
	"github.com/juju/errors"
)

var (
	internalServerError error
	unauthorizedError   error
	forbiddenError      error
)

func init() {
	internalServerError = errors.New("internal server error")
	unauthorizedError = errors.New("unauthorized")
	forbiddenError = errors.New("forbidden")
}

type ErrorResponse struct {
	Status  int    `json:"status"`
	Message string `json:"message"`
}

const (
	DesiredContentTypeKey = "desiredContentType"
)

func RespondWithError(w http.ResponseWriter, r *http.Request, errResp error) {
	httpErrorCode := getHTTPErrorCode(errResp)

	desiredContentType := "text/html"

	if errors.Cause(errResp) == internalServerError {
		glog.Errorf("INTERNAL SERVER ERROR:\n" + interror.ErrorStack(errResp))
	} else {
		glog.V(2).Infof(errors.ErrorStack(errResp))
	}

	v := r.Context().Value(DesiredContentTypeKey)
	if v != nil {
		var ok bool
		desiredContentType, ok = v.(string)
		if !ok {
			glog.Errorf("wrong type of desiredContentType: %T (%v)",
				desiredContentType, desiredContentType)
			w.WriteHeader(http.StatusInternalServerError)
		}
	}

	switch desiredContentType {
	case "application/json":
		resp := ErrorResponse{
			Status:  httpErrorCode,
			Message: errResp.Error(),
		}
		d, err := json.MarshalIndent(resp, "", "  ")
		if err != nil {
			panic(err)
		}

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(httpErrorCode)
		_, err = w.Write(d)
		if err != nil {
			panic(err)
		}
	case "text/html":
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.WriteHeader(httpErrorCode)
		_, err := w.Write([]byte("Error: " + errResp.Error()))
		if err != nil {
			panic(err)
		}
	default:
		glog.Errorf("wrong desiredContentType: %q", desiredContentType)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func MakeAPIHandler(
	f func(r *http.Request) (resp interface{}, err error),
) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		resp, err := f(r)
		if err != nil {
			RespondWithError(w, r, errors.Trace(err))
			return
		}

		d, err := json.MarshalIndent(resp, "", "  ")
		if err != nil {
			RespondWithError(w, r, MakeInternalServerError(
				errors.Annotatef(err, "marshalling resp"),
			))
		}

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(d)
		if err != nil {
			panic(err)
		}
	}
}

// MakeInternalServerError logs the given error and returns internalServerError
// annotated with the message, which does NOT wrap the original error, since we
// don't want internal server error details to percolate to clients.
func MakeInternalServerError(intError error) error {
	if errors.Cause(intError) != internalServerError {
		return interror.WrapInternalError(intError, internalServerError)
	}
	return errors.Trace(intError)
}

func MakeInternalServerErrorf(
	intError error, format string, args ...interface{},
) error {
	return interror.WrapInternalError(
		intError,
		errors.Annotatef(internalServerError, format, args...),
	)
}

func MakeUnauthorizedError() error {
	return unauthorizedError
}

func MakeForbiddenError() error {
	return forbiddenError
}

func getHTTPErrorCode(err error) int {
	status := http.StatusBadRequest

	switch errors.Cause(err) {
	case internalServerError:
		status = http.StatusInternalServerError
	case unauthorizedError:
		status = http.StatusUnauthorized
	case forbiddenError:
		status = http.StatusForbidden
	}

	return status
}

func MakeDesiredContentTypeMiddleware(
	contentType string,
) func(inner http.Handler) http.Handler {
	return func(inner http.Handler) http.Handler {
		mw := func(w http.ResponseWriter, r *http.Request) {
			// Process request
			inner.ServeHTTP(w, r.WithContext(context.WithValue(
				r.Context(), DesiredContentTypeKey, contentType,
			)))
		}
		return middleware.MkMiddleware(mw)
	}
}
<|endoftext|>