<commit_before>/*
Package jsonrpc2 implements a JSON-RPC 2.0 ClientCodec and ServerCodec
for the net/rpc package and HTTP transport for JSON-RPC 2.0.

RPC method's signature

JSON-RPC 2.0 support positional and named parameters. Which one should be
used when calling server's method depends on type of that method's first
parameter: if it is an Array or Slice then positional parameters should be
used, if it is a Map or Struct then named parameters should be used. (Also
any method can be called without parameters at all.) If first parameter
will be of custom type with json.Unmarshaler interface then it depends on
what is supported by that type - this way you can even implement method
which can be called both with positional and named parameters.

JSON-RPC 2.0 support result of any type, so method's result (second param)
can be a reference of any type supported by json.Marshal.

JSON-RPC 2.0 support error codes and optional extra error data in addition
to error message. If method returns error of standard error type (i.e.
just error message without error code) then error code -32000 will be
used. To define custom error code (and optionally extra error data) method
should return jsonrpc2.Error.

Using positional parameters of different types

If you'll have to provide method which should be called using positional
parameters of different types then it's recommended to implement this
using first parameter of custom type with json.Unmarshaler interface.

To call such a method you'll have to use client.Call() with []interface{}
in args.

Decoding errors on client

Because of net/rpc limitations client.Call() can't return JSON-RPC 2.0
error with code, message and extra data - it'll return either one of
rpc.ErrShutdown or io.ErrUnexpectedEOF errors, or encoded JSON-RPC 2.0
error, which have to be decoded using jsonrpc2.ServerError to get error's
code, message and extra data.

Limitations

HTTP does not support Pipelined Requests/Responses.

HTTP does not support GET Request.

Because of net/rpc limitations RPC method MUST NOT return standard
error which begins with '{' and ends with '}'.

Because of net/rpc limitations there is no way to provide
transport-level details (like client's IP) to RPC method.

Current implementation does a lot of sanity checks to conform to
protocol spec. Making most of them optional may improve performance.
*/
package jsonrpc2
<commit_msg>update doc<commit_after>/*
Package jsonrpc2 implements a JSON-RPC 2.0 ClientCodec and ServerCodec
for the net/rpc package and HTTP transport for JSON-RPC 2.0.

RPC method's signature

JSON-RPC 2.0 support positional and named parameters. Which one should be
used when calling server's method depends on type of that method's first
parameter: if it is an Array or Slice then positional parameters should be
used, if it is a Map or Struct then named parameters should be used. (Also
any method can be called without parameters at all.) If first parameter
will be of custom type with json.Unmarshaler interface then it depends on
what is supported by that type - this way you can even implement method
which can be called both with positional and named parameters.

JSON-RPC 2.0 support result of any type, so method's result (second param)
can be a reference of any type supported by json.Marshal.

JSON-RPC 2.0 support error codes and optional extra error data in addition
to error message. If method returns error of standard error type (i.e.
just error message without error code) then error code -32000 will be
used. To define custom error code (and optionally extra error data) method
should return jsonrpc2.Error.

Using positional parameters of different types

If you'll have to provide method which should be called using positional
parameters of different types then it's recommended to implement this
using first parameter of custom type with json.Unmarshaler interface.

To call such a method you'll have to use client.Call() with []interface{}
in args.

Decoding errors on client

Because of net/rpc limitations client.Call() can't return JSON-RPC 2.0
error with code, message and extra data - it'll return either one of
rpc.ErrShutdown or io.ErrUnexpectedEOF errors, or encoded JSON-RPC 2.0
error, which have to be decoded using jsonrpc2.ServerError to get error's
code, message and extra data.

Limitations

HTTP client&server does not support Pipelined Requests/Responses.

HTTP client&server does not support GET Request.

HTTP client does not support Batch Request.

Because of net/rpc limitations RPC method MUST NOT return standard
error which begins with '{' and ends with '}'.

Because of net/rpc limitations there is no way to provide
transport-level details (like client's IP) to RPC method.

Current implementation does a lot of sanity checks to conform to
protocol spec. Making most of them optional may improve performance.
*/
package jsonrpc2
<|endoftext|>


<commit_before>package dynamic

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Driver struct {
	builtin bool
	url     string
	hash    string
	name    string
}

func NewDriver(builtin bool, name, url, hash string) *Driver {
	d := &Driver{
		builtin: builtin,
		name:    name,
		url:     url,
		hash:    hash,
	}
	if d.builtin && !strings.HasPrefix(d.name, "docker-machine-driver-") {
		d.name = "docker-machine-driver-" + d.name
	}
	return d
}

func (d *Driver) Name() string {
	return d.name
}

func (d *Driver) Hash() string {
	return d.hash
}

func (d *Driver) Checksum() string {
	return d.name
}

func (d *Driver) FriendlyName() string {
	return strings.TrimPrefix(d.name, "docker-machine-driver-")
}

func (d *Driver) Remove() error {
	cacheFilePrefix := d.cacheFile()
	content, err := ioutil.ReadFile(cacheFilePrefix)
	if os.IsNotExist(err) {
		return nil
	}

	if err != nil {
		return err
	}

	dest := path.Join(binDir(), string(content))
	os.Remove(dest)
	os.Remove(cacheFilePrefix + "-" + string(content))
	os.Remove(cacheFilePrefix)

	return nil
}

func (d *Driver) Stage() error {
	if err := d.getError(); err != nil {
		return err
	}

	return d.setError(d.stage())
}

func (d *Driver) setError(err error) error {
	errFile := d.cacheFile() + ".error"

	if err != nil {
		os.MkdirAll(path.Dir(errFile))
		ioutil.WriteFile(errFile, []byte(err.Error()), 0600)
	}
	return err
}

func (d *Driver) getError() error {
	errFile := d.cacheFile() + ".error"

	if content, err := ioutil.ReadFile(errFile); err == nil {
		logrus.Errorf("Returning previous error: %s", content)
		d.ClearError()
		return errors.New(string(content))
	}

	return nil
}

func (d *Driver) ClearError() {
	errFile := d.cacheFile() + ".error"
	os.Remove(errFile)
}

func (d *Driver) stage() error {
	if d.builtin {
		return nil
	}

	cacheFilePrefix := d.cacheFile()

	driverName, err := isInstalled(cacheFilePrefix)
	if err != nil || driverName != "" {
		d.name = driverName
		return err
	}

	tempFile, err := ioutil.TempFile("", "machine-driver")
	if err != nil {
		return err
	}
	defer os.Remove(tempFile.Name())
	defer tempFile.Close()

	hasher, err := getHasher(d.hash)
	if err != nil {
		return err
	}

	downloadDest := io.Writer(tempFile)
	if hasher != nil {
		downloadDest = io.MultiWriter(tempFile, hasher)
	}

	if err := d.download(downloadDest); err != nil {
		return err
	}

	if got, ok := compare(hasher, d.hash); !ok {
		return fmt.Errorf("Hash does not match, got %s, expected %s", got, d.hash)
	}

	if err := tempFile.Close(); err != nil {
		return err
	}

	driverName, err = d.copyBinary(cacheFilePrefix, tempFile.Name())
	if err != nil {
		return err
	}

	d.name = driverName
	return nil
}

func (d *Driver) Install() error {
	if d.builtin {
		return nil
	}

	f, err := os.OpenFile(path.Join(binDir(), d.name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	defer f.Close()

	src, err := os.Open(d.srcBinName())
	if err != nil {
		return err
	}
	defer src.Close()

	logrus.Infof("Copying %s => %s", d.srcBinName(), path.Join(binDir(), d.name))
	_, err = io.Copy(f, src)
	return err
}

func isElf(input string) bool {
	f, err := os.Open(input)
	if err != nil {
		return false
	}
	defer f.Close()

	elf := make([]byte, 4)
	if _, err := f.Read(elf); err != nil {
		return false
	}

	return bytes.Compare(elf, []byte{0x7f, 0x45, 0x4c, 0x46}) == 0
}

func (d *Driver) copyBinary(cacheFile, input string) (string, error) {
	temp, err := ioutil.TempDir("", "machine-driver-extract")
	if err != nil {
		return "", err
	}
	defer os.RemoveAll(temp)

	file := ""
	driverName := ""

	if isElf(input) {
		file = input
		u, err := url.Parse(d.url)
		if err != nil {
			return "", err
		}
		driverName = strings.Split(path.Base(u.Path), "_")[0]
		if !strings.HasPrefix(driverName, "docker-machine-driver-") {
			return "", fmt.Errorf("Invalid URL %s, path should be of the format docker-machine-driver-*", d.url)
		}
	} else {
		if err := exec.Command("tar", "xvf", input, "-C", temp).Run(); err != nil {
			if err := exec.Command("unzip", "-o", input, "-d", temp).Run(); err != nil {
				return "", fmt.Errorf("Failed to extract")
			}
		}
	}

	filepath.Walk(temp, filepath.WalkFunc(func(p string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		if strings.HasPrefix(path.Base(p), "docker-machine-driver-") {
			file = p
		}

		return nil
	}))

	if file == "" {
		return "", fmt.Errorf("Failed to find machine driver in archive. There must be a file of form docker-machine-driver*")
	}

	if driverName == "" {
		driverName = path.Base(file)
	}
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	if err := os.MkdirAll(path.Dir(cacheFile), 0755); err != nil {
		return "", err
	}

	dest, err := os.Create(cacheFile + "-" + driverName)
	if err != nil {
		return "", err
	}
	defer dest.Close()

	if _, err := io.Copy(dest, f); err != nil {
		return "", err
	}

	logrus.Infof("Found driver %s", driverName)
	return driverName, ioutil.WriteFile(cacheFile, []byte(driverName), 0644)
}

func (d *Driver) srcBinName() string {
	return d.cacheFile() + "-" + d.name
}

func binDir() string {
	dest := os.Getenv("GMS_BIN_DIR")
	if dest != "" {
		return dest
	}
	return "/usr/local/bin"
}

func compare(hash hash.Hash, value string) (string, bool) {
	if hash == nil {
		return "", true
	}

	got := hex.EncodeToString(hash.Sum([]byte{}))
	expected := strings.TrimSpace(strings.ToLower(value))

	return got, got == expected
}

func getHasher(hash string) (hash.Hash, error) {
	switch len(hash) {
	case 0:
		return nil, nil
	case 32:
		return md5.New(), nil
	case 40:
		return sha1.New(), nil
	case 64:
		return sha256.New(), nil
	case 128:
		return sha512.New(), nil
	}

	return nil, fmt.Errorf("Invalid hash format: %s", hash)
}

func (d *Driver) download(dest io.Writer) error {
	logrus.Infof("Download %s", d.url)
	resp, err := http.Get(d.url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	_, err = io.Copy(dest, resp.Body)
	return err
}

func (d *Driver) cacheFile() string {
	key := sha256Bytes([]byte(d.url + d.hash))

	base := os.Getenv("CATTLE_HOME")
	if base == "" {
		base = "/var/lib/cattle"
	}

	return path.Join(base, "machine-drivers", key)
}

func isInstalled(file string) (string, error) {
	content, err := ioutil.ReadFile(file)
	if os.IsNotExist(err) {
		return "", nil
	}
	return strings.TrimSpace(string(content)), err
}

func sha256Bytes(content []byte) string {
	hash := sha256.New()
	io.Copy(hash, bytes.NewBuffer(content))
	return hex.EncodeToString(hash.Sum([]byte{}))
}
<commit_msg>Fix compilation error<commit_after>package dynamic

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Driver struct {
	builtin bool
	url     string
	hash    string
	name    string
}

func NewDriver(builtin bool, name, url, hash string) *Driver {
	d := &Driver{
		builtin: builtin,
		name:    name,
		url:     url,
		hash:    hash,
	}
	if d.builtin && !strings.HasPrefix(d.name, "docker-machine-driver-") {
		d.name = "docker-machine-driver-" + d.name
	}
	return d
}

func (d *Driver) Name() string {
	return d.name
}

func (d *Driver) Hash() string {
	return d.hash
}

func (d *Driver) Checksum() string {
	return d.name
}

func (d *Driver) FriendlyName() string {
	return strings.TrimPrefix(d.name, "docker-machine-driver-")
}

func (d *Driver) Remove() error {
	cacheFilePrefix := d.cacheFile()
	content, err := ioutil.ReadFile(cacheFilePrefix)
	if os.IsNotExist(err) {
		return nil
	}

	if err != nil {
		return err
	}

	dest := path.Join(binDir(), string(content))
	os.Remove(dest)
	os.Remove(cacheFilePrefix + "-" + string(content))
	os.Remove(cacheFilePrefix)

	return nil
}

func (d *Driver) Stage() error {
	if err := d.getError(); err != nil {
		return err
	}

	return d.setError(d.stage())
}

func (d *Driver) setError(err error) error {
	errFile := d.cacheFile() + ".error"

	if err != nil {
		os.MkdirAll(path.Dir(errFile), 0700)
		ioutil.WriteFile(errFile, []byte(err.Error()), 0600)
	}
	return err
}

func (d *Driver) getError() error {
	errFile := d.cacheFile() + ".error"

	if content, err := ioutil.ReadFile(errFile); err == nil {
		logrus.Errorf("Returning previous error: %s", content)
		d.ClearError()
		return errors.New(string(content))
	}

	return nil
}

func (d *Driver) ClearError() {
	errFile := d.cacheFile() + ".error"
	os.Remove(errFile)
}

func (d *Driver) stage() error {
	if d.builtin {
		return nil
	}

	cacheFilePrefix := d.cacheFile()

	driverName, err := isInstalled(cacheFilePrefix)
	if err != nil || driverName != "" {
		d.name = driverName
		return err
	}

	tempFile, err := ioutil.TempFile("", "machine-driver")
	if err != nil {
		return err
	}
	defer os.Remove(tempFile.Name())
	defer tempFile.Close()

	hasher, err := getHasher(d.hash)
	if err != nil {
		return err
	}

	downloadDest := io.Writer(tempFile)
	if hasher != nil {
		downloadDest = io.MultiWriter(tempFile, hasher)
	}

	if err := d.download(downloadDest); err != nil {
		return err
	}

	if got, ok := compare(hasher, d.hash); !ok {
		return fmt.Errorf("Hash does not match, got %s, expected %s", got, d.hash)
	}

	if err := tempFile.Close(); err != nil {
		return err
	}

	driverName, err = d.copyBinary(cacheFilePrefix, tempFile.Name())
	if err != nil {
		return err
	}

	d.name = driverName
	return nil
}

func (d *Driver) Install() error {
	if d.builtin {
		return nil
	}

	f, err := os.OpenFile(path.Join(binDir(), d.name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	defer f.Close()

	src, err := os.Open(d.srcBinName())
	if err != nil {
		return err
	}
	defer src.Close()

	logrus.Infof("Copying %s => %s", d.srcBinName(), path.Join(binDir(), d.name))
	_, err = io.Copy(f, src)
	return err
}

func isElf(input string) bool {
	f, err := os.Open(input)
	if err != nil {
		return false
	}
	defer f.Close()

	elf := make([]byte, 4)
	if _, err := f.Read(elf); err != nil {
		return false
	}

	return bytes.Compare(elf, []byte{0x7f, 0x45, 0x4c, 0x46}) == 0
}

func (d *Driver) copyBinary(cacheFile, input string) (string, error) {
	temp, err := ioutil.TempDir("", "machine-driver-extract")
	if err != nil {
		return "", err
	}
	defer os.RemoveAll(temp)

	file := ""
	driverName := ""

	if isElf(input) {
		file = input
		u, err := url.Parse(d.url)
		if err != nil {
			return "", err
		}
		driverName = strings.Split(path.Base(u.Path), "_")[0]
		if !strings.HasPrefix(driverName, "docker-machine-driver-") {
			return "", fmt.Errorf("Invalid URL %s, path should be of the format docker-machine-driver-*", d.url)
		}
	} else {
		if err := exec.Command("tar", "xvf", input, "-C", temp).Run(); err != nil {
			if err := exec.Command("unzip", "-o", input, "-d", temp).Run(); err != nil {
				return "", fmt.Errorf("Failed to extract")
			}
		}
	}

	filepath.Walk(temp, filepath.WalkFunc(func(p string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		if strings.HasPrefix(path.Base(p), "docker-machine-driver-") {
			file = p
		}

		return nil
	}))

	if file == "" {
		return "", fmt.Errorf("Failed to find machine driver in archive. There must be a file of form docker-machine-driver*")
	}

	if driverName == "" {
		driverName = path.Base(file)
	}
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	if err := os.MkdirAll(path.Dir(cacheFile), 0755); err != nil {
		return "", err
	}

	dest, err := os.Create(cacheFile + "-" + driverName)
	if err != nil {
		return "", err
	}
	defer dest.Close()

	if _, err := io.Copy(dest, f); err != nil {
		return "", err
	}

	logrus.Infof("Found driver %s", driverName)
	return driverName, ioutil.WriteFile(cacheFile, []byte(driverName), 0644)
}

func (d *Driver) srcBinName() string {
	return d.cacheFile() + "-" + d.name
}

func binDir() string {
	dest := os.Getenv("GMS_BIN_DIR")
	if dest != "" {
		return dest
	}
	return "/usr/local/bin"
}

func compare(hash hash.Hash, value string) (string, bool) {
	if hash == nil {
		return "", true
	}

	got := hex.EncodeToString(hash.Sum([]byte{}))
	expected := strings.TrimSpace(strings.ToLower(value))

	return got, got == expected
}

func getHasher(hash string) (hash.Hash, error) {
	switch len(hash) {
	case 0:
		return nil, nil
	case 32:
		return md5.New(), nil
	case 40:
		return sha1.New(), nil
	case 64:
		return sha256.New(), nil
	case 128:
		return sha512.New(), nil
	}

	return nil, fmt.Errorf("Invalid hash format: %s", hash)
}

func (d *Driver) download(dest io.Writer) error {
	logrus.Infof("Download %s", d.url)
	resp, err := http.Get(d.url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	_, err = io.Copy(dest, resp.Body)
	return err
}

func (d *Driver) cacheFile() string {
	key := sha256Bytes([]byte(d.url + d.hash))

	base := os.Getenv("CATTLE_HOME")
	if base == "" {
		base = "/var/lib/cattle"
	}

	return path.Join(base, "machine-drivers", key)
}

func isInstalled(file string) (string, error) {
	content, err := ioutil.ReadFile(file)
	if os.IsNotExist(err) {
		return "", nil
	}
	return strings.TrimSpace(string(content)), err
}

func sha256Bytes(content []byte) string {
	hash := sha256.New()
	io.Copy(hash, bytes.NewBuffer(content))
	return hex.EncodeToString(hash.Sum([]byte{}))
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Ceph-CSI Authors.

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

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

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

package e2e

import (
	"context"
	"errors"
	"fmt"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
)

// execCommandInPodWithName run command in pod using podName.
func execCommandInPodWithName(
	f *framework.Framework,
	cmdString,
	podName,
	containerName,
	nameSpace string) (string, string, error) {
	cmd := []string{"/bin/sh", "-c", cmdString}
	podOpt := framework.ExecOptions{
		Command:            cmd,
		PodName:            podName,
		Namespace:          nameSpace,
		ContainerName:      containerName,
		Stdin:              nil,
		CaptureStdout:      true,
		CaptureStderr:      true,
		PreserveWhitespace: true,
	}

	return f.ExecWithOptions(podOpt)
}

// loadAppDeployment loads the deployment app config and return deployment
// object.
func loadAppDeployment(path string) (*appsv1.Deployment, error) {
	deploy := appsv1.Deployment{}
	if err := unmarshal(path, &deploy); err != nil {
		return nil, err
	}

	return &deploy, nil
}

// createDeploymentApp creates the deployment object and waits for it to be in
// Available state.
func createDeploymentApp(clientSet kubernetes.Interface, app *appsv1.Deployment, deployTimeout int) error {
	_, err := clientSet.AppsV1().Deployments(app.Namespace).Create(context.TODO(), app, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("failed to create deploy: %w", err)
	}

	return waitForDeploymentInAvailableState(clientSet, app.Name, app.Namespace, deployTimeout)
}

// deleteDeploymentApp deletes the deployment object.
func deleteDeploymentApp(clientSet kubernetes.Interface, name, ns string, deployTimeout int) error {
	timeout := time.Duration(deployTimeout) * time.Minute
	err := clientSet.AppsV1().Deployments(ns).Delete(context.TODO(), name, metav1.DeleteOptions{})
	if err != nil {
		return fmt.Errorf("failed to delete deployment: %w", err)
	}
	start := time.Now()
	e2elog.Logf("Waiting for deployment %q to be deleted", name)

	return wait.PollImmediate(poll, timeout, func() (bool, error) {
		_, err := clientSet.AppsV1().Deployments(ns).Get(context.TODO(), name, metav1.GetOptions{})
		if err != nil {
			if isRetryableAPIError(err) {
				return false, nil
			}
			if apierrs.IsNotFound(err) {
				return true, nil
			}
			e2elog.Logf("%q deployment to be deleted (%d seconds elapsed)", name, int(time.Since(start).Seconds()))

			return false, fmt.Errorf("failed to get deployment: %w", err)
		}

		return false, nil
	})
}

// waitForDeploymentInAvailableState wait for deployment to be in Available state.
func waitForDeploymentInAvailableState(clientSet kubernetes.Interface, name, ns string, deployTimeout int) error {
	timeout := time.Duration(deployTimeout) * time.Minute
	start := time.Now()
	e2elog.Logf("Waiting up to %q to be in Available state", name)

	return wait.PollImmediate(poll, timeout, func() (bool, error) {
		d, err := clientSet.AppsV1().Deployments(ns).Get(context.TODO(), name, metav1.GetOptions{})
		if err != nil {
			if isRetryableAPIError(err) {
				return false, nil
			}
			e2elog.Logf("%q deployment to be Available (%d seconds elapsed)", name, int(time.Since(start).Seconds()))

			return false, err
		}
		cond := deploymentutil.GetDeploymentCondition(d.Status, appsv1.DeploymentAvailable)

		return cond != nil, nil
	})
}

// Waits for the deployment to complete.
func waitForDeploymentComplete(clientSet kubernetes.Interface, name, ns string, deployTimeout int) error {
	var (
		deployment *appsv1.Deployment
		reason     string
		err        error
	)
	timeout := time.Duration(deployTimeout) * time.Minute
	err = wait.PollImmediate(poll, timeout, func() (bool, error) {
		deployment, err = clientSet.AppsV1().Deployments(ns).Get(context.TODO(), name, metav1.GetOptions{})
		if err != nil {
			if isRetryableAPIError(err) {
				return false, nil
			}
			e2elog.Logf("deployment error: %v", err)

			return false, err
		}

		// TODO need to check rolling update

		// When the deployment status and its underlying resources reach the
		// desired state, we're done
		if deployment.Status.Replicas == deployment.Status.ReadyReplicas {
			return true, nil
		}
		e2elog.Logf(
			"deployment status: expected replica count %d running replica count %d",
			deployment.Status.Replicas,
			deployment.Status.ReadyReplicas)
		reason = fmt.Sprintf("deployment status: %#v", deployment.Status.String())

		return false, nil
	})

	if errors.Is(err, wait.ErrWaitTimeout) {
		err = fmt.Errorf("%s", reason)
	}
	if err != nil {
		return fmt.Errorf("error waiting for deployment %q status to match desired state: %w", name, err)
	}

	return nil
}
<commit_msg>e2e: consider not found error in deployment check<commit_after>/*
Copyright 2021 The Ceph-CSI Authors.

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

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

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

package e2e

import (
	"context"
	"errors"
	"fmt"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
)

// execCommandInPodWithName run command in pod using podName.
func execCommandInPodWithName(
	f *framework.Framework,
	cmdString,
	podName,
	containerName,
	nameSpace string) (string, string, error) {
	cmd := []string{"/bin/sh", "-c", cmdString}
	podOpt := framework.ExecOptions{
		Command:            cmd,
		PodName:            podName,
		Namespace:          nameSpace,
		ContainerName:      containerName,
		Stdin:              nil,
		CaptureStdout:      true,
		CaptureStderr:      true,
		PreserveWhitespace: true,
	}

	return f.ExecWithOptions(podOpt)
}

// loadAppDeployment loads the deployment app config and return deployment
// object.
func loadAppDeployment(path string) (*appsv1.Deployment, error) {
	deploy := appsv1.Deployment{}
	if err := unmarshal(path, &deploy); err != nil {
		return nil, err
	}

	return &deploy, nil
}

// createDeploymentApp creates the deployment object and waits for it to be in
// Available state.
func createDeploymentApp(clientSet kubernetes.Interface, app *appsv1.Deployment, deployTimeout int) error {
	_, err := clientSet.AppsV1().Deployments(app.Namespace).Create(context.TODO(), app, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("failed to create deploy: %w", err)
	}

	return waitForDeploymentInAvailableState(clientSet, app.Name, app.Namespace, deployTimeout)
}

// deleteDeploymentApp deletes the deployment object.
func deleteDeploymentApp(clientSet kubernetes.Interface, name, ns string, deployTimeout int) error {
	timeout := time.Duration(deployTimeout) * time.Minute
	err := clientSet.AppsV1().Deployments(ns).Delete(context.TODO(), name, metav1.DeleteOptions{})
	if err != nil {
		return fmt.Errorf("failed to delete deployment: %w", err)
	}
	start := time.Now()
	e2elog.Logf("Waiting for deployment %q to be deleted", name)

	return wait.PollImmediate(poll, timeout, func() (bool, error) {
		_, err := clientSet.AppsV1().Deployments(ns).Get(context.TODO(), name, metav1.GetOptions{})
		if err != nil {
			if isRetryableAPIError(err) {
				return false, nil
			}
			if apierrs.IsNotFound(err) {
				return true, nil
			}
			e2elog.Logf("%q deployment to be deleted (%d seconds elapsed)", name, int(time.Since(start).Seconds()))

			return false, fmt.Errorf("failed to get deployment: %w", err)
		}

		return false, nil
	})
}

// waitForDeploymentInAvailableState wait for deployment to be in Available state.
func waitForDeploymentInAvailableState(clientSet kubernetes.Interface, name, ns string, deployTimeout int) error {
	timeout := time.Duration(deployTimeout) * time.Minute
	start := time.Now()
	e2elog.Logf("Waiting up to %q to be in Available state", name)

	return wait.PollImmediate(poll, timeout, func() (bool, error) {
		d, err := clientSet.AppsV1().Deployments(ns).Get(context.TODO(), name, metav1.GetOptions{})
		if err != nil {
			if isRetryableAPIError(err) {
				return false, nil
			}
			e2elog.Logf("%q deployment to be Available (%d seconds elapsed)", name, int(time.Since(start).Seconds()))

			return false, err
		}
		cond := deploymentutil.GetDeploymentCondition(d.Status, appsv1.DeploymentAvailable)

		return cond != nil, nil
	})
}

// Waits for the deployment to complete.
func waitForDeploymentComplete(clientSet kubernetes.Interface, name, ns string, deployTimeout int) error {
	var (
		deployment *appsv1.Deployment
		reason     string
		err        error
	)
	timeout := time.Duration(deployTimeout) * time.Minute
	err = wait.PollImmediate(poll, timeout, func() (bool, error) {
		deployment, err = clientSet.AppsV1().Deployments(ns).Get(context.TODO(), name, metav1.GetOptions{})
		if err != nil {
			if isRetryableAPIError(err) {
				return false, nil
			}
			if apierrs.IsNotFound(err) {
				return false, nil
			}
			e2elog.Logf("deployment error: %v", err)

			return false, err
		}

		// TODO need to check rolling update

		// When the deployment status and its underlying resources reach the
		// desired state, we're done
		if deployment.Status.Replicas == deployment.Status.ReadyReplicas {
			return true, nil
		}
		e2elog.Logf(
			"deployment status: expected replica count %d running replica count %d",
			deployment.Status.Replicas,
			deployment.Status.ReadyReplicas)
		reason = fmt.Sprintf("deployment status: %#v", deployment.Status.String())

		return false, nil
	})

	if errors.Is(err, wait.ErrWaitTimeout) {
		err = fmt.Errorf("%s", reason)
	}
	if err != nil {
		return fmt.Errorf("error waiting for deployment %q status to match desired state: %w", name, err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Michał Matczuk
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tunnel

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"

	"github.com/mmatczuk/go-http-tunnel/log"
	"github.com/mmatczuk/go-http-tunnel/proto"
)

// HTTPProxy forwards HTTP traffic.
type HTTPProxy struct {
	httputil.ReverseProxy
	// localURL specifies default base URL of local service.
	localURL *url.URL
	// localURLMap specifies mapping from ControlMessage.ForwardedHost to
	// local service URL, keys may contain host and port, only host or
	// only port. The order of precedence is the following
	// * host and port
	// * port
	// * host
	localURLMap map[string]*url.URL
	// logger is the proxy logger.
	logger log.Logger
}

// NewHTTPProxy creates a new direct HTTPProxy, everything will be proxied to
// localURL.
func NewHTTPProxy(localURL *url.URL, logger log.Logger) *HTTPProxy {
	if localURL == nil {
		panic("empty localURL")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	p := &HTTPProxy{
		localURL: localURL,
		logger:   logger,
	}
	p.ReverseProxy.Director = p.Director

	return p
}

// NewMultiHTTPProxy creates a new dispatching HTTPProxy, requests may go to
// different backends based on localURLMap.
func NewMultiHTTPProxy(localURLMap map[string]*url.URL, logger log.Logger) *HTTPProxy {
	if localURLMap == nil {
		panic("empty localURLMap")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	p := &HTTPProxy{
		localURLMap: localURLMap,
		logger:      logger,
	}
	p.ReverseProxy.Director = p.Director

	return p
}

// Proxy is a ProxyFunc.
func (p *HTTPProxy) Proxy(w io.Writer, r io.ReadCloser, msg *proto.ControlMessage) {
	switch msg.ForwardedProto {
	case proto.HTTP, proto.HTTPS:
		// ok
	default:
		p.logger.Log(
			"level", 0,
			"msg", "unsupported protocol",
			"ctrlMsg", msg,
		)
		return
	}

	rw, ok := w.(http.ResponseWriter)
	if !ok {
		panic(fmt.Sprintf("Expected http.ResponseWriter got %T", w))
	}

	req, err := http.ReadRequest(bufio.NewReader(r))
	if err != nil {
		p.logger.Log(
			"level", 0,
			"msg", "failed to read request",
			"ctrlMsg", msg,
			"err", err,
		)
		return
	}
	req.URL.Host = msg.ForwardedHost

	p.ServeHTTP(rw, req)
}

// Director is ReverseProxy Director it changes request URL so that the request
// is correctly routed based on localURL and localURLMap. If no URL can be found
// the request is canceled.
func (p *HTTPProxy) Director(req *http.Request) {
	orig := *req.URL

	target := p.localURLFor(req.URL)
	if target == nil {
		p.logger.Log(
			"level", 1,
			"msg", "no target",
			"url", req.URL,
		)

		_, cancel := context.WithCancel(req.Context())
		cancel()

		return
	}

	req.URL.Scheme = target.Scheme
	req.URL.Host = target.Host
	req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

	targetQuery := target.RawQuery
	if targetQuery == "" || req.URL.RawQuery == "" {
		req.URL.RawQuery = targetQuery + req.URL.RawQuery
	} else {
		req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
	}
	if _, ok := req.Header["User-Agent"]; !ok {
		// explicitly disable User-Agent so it's not set to default value
		req.Header.Set("User-Agent", "")
	}

	req.Host = req.URL.Host

	p.logger.Log(
		"level", 2,
		"action", "url rewrite",
		"from", &orig,
		"to", req.URL,
	)
}

func singleJoiningSlash(a, b string) string {
	if a == "" || a == "/" {
		return b
	}
	if b == "" || b == "/" {
		return a
	}

	return path.Join(a, b)
}

func (p *HTTPProxy) localURLFor(u *url.URL) *url.URL {
	if p.localURLMap == nil {
		return p.localURL
	}

	// try host and port
	hostPort := u.Host
	if addr := p.localURLMap[hostPort]; addr != nil {
		return addr
	}

	// try port
	host, port, _ := net.SplitHostPort(hostPort)
	if addr := p.localURLMap[port]; addr != nil {
		return addr
	}

	// try host
	if addr := p.localURLMap[host]; addr != nil {
		return addr
	}

	return p.localURL
}
<commit_msg>httpproxy: support for X-Forwarded-Host and X-Forwarded-Proto<commit_after>// Copyright (C) 2017 Michał Matczuk
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tunnel

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"

	"github.com/mmatczuk/go-http-tunnel/log"
	"github.com/mmatczuk/go-http-tunnel/proto"
)

// HTTPProxy forwards HTTP traffic.
type HTTPProxy struct {
	httputil.ReverseProxy
	// localURL specifies default base URL of local service.
	localURL *url.URL
	// localURLMap specifies mapping from ControlMessage.ForwardedHost to
	// local service URL, keys may contain host and port, only host or
	// only port. The order of precedence is the following
	// * host and port
	// * port
	// * host
	localURLMap map[string]*url.URL
	// logger is the proxy logger.
	logger log.Logger
}

// NewHTTPProxy creates a new direct HTTPProxy, everything will be proxied to
// localURL.
func NewHTTPProxy(localURL *url.URL, logger log.Logger) *HTTPProxy {
	if localURL == nil {
		panic("empty localURL")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	p := &HTTPProxy{
		localURL: localURL,
		logger:   logger,
	}
	p.ReverseProxy.Director = p.Director

	return p
}

// NewMultiHTTPProxy creates a new dispatching HTTPProxy, requests may go to
// different backends based on localURLMap.
func NewMultiHTTPProxy(localURLMap map[string]*url.URL, logger log.Logger) *HTTPProxy {
	if localURLMap == nil {
		panic("empty localURLMap")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	p := &HTTPProxy{
		localURLMap: localURLMap,
		logger:      logger,
	}
	p.ReverseProxy.Director = p.Director

	return p
}

// Proxy is a ProxyFunc.
func (p *HTTPProxy) Proxy(w io.Writer, r io.ReadCloser, msg *proto.ControlMessage) {
	switch msg.ForwardedProto {
	case proto.HTTP, proto.HTTPS:
		// ok
	default:
		p.logger.Log(
			"level", 0,
			"msg", "unsupported protocol",
			"ctrlMsg", msg,
		)
		return
	}

	rw, ok := w.(http.ResponseWriter)
	if !ok {
		panic(fmt.Sprintf("Expected http.ResponseWriter got %T", w))
	}

	req, err := http.ReadRequest(bufio.NewReader(r))
	if err != nil {
		p.logger.Log(
			"level", 0,
			"msg", "failed to read request",
			"ctrlMsg", msg,
			"err", err,
		)
		return
	}

	req.URL.Host = msg.ForwardedHost

	if req.Header.Get("X-Forwarded-Host") == "" {
		req.Header.Set("X-Forwarded-Host", msg.ForwardedHost)
	}
	if req.Header.Get("X-Forwarded-Proto") == "" {
		req.Header.Set("X-Forwarded-Proto", msg.ForwardedProto)
	}

	p.ServeHTTP(rw, req)
}

// Director is ReverseProxy Director it changes request URL so that the request
// is correctly routed based on localURL and localURLMap. If no URL can be found
// the request is canceled.
func (p *HTTPProxy) Director(req *http.Request) {
	orig := *req.URL

	target := p.localURLFor(req.URL)
	if target == nil {
		p.logger.Log(
			"level", 1,
			"msg", "no target",
			"url", req.URL,
		)

		_, cancel := context.WithCancel(req.Context())
		cancel()

		return
	}

	req.URL.Host = target.Host
	req.URL.Scheme = target.Scheme
	req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

	targetQuery := target.RawQuery
	if targetQuery == "" || req.URL.RawQuery == "" {
		req.URL.RawQuery = targetQuery + req.URL.RawQuery
	} else {
		req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
	}
	if _, ok := req.Header["User-Agent"]; !ok {
		// explicitly disable User-Agent so it's not set to default value
		req.Header.Set("User-Agent", "")
	}

	req.Host = req.URL.Host

	p.logger.Log(
		"level", 2,
		"action", "url rewrite",
		"from", &orig,
		"to", req.URL,
	)
}

func singleJoiningSlash(a, b string) string {
	if a == "" || a == "/" {
		return b
	}
	if b == "" || b == "/" {
		return a
	}

	return path.Join(a, b)
}

func (p *HTTPProxy) localURLFor(u *url.URL) *url.URL {
	if p.localURLMap == nil {
		return p.localURL
	}

	// try host and port
	hostPort := u.Host
	if addr := p.localURLMap[hostPort]; addr != nil {
		return addr
	}

	// try port
	host, port, _ := net.SplitHostPort(hostPort)
	if addr := p.localURLMap[port]; addr != nil {
		return addr
	}

	// try host
	if addr := p.localURLMap[host]; addr != nil {
		return addr
	}

	return p.localURL
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	mux "github.com/gorilla/mux"
	p2p_peer "github.com/ipfs/go-libp2p-peer"
	mc "github.com/mediachain/concat/mc"
	mcq "github.com/mediachain/concat/mc/query"
	pb "github.com/mediachain/concat/proto"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func (node *Node) httpId(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, node.Identity.Pretty())
}

func (node *Node) httpPing(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: Bad id: %s\n", err.Error())
		return
	}

	err = node.doPing(r.Context(), pid)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, "OK")
}

func (node *Node) httpPublish(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ns := vars["namespace"]

	rbody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/publish: Error reading request body: %s", err.Error())
		return
	}

	// just simple statements for now
	sbody := new(pb.SimpleStatement)
	err = json.Unmarshal(rbody, sbody)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	sid, err := node.doPublish(ns, sbody)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, sid)
}

func (node *Node) httpStatement(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["statementId"]

	stmt, err := node.db.Get(id)
	if err != nil {
		switch err {
		case UnknownStatement:
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "Unknown statement\n")
			return

		default:
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "Error: %s\n", err.Error())
			return
		}
	}

	err = json.NewEncoder(w).Encode(stmt)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}
}

func (node *Node) httpQuery(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/query: Error reading request body: %s", err.Error())
		return
	}

	q, err := mcq.ParseQuery(string(body))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	res, err := node.db.Query(q)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	err = json.NewEncoder(w).Encode(res)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}
}

func (node *Node) httpStatus(w http.ResponseWriter, r *http.Request) {
	status := statusString[node.status]
	fmt.Fprintln(w, status)
}

func (node *Node) httpStatusSet(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	state := vars["state"]

	var err error
	switch state {
	case "offline":
		err = node.goOffline()

	case "online":
		err = node.goOnline()

	case "public":
		err = node.goPublic()

	default:
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Bad state: %s\n", state)
		return
	}

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, statusString[node.status])
}

func (node *Node) httpConfigDir(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodHead:
		return
	case http.MethodGet:
		if node.dir != nil {
			fmt.Fprintln(w, mc.FormatHandle(*node.dir))
		} else {
			fmt.Fprintln(w, "nil")
		}
	case http.MethodPost:
		node.httpConfigDirSet(w, r)

	default:
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Unsupported method: %s", r.Method)
	}
}

func (node *Node) httpConfigDirSet(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/query: Error reading request body: %s", err.Error())
		return
	}

	handle := strings.TrimSpace(string(body))
	pinfo, err := mc.ParseHandle(handle)

	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	node.dir = &pinfo
	fmt.Fprintln(w, "OK")
}
<commit_msg>mcnode: implement streaming interface for /query<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	mux "github.com/gorilla/mux"
	p2p_peer "github.com/ipfs/go-libp2p-peer"
	mc "github.com/mediachain/concat/mc"
	mcq "github.com/mediachain/concat/mc/query"
	pb "github.com/mediachain/concat/proto"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func (node *Node) httpId(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, node.Identity.Pretty())
}

func (node *Node) httpPing(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	peerId := vars["peerId"]
	pid, err := p2p_peer.IDB58Decode(peerId)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: Bad id: %s\n", err.Error())
		return
	}

	err = node.doPing(r.Context(), pid)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, "OK")
}

func (node *Node) httpPublish(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	ns := vars["namespace"]

	rbody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/publish: Error reading request body: %s", err.Error())
		return
	}

	// just simple statements for now
	sbody := new(pb.SimpleStatement)
	err = json.Unmarshal(rbody, sbody)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	sid, err := node.doPublish(ns, sbody)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, sid)
}

func (node *Node) httpStatement(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["statementId"]

	stmt, err := node.db.Get(id)
	if err != nil {
		switch err {
		case UnknownStatement:
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "Unknown statement\n")
			return

		default:
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "Error: %s\n", err.Error())
			return
		}
	}

	err = json.NewEncoder(w).Encode(stmt)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}
}

func (node *Node) httpQuery(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/query: Error reading request body: %s", err.Error())
		return
	}

	q, err := mcq.ParseQuery(string(body))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()

	ch, err := node.db.QueryStream(ctx, q)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	enc := json.NewEncoder(w)
	for obj := range ch {
		err = enc.Encode(obj)
		if err != nil {
			log.Printf("Error encoding query data: %s", err.Error())
			return
		}
	}
}

func (node *Node) httpStatus(w http.ResponseWriter, r *http.Request) {
	status := statusString[node.status]
	fmt.Fprintln(w, status)
}

func (node *Node) httpStatusSet(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	state := vars["state"]

	var err error
	switch state {
	case "offline":
		err = node.goOffline()

	case "online":
		err = node.goOnline()

	case "public":
		err = node.goPublic()

	default:
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Bad state: %s\n", state)
		return
	}

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	fmt.Fprintln(w, statusString[node.status])
}

func (node *Node) httpConfigDir(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodHead:
		return
	case http.MethodGet:
		if node.dir != nil {
			fmt.Fprintln(w, mc.FormatHandle(*node.dir))
		} else {
			fmt.Fprintln(w, "nil")
		}
	case http.MethodPost:
		node.httpConfigDirSet(w, r)

	default:
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Unsupported method: %s", r.Method)
	}
}

func (node *Node) httpConfigDirSet(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("http/query: Error reading request body: %s", err.Error())
		return
	}

	handle := strings.TrimSpace(string(body))
	pinfo, err := mc.ParseHandle(handle)

	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Error: %s\n", err.Error())
		return
	}

	node.dir = &pinfo
	fmt.Fprintln(w, "OK")
}
<|endoftext|>


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

package declarative

import (
	"github.com/lxn/walk"
)

type Splitter struct {
	AssignTo           **walk.Splitter
	Name               string
	Disabled           bool
	Hidden             bool
	Font               Font
	MinSize            Size
	MaxSize            Size
	StretchFactor      int
	Row                int
	RowSpan            int
	Column             int
	ColumnSpan         int
	ContextMenuActions []*walk.Action
	Layout             Layout
	Children           []Widget
	HandleWidth        int
	Orientation        Orientation
}

func (s Splitter) Create(parent walk.Container) error {
	w, err := walk.NewSplitter(parent)
	if err != nil {
		return err
	}

	return InitWidget(s, w, func() error {
		if s.HandleWidth > 0 {
			if err := w.SetHandleWidth(s.HandleWidth); err != nil {
				return err
			}
		}
		if err := w.SetOrientation(walk.Orientation(s.Orientation)); err != nil {
			return err
		}

		if s.AssignTo != nil {
			*s.AssignTo = w
		}

		return nil
	})
}

func (s Splitter) WidgetInfo() (name string, disabled, hidden bool, font *Font, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, contextMenuActions []*walk.Action) {
	return s.Name, s.Disabled, s.Hidden, &s.Font, s.MinSize, s.MaxSize, s.StretchFactor, s.Row, s.RowSpan, s.Column, s.ColumnSpan, s.ContextMenuActions
}

func (s Splitter) ContainerInfo() (Layout, []Widget) {
	return s.Layout, s.Children
}
<commit_msg>declarative/Splitter: Remove Layout field<commit_after>// Copyright 2012 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package declarative

import (
	"github.com/lxn/walk"
)

type Splitter struct {
	AssignTo           **walk.Splitter
	Name               string
	Disabled           bool
	Hidden             bool
	Font               Font
	MinSize            Size
	MaxSize            Size
	StretchFactor      int
	Row                int
	RowSpan            int
	Column             int
	ColumnSpan         int
	ContextMenuActions []*walk.Action
	Children           []Widget
	HandleWidth        int
	Orientation        Orientation
}

func (s Splitter) Create(parent walk.Container) error {
	w, err := walk.NewSplitter(parent)
	if err != nil {
		return err
	}

	return InitWidget(s, w, func() error {
		if s.HandleWidth > 0 {
			if err := w.SetHandleWidth(s.HandleWidth); err != nil {
				return err
			}
		}
		if err := w.SetOrientation(walk.Orientation(s.Orientation)); err != nil {
			return err
		}

		if s.AssignTo != nil {
			*s.AssignTo = w
		}

		return nil
	})
}

func (s Splitter) WidgetInfo() (name string, disabled, hidden bool, font *Font, minSize, maxSize Size, stretchFactor, row, rowSpan, column, columnSpan int, contextMenuActions []*walk.Action) {
	return s.Name, s.Disabled, s.Hidden, &s.Font, s.MinSize, s.MaxSize, s.StretchFactor, s.Row, s.RowSpan, s.Column, s.ColumnSpan, s.ContextMenuActions
}

func (s Splitter) ContainerInfo() (Layout, []Widget) {
	return nil, s.Children
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	//"github.com/Zamiell/isaac-racing-server/src/log"

	"github.com/go-sql-driver/mysql"
)

/*
	These are functions used for the listing the schedules for tournaments
	on the website
*/

// TournamentRace holds data for a single race
type TournamentRace struct {
	TournamentName  sql.NullString
	TournamentID    sql.NullString
	TournamentType  sql.NullString
	RaceID          sql.NullInt64
	Racer1          sql.NullString
	Racer2          sql.NullString
	RaceState       sql.NullString
	TournamentRound sql.NullInt64
	RaceDateTime    mysql.NullTime
	ChallongeID     sql.NullInt64
	RaceCaster      sql.NullString
}

// GetTournamentRaces gets all data for all races
func (*Tournament) GetTournamentRaces() ([]TournamentRace, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
				tr.tournament_name,
				tr.challonge_url,
				tr.id,
				trs1.username,
				trs2.username,
				tr.state,
				tr.bracket_round,
				tr.datetime_scheduled,
				tr.challonge_match_id,
				c.username
		FROM
			isaac.tournament_races tr
					LEFT JOIN
							isaac.tournament_racers trs1 ON trs1.id = tr.racer1
					LEFT JOIN
							isaac.tournament_racers trs2 ON trs2.id = tr.racer2
					LEFT JOIN
							isaac.tournament_racers c ON c.id = tr.caster
		WHERE
				tr.state = 'scheduled'
		ORDER BY datetime_scheduled ASC
	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	tournamentRaces := make([]TournamentRace, 0)
	for rows.Next() {
		var race TournamentRace
		if err := rows.Scan(
			&race.TournamentName,
			&race.TournamentID,
			&race.RaceID,
			&race.Racer1,
			&race.Racer2,
			&race.RaceState,
			&race.TournamentRound,
			&race.RaceDateTime,
			&race.ChallongeID,
			&race.RaceCaster,
		); err == sql.ErrNoRows {
			return nil, sql.ErrNoRows
		} else if err != nil {
			return nil, err
		}
		tournamentRaces = append(tournamentRaces, race)
	}
	if len(tournamentRaces) == 0 {
		return nil, sql.ErrNoRows
	}
	return tournamentRaces, nil
}
<commit_msg>Pull races that are scheduled or in progress<commit_after>package models

import (
	"database/sql"
	//"github.com/Zamiell/isaac-racing-server/src/log"

	"github.com/go-sql-driver/mysql"
)

/*
	These are functions used for the listing the schedules for tournaments
	on the website
*/

// TournamentRace holds data for a single race
type TournamentRace struct {
	TournamentName  sql.NullString
	TournamentID    sql.NullString
	TournamentType  sql.NullString
	RaceID          sql.NullInt64
	Racer1          sql.NullString
	Racer2          sql.NullString
	RaceState       sql.NullString
	TournamentRound sql.NullInt64
	RaceDateTime    mysql.NullTime
	ChallongeID     sql.NullInt64
	RaceCaster      sql.NullString
}

// GetTournamentRaces gets all data for all races
func (*Tournament) GetTournamentRaces() ([]TournamentRace, error) {
	var rows *sql.Rows
	if v, err := db.Query(`
		SELECT
				tr.tournament_name,
				tr.challonge_url,
				tr.id,
				trs1.username,
				trs2.username,
				tr.state,
				tr.bracket_round,
				tr.datetime_scheduled,
				tr.challonge_match_id,
				c.username
		FROM
			isaac.tournament_races tr
					LEFT JOIN
							isaac.tournament_racers trs1 ON trs1.id = tr.racer1
					LEFT JOIN
							isaac.tournament_racers trs2 ON trs2.id = tr.racer2
					LEFT JOIN
							isaac.tournament_racers c ON c.id = tr.caster
		WHERE
				tr.state in ('scheduled', 'inProgress')
		ORDER BY datetime_scheduled ASC
	`); err != nil {
		return nil, err
	} else {
		rows = v
	}
	defer rows.Close()

	tournamentRaces := make([]TournamentRace, 0)
	for rows.Next() {
		var race TournamentRace
		if err := rows.Scan(
			&race.TournamentName,
			&race.TournamentID,
			&race.RaceID,
			&race.Racer1,
			&race.Racer2,
			&race.RaceState,
			&race.TournamentRound,
			&race.RaceDateTime,
			&race.ChallongeID,
			&race.RaceCaster,
		); err == sql.ErrNoRows {
			return nil, sql.ErrNoRows
		} else if err != nil {
			return nil, err
		}
		tournamentRaces = append(tournamentRaces, race)
	}
	if len(tournamentRaces) == 0 {
		return nil, sql.ErrNoRows
	}
	return tournamentRaces, nil
}
<|endoftext|>


<commit_before>package stage

import (
	"errors"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_auth"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_conn"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_context"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_error"
	"github.com/watermint/toolbox/domain/dropbox/model/mo_path"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_file"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_file_folder"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_group"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_group_member"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_profile"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_sharedfolder"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_sharedfolder_member"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_teamfolder"
	"github.com/watermint/toolbox/essentials/log/esl"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/recipe/rc_exec"
	"github.com/watermint/toolbox/infra/recipe/rc_recipe"
	"strings"
)

type Teamfolder struct {
	rc_recipe.RemarkSecret
	rc_recipe.RemarkExperimental
	Peer dbx_conn.ConnScopedTeam
}

func (z *Teamfolder) Preset() {
	z.Peer.SetScopes(
		dbx_auth.ScopeFilesContentRead,
		dbx_auth.ScopeFilesContentWrite,
		dbx_auth.ScopeGroupsWrite,
		dbx_auth.ScopeSharingRead,
		dbx_auth.ScopeSharingWrite,
		dbx_auth.ScopeTeamDataMember,
		dbx_auth.ScopeTeamDataTeamSpace,
		dbx_auth.ScopeTeamInfoRead,
	)
}

func (z *Teamfolder) Exec(c app_control.Control) error {
	teamFolderName := "Tokyo Branch"
	nestedFolderPlainName := "Organization"
	nestedFolderSharedName := "Sales"
	nestedFolderRestrictedName := "Report"
	adminGroupName := "toolbox-admin"
	sampleGroupName := "toolbox-sample"

	// [Tokyo Branch] (Team folder, [editor=toolbox-admin])
	//  |
	//  +-- [Organization] (plain folder, not_synced)
	//  |
	//  +-- [Sales] (nested folder, not_synced)
	//       |
	//       +-- [Report] (nested folder, do not inherit, no external sharing, [editor=toolbox-sample])

	l := c.Log()

	// find admin
	admin, err := sv_profile.NewTeam(z.Peer.Context()).Admin()
	if err != nil {
		return err
	}

	// create team folder
	tf, err := sv_teamfolder.New(z.Peer.Context()).Create(teamFolderName)
	de := dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("teamfolder", tf))
		break

	case de.IsFolderNameAlreadyUsed():
		l.Info("The folder already created")
		teamfolders, err := sv_teamfolder.New(z.Peer.Context()).List()
		if err != nil {
			l.Warn("Unable to retrieve team folder list", esl.Error(err))
			return err
		}

		for _, teamfolder := range teamfolders {
			if strings.ToLower(teamfolder.Name) == strings.ToLower(teamFolderName) {
				tf = teamfolder
				break
			}
		}
		if tf == nil {
			l.Warn("Team folder not found")
			return errors.New("team folder not found")
		}

		break

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	tfCtx := z.Peer.Context().AsAdminId(admin.TeamMemberId).WithPath(dbx_context.Namespace(tf.TeamFolderId))

	// create sub folder : Organization
	folderOrganization, err := sv_file_folder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + nestedFolderPlainName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderOrganization))
		break

	case de.Path().IsConflict():
		l.Info("The folder already created")
		folderOrganization, err = sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderPlainName))
		if err != nil {
			l.Warn("Unable to identify sub folder", esl.Error(err))
			return err
		}
		break

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	// create nested folder : Sales
	folderSales, err := sv_sharedfolder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + nestedFolderSharedName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderSales))
		break

	case de.BadPath().IsAlreadyShared():
		l.Info("The folder is already shared")
		folderSalesMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderSharedName))
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}

		folderSales, err = sv_sharedfolder.New(tfCtx).Resolve(folderSalesMeta.Concrete().SharedFolderId)
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}
		l.Info("Nested folder resolved", esl.Any("folder", folderSales))

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	// create nested folder : Sales
	folderSalesReport, err := sv_sharedfolder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + nestedFolderSharedName + "/" + nestedFolderRestrictedName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderSalesReport))
		break

	case de.BadPath().IsAlreadyShared():
		l.Info("The folder is already shared")
		folderSalesReportMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderSharedName + "/" + nestedFolderRestrictedName))
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}

		folderSalesReport, err = sv_sharedfolder.New(tfCtx).Resolve(folderSalesReportMeta.Concrete().SharedFolderId)
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}
		l.Info("Nested folder resolved", esl.Any("folder", folderSales))

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	// Change sync setting
	folderOrganizationMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderPlainName))
	if err != nil {
		l.Warn("Unable to find meta", esl.Error(err))
		return err
	}
	folderSalesMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderSharedName))
	if err != nil {
		l.Warn("Unable to find meta", esl.Error(err))
		return err
	}

	updated, err := sv_teamfolder.New(z.Peer.Context()).UpdateSyncSetting(tf,
		sv_teamfolder.AddNestedSetting(folderOrganizationMeta, sv_teamfolder.SyncSettingNotSynced),
		sv_teamfolder.AddNestedSetting(folderSalesMeta, sv_teamfolder.SyncSettingNotSynced),
	)
	if err != nil {
		l.Warn("Unable to change", esl.Error(err))
		return err
	}
	l.Info("Sync settings updated", esl.Any("updated", updated))

	// Create toolbox admin group
	adminGroup, err := sv_group.New(z.Peer.Context()).Create(
		adminGroupName,
		sv_group.CompanyManaged(),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The admin group created", esl.Any("group", adminGroup))

	case de.IsGroupNameAlreadyUsed():
		l.Info("The admin group already created")
		adminGroup, err = sv_group.New(z.Peer.Context()).ResolveByName(adminGroupName)
		if err != nil {
			l.Warn("Unable to find the admin group", esl.Error(err))
			return err
		}

	default:
		l.Warn("Unable to create the admin group", esl.Error(err))
		return err
	}

	// Add the admin to the admin group
	updatedAdminGroup, err := sv_group_member.NewByGroupId(z.Peer.Context(), adminGroup.GroupId).Add(
		sv_group_member.ByTeamMemberId(admin.TeamMemberId),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The admin successfully added to the admin group", esl.Any("group", updatedAdminGroup))

	case de.IsDuplicateUser():
		l.Info("The admin is already added to the admin group", esl.Any("group", updatedAdminGroup))

	default:
		l.Warn("Unable to add member", esl.Error(err))
		return err
	}

	// Create toolbox sample group
	sampleGroup, err := sv_group.New(z.Peer.Context()).Create(
		sampleGroupName,
		sv_group.UserManaged(),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The sample group created", esl.Any("group", sampleGroup))

	case de.IsGroupNameAlreadyUsed():
		l.Info("The sample group already created")
		sampleGroup, err = sv_group.New(z.Peer.Context()).ResolveByName(sampleGroupName)
		if err != nil {
			l.Warn("Unable to find the sample group", esl.Error(err))
			return err
		}

	default:
		l.Warn("Unable to create the sample group", esl.Error(err))
		return err
	}

	// Add admin group to the team folder
	err = sv_sharedfolder_member.NewByTeamFolder(z.Peer.Context().AsAdminId(admin.TeamMemberId), tf).Add(
		sv_sharedfolder_member.AddByGroup(adminGroup, "editor"),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The admin group added to the team folder as editor")

	default:
		l.Warn("Unable to update members", esl.Error(err))
	}

	// Do not inherit permission from parent : Sales/Report
	updatedFolderSalesReport, err := sv_sharedfolder.New(z.Peer.Context().AsMemberId(admin.TeamMemberId)).UpdateInheritance(folderSalesReport.SharedFolderId, sv_sharedfolder.AccessInheritanceNoInherit)
	if err != nil {
		l.Warn("Unable to change", esl.Error(err))
		return err
	}
	l.Info("Sync access inheritance updated", esl.Any("updated", updatedFolderSalesReport))

	// Add sample group to the nested folder
	err = sv_sharedfolder_member.NewBySharedFolderId(z.Peer.Context().AsAdminId(admin.TeamMemberId), folderSalesReport.SharedFolderId).Add(
		sv_sharedfolder_member.AddByGroup(sampleGroup, "editor"),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The sample group added to the team folder as editor")

	default:
		l.Warn("Unable to update members", esl.Error(err))
	}

	// Change folder policy : Sales
	updatedSalesPolicy, err := sv_sharedfolder.New(z.Peer.Context().AsAdminId(admin.TeamMemberId)).UpdatePolicy(
		folderSales.SharedFolderId,
		sv_sharedfolder.MemberPolicy("team"),
		sv_sharedfolder.AclUpdatePolicy("owner"),
		sv_sharedfolder.SharedLinkPolicy("team"),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The sales folder policy successfully updated", esl.Any("updated", updatedSalesPolicy))

	default:
		l.Warn("Unable to update policies", esl.Error(err))
	}

	return nil
}

func (z *Teamfolder) Test(c app_control.Control) error {
	return rc_exec.ExecMock(c, &Teamfolder{}, rc_recipe.NoCustomValues)
}
<commit_msg>#303 : additional poc<commit_after>package stage

import (
	"errors"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_auth"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_conn"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_context"
	"github.com/watermint/toolbox/domain/dropbox/api/dbx_error"
	"github.com/watermint/toolbox/domain/dropbox/model/mo_path"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_file"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_file_folder"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_group"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_group_member"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_profile"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_sharedfolder"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_sharedfolder_member"
	"github.com/watermint/toolbox/domain/dropbox/service/sv_teamfolder"
	"github.com/watermint/toolbox/essentials/log/esl"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/recipe/rc_exec"
	"github.com/watermint/toolbox/infra/recipe/rc_recipe"
	"strings"
)

type Teamfolder struct {
	rc_recipe.RemarkSecret
	rc_recipe.RemarkExperimental
	Peer dbx_conn.ConnScopedTeam
}

func (z *Teamfolder) Preset() {
	z.Peer.SetScopes(
		dbx_auth.ScopeFilesContentRead,
		dbx_auth.ScopeFilesContentWrite,
		dbx_auth.ScopeGroupsWrite,
		dbx_auth.ScopeSharingRead,
		dbx_auth.ScopeSharingWrite,
		dbx_auth.ScopeTeamDataMember,
		dbx_auth.ScopeTeamDataTeamSpace,
		dbx_auth.ScopeTeamInfoRead,
	)
}

func (z *Teamfolder) Exec(c app_control.Control) error {
	teamFolderName := "Tokyo Branch 4"
	nestedFolderPlainName := "Organization"
	nestedFolderSharedName := "Sales"
	nestedFolderRestrictedName := "Report"
	restedFolderRestrictedNoSyncName := "Finance"
	adminGroupName := "toolbox-admin"
	sampleGroupName := "toolbox-sample"

	// [Tokyo Branch] (Team folder, [editor=toolbox-admin])
	//  |
	//  +-- [Organization] (plain folder, not_synced)
	//  |
	//  +-- [Sales] (nested folder, not_synced)
	//  |    |
	//  |    +-- [Report] (nested folder, do not inherit, no external sharing, [editor=toolbox-sample])
	//  |
	//  +-- [Finance] (nested folder, not_synced, do not inherit)

	l := c.Log()

	// find admin
	admin, err := sv_profile.NewTeam(z.Peer.Context()).Admin()
	if err != nil {
		return err
	}

	// create team folder
	tf, err := sv_teamfolder.New(z.Peer.Context()).Create(teamFolderName)
	de := dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("teamfolder", tf))
		break

	case de.IsFolderNameAlreadyUsed():
		l.Info("The folder already created")
		teamfolders, err := sv_teamfolder.New(z.Peer.Context()).List()
		if err != nil {
			l.Warn("Unable to retrieve team folder list", esl.Error(err))
			return err
		}

		for _, teamfolder := range teamfolders {
			if strings.ToLower(teamfolder.Name) == strings.ToLower(teamFolderName) {
				tf = teamfolder
				break
			}
		}
		if tf == nil {
			l.Warn("Team folder not found")
			return errors.New("team folder not found")
		}

		break

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	tfCtx := z.Peer.Context().AsAdminId(admin.TeamMemberId).WithPath(dbx_context.Namespace(tf.TeamFolderId))

	// create sub folder : Organization
	folderOrganization, err := sv_file_folder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + nestedFolderPlainName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderOrganization))
		break

	case de.Path().IsConflict():
		l.Info("The folder already created")
		folderOrganization, err = sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderPlainName))
		if err != nil {
			l.Warn("Unable to identify sub folder", esl.Error(err))
			return err
		}
		break

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	// create nested folder : Sales
	folderSales, err := sv_sharedfolder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + nestedFolderSharedName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderSales))
		break

	case de.BadPath().IsAlreadyShared():
		l.Info("The folder is already shared")
		folderSalesMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderSharedName))
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}

		folderSales, err = sv_sharedfolder.New(tfCtx).Resolve(folderSalesMeta.Concrete().SharedFolderId)
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}
		l.Info("Nested folder resolved", esl.Any("folder", folderSales))

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	// create nested folder : Sales
	folderSalesReport, err := sv_sharedfolder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + nestedFolderSharedName + "/" + nestedFolderRestrictedName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderSalesReport))
		break

	case de.BadPath().IsAlreadyShared():
		l.Info("The folder is already shared")
		folderSalesReportMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderSharedName + "/" + nestedFolderRestrictedName))
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}

		folderSalesReport, err = sv_sharedfolder.New(tfCtx).Resolve(folderSalesReportMeta.Concrete().SharedFolderId)
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}
		l.Info("Nested folder resolved", esl.Any("folder", folderSales))

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	// Change sync setting
	folderOrganizationMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderPlainName))
	if err != nil {
		l.Warn("Unable to find meta", esl.Error(err))
		return err
	}
	folderSalesMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + nestedFolderSharedName))
	if err != nil {
		l.Warn("Unable to find meta", esl.Error(err))
		return err
	}

	updated, err := sv_teamfolder.New(z.Peer.Context()).UpdateSyncSetting(tf,
		sv_teamfolder.AddNestedSetting(folderOrganizationMeta, sv_teamfolder.SyncSettingNotSynced),
		sv_teamfolder.AddNestedSetting(folderSalesMeta, sv_teamfolder.SyncSettingNotSynced),
	)
	if err != nil {
		l.Warn("Unable to change : sync setting", esl.Error(err))
		return err
	}
	l.Info("Sync settings updated", esl.Any("updated", updated))

	// Create toolbox admin group
	adminGroup, err := sv_group.New(z.Peer.Context()).Create(
		adminGroupName,
		sv_group.CompanyManaged(),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The admin group created", esl.Any("group", adminGroup))

	case de.IsGroupNameAlreadyUsed():
		l.Info("The admin group already created")
		adminGroup, err = sv_group.New(z.Peer.Context()).ResolveByName(adminGroupName)
		if err != nil {
			l.Warn("Unable to find the admin group", esl.Error(err))
			return err
		}

	default:
		l.Warn("Unable to create the admin group", esl.Error(err))
		return err
	}

	// Add the admin to the admin group
	updatedAdminGroup, err := sv_group_member.NewByGroupId(z.Peer.Context(), adminGroup.GroupId).Add(
		sv_group_member.ByTeamMemberId(admin.TeamMemberId),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The admin successfully added to the admin group", esl.Any("group", updatedAdminGroup))

	case de.IsDuplicateUser():
		l.Info("The admin is already added to the admin group", esl.Any("group", updatedAdminGroup))

	default:
		l.Warn("Unable to add member", esl.Error(err))
		return err
	}

	// Create toolbox sample group
	sampleGroup, err := sv_group.New(z.Peer.Context()).Create(
		sampleGroupName,
		sv_group.UserManaged(),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The sample group created", esl.Any("group", sampleGroup))

	case de.IsGroupNameAlreadyUsed():
		l.Info("The sample group already created")
		sampleGroup, err = sv_group.New(z.Peer.Context()).ResolveByName(sampleGroupName)
		if err != nil {
			l.Warn("Unable to find the sample group", esl.Error(err))
			return err
		}

	default:
		l.Warn("Unable to create the sample group", esl.Error(err))
		return err
	}

	// Add admin group to the team folder
	err = sv_sharedfolder_member.NewByTeamFolder(z.Peer.Context().AsAdminId(admin.TeamMemberId), tf).Add(
		sv_sharedfolder_member.AddByGroup(adminGroup, "editor"),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The admin group added to the team folder as editor")

	default:
		l.Warn("Unable to update members", esl.Error(err))
	}

	// Do not inherit permission from parent : Sales/Report
	updatedFolderSalesReport, err := sv_sharedfolder.New(z.Peer.Context().AsMemberId(admin.TeamMemberId)).UpdateInheritance(folderSalesReport.SharedFolderId, sv_sharedfolder.AccessInheritanceNoInherit)
	if err != nil {
		l.Warn("Unable to change: inherit", esl.Error(err))
		return err
	}
	l.Info("Sync access inheritance updated", esl.Any("updated", updatedFolderSalesReport))

	// Add sample group to the nested folder
	err = sv_sharedfolder_member.NewBySharedFolderId(z.Peer.Context().AsAdminId(admin.TeamMemberId), folderSalesReport.SharedFolderId).Add(
		sv_sharedfolder_member.AddByGroup(sampleGroup, "editor"),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The sample group added to the team folder as editor")

	default:
		l.Warn("Unable to update members", esl.Error(err))
	}

	// Change folder policy : Sales
	updatedSalesPolicy, err := sv_sharedfolder.New(z.Peer.Context().AsAdminId(admin.TeamMemberId)).UpdatePolicy(
		folderSales.SharedFolderId,
		sv_sharedfolder.MemberPolicy("team"),
		sv_sharedfolder.AclUpdatePolicy("owner"),
		sv_sharedfolder.SharedLinkPolicy("team"),
	)
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("The sales folder policy successfully updated", esl.Any("updated", updatedSalesPolicy))

	default:
		l.Warn("Unable to update policies", esl.Error(err))
	}

	// Restricted & no sync
	// Apply no sync to Finance: 1. create folder
	folderFinance, err := sv_sharedfolder.New(tfCtx).Create(mo_path.NewDropboxPath("/" + restedFolderRestrictedNoSyncName))
	de = dbx_error.NewErrors(err)
	switch {
	case de == nil:
		l.Info("Team folder created", esl.Any("folder", folderFinance))
		break

	case de.BadPath().IsAlreadyShared():
		l.Info("The folder is already shared")
		folderFinanceMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + restedFolderRestrictedNoSyncName))
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}

		folderFinance, err = sv_sharedfolder.New(tfCtx).Resolve(folderFinanceMeta.Concrete().SharedFolderId)
		if err != nil {
			l.Warn("Unable to resolve nested folder", esl.Error(err))
			return err
		}
		l.Info("Nested folder resolved", esl.Any("folder", folderSales))

	default:
		l.Warn("Unable to create team folder", esl.Error(err))
		return err
	}

	folderFinanceMeta, err := sv_file.NewFiles(tfCtx).Resolve(mo_path.NewDropboxPath("/" + restedFolderRestrictedNoSyncName))
	if err != nil {
		l.Warn("Unable to find meta", esl.Error(err))
		return err
	}

	// 2. set un-sync
	updatedFinance, err := sv_teamfolder.New(z.Peer.Context()).UpdateSyncSetting(tf,
		sv_teamfolder.AddNestedSetting(folderOrganizationMeta, sv_teamfolder.SyncSettingNotSynced),
		sv_teamfolder.AddNestedSetting(folderFinanceMeta, sv_teamfolder.SyncSettingNotSynced),
	)
	if err != nil {
		l.Warn("Unable to change", esl.Error(err))
		return err
	}
	l.Info("Sync settings updated", esl.Any("updated", updatedFinance))

	// 3. set no_inherit
	updatedFinanceInherit, err := sv_sharedfolder.New(z.Peer.Context().AsMemberId(admin.TeamMemberId)).UpdateInheritance(folderFinance.SharedFolderId, sv_sharedfolder.AccessInheritanceNoInherit)
	if err != nil {
		l.Warn("Unable to change: inherit", esl.Error(err))
		return err
	}
	l.Info("Sync access inheritance updated", esl.Any("updated", updatedFinanceInherit))

	return nil
}

func (z *Teamfolder) Test(c app_control.Control) error {
	return rc_exec.ExecMock(c, &Teamfolder{}, rc_recipe.NoCustomValues)
}
<|endoftext|>


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

package gcscaching_test

import (
	"testing"
	"time"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	. "github.com/jacobsa/ogletest"
)

func TestStatCache(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking cache
////////////////////////////////////////////////////////////////////////

type invariantsCache struct {
	wrapped gcscaching.StatCache
}

func (c *invariantsCache) Insert(
	o *gcs.Object,
	expiration time.Time) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Insert(o, expiration)
	return
}

func (c *invariantsCache) Erase(name string) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Erase(name)
	return
}

func (c *invariantsCache) LookUp(
	name string,
	now time.Time) (hit bool, o *gcs.Object) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	hit, o = c.wrapped.LookUp(name, now)
	return
}

func (c *invariantsCache) Hit(
	name string,
	now time.Time) (hit bool) {
	hit, _ = c.LookUp(name, now)
	return
}

func (c *invariantsCache) LookUpOrNil(
	name string,
	now time.Time) (o *gcs.Object) {
	_, o = c.LookUp(name, now)
	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

const capacity = 3

var someTime = time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local)
var expiration = someTime.Add(time.Second)

type StatCacheTest struct {
	cache invariantsCache
}

func init() { RegisterTestSuite(&StatCacheTest{}) }

func (t *StatCacheTest) SetUp(ti *TestInfo) {
	t.cache.wrapped = gcscaching.NewStatCache(capacity)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *StatCacheTest) LookUpInEmptyCache() {
	ExpectFalse(t.cache.Hit("", someTime))
	ExpectFalse(t.cache.Hit("taco", someTime))
}

func (t *StatCacheTest) LookUpUnknownKey() {
	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}

	t.cache.Insert(o0, someTime.Add(time.Second))
	t.cache.Insert(o1, someTime.Add(time.Second))

	ExpectFalse(t.cache.Hit("", someTime))
	ExpectFalse(t.cache.Hit("enchilada", someTime))
}

func (t *StatCacheTest) KeysPresentButEverythingIsExpired() {
	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}

	t.cache.Insert(o0, someTime.Add(-time.Second))
	t.cache.Insert(o1, someTime.Add(-time.Second))

	ExpectFalse(t.cache.Hit("burrito", someTime))
	ExpectFalse(t.cache.Hit("taco", someTime))
}

func (t *StatCacheTest) FillUpToCapacity() {
	AssertEq(3, capacity)

	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}
	o2 := &gcs.Object{Name: "enchilada"}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)
	t.cache.Insert(o2, expiration)

	// Before expiration
	justBefore := expiration.Add(-time.Nanosecond)
	ExpectEq(o0, t.cache.LookUpOrNil("burrito", justBefore))
	ExpectEq(o1, t.cache.LookUpOrNil("taco", justBefore))
	ExpectEq(o2, t.cache.LookUpOrNil("enchilada", justBefore))

	// At expiration
	ExpectEq(o0, t.cache.LookUpOrNil("burrito", expiration))
	ExpectEq(o1, t.cache.LookUpOrNil("taco", expiration))
	ExpectEq(o2, t.cache.LookUpOrNil("enchilada", expiration))

	// After expiration
	justAfter := expiration.Add(time.Nanosecond)
	ExpectFalse(t.cache.Hit("burrito", justAfter))
	ExpectFalse(t.cache.Hit("taco", justAfter))
	ExpectFalse(t.cache.Hit("enchilada", justAfter))
}

func (t *StatCacheTest) ExpiresLeastRecentlyUsed() {
	AssertEq(3, capacity)

	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}
	o2 := &gcs.Object{Name: "enchilada"}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)                         // Least recent
	t.cache.Insert(o2, expiration)                         // Second most recent
	AssertEq(o0, t.cache.LookUpOrNil("burrito", someTime)) // Most recent

	// Insert another.
	o3 := &gcs.Object{Name: "queso"}
	t.cache.Insert(o3, expiration)

	// See what's left.
	ExpectFalse(t.cache.Hit("taco", someTime))
	ExpectEq(o0, t.cache.LookUpOrNil("burrito", someTime))
	ExpectEq(o2, t.cache.LookUpOrNil("enchilada", someTime))
	ExpectEq(o3, t.cache.LookUpOrNil("queso", someTime))
}

func (t *StatCacheTest) Overwrite_NewerGeneration() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 19, MetaGeneration: 1}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o1, t.cache.LookUpOrNil("taco", someTime))

	// The overwritten entry shouldn't count toward capacity.
	AssertEq(3, capacity)

	t.cache.Insert(&gcs.Object{Name: "burrito"}, expiration)
	t.cache.Insert(&gcs.Object{Name: "enchilada"}, expiration)

	ExpectNe(nil, t.cache.LookUpOrNil("taco", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("burrito", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("enchilada", someTime))
}

func (t *StatCacheTest) Overwrite_SameGeneration_NewerMetadataGen() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 7}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o1, t.cache.LookUpOrNil("taco", someTime))

	// The overwritten entry shouldn't count toward capacity.
	AssertEq(3, capacity)

	t.cache.Insert(&gcs.Object{Name: "burrito"}, expiration)
	t.cache.Insert(&gcs.Object{Name: "enchilada"}, expiration)

	ExpectNe(nil, t.cache.LookUpOrNil("taco", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("burrito", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("enchilada", someTime))
}

func (t *StatCacheTest) Overwrite_SameGeneration_SameMetadataGen() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o1, t.cache.LookUpOrNil("taco", someTime))
}

func (t *StatCacheTest) Overwrite_SameGeneration_OlderMetadataGen() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 3}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o0, t.cache.LookUpOrNil("taco", someTime))
}

func (t *StatCacheTest) Overwrite_OlderGeneration() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 13, MetaGeneration: 7}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o0, t.cache.LookUpOrNil("taco", someTime))
}
<commit_msg>Improved StatCacheTest and added more test names.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcscaching_test

import (
	"testing"
	"time"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	. "github.com/jacobsa/ogletest"
)

func TestStatCache(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking cache
////////////////////////////////////////////////////////////////////////

type invariantsCache struct {
	wrapped gcscaching.StatCache
}

func (c *invariantsCache) Insert(
	o *gcs.Object,
	expiration time.Time) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Insert(o, expiration)
	return
}

func (c *invariantsCache) AddNegativeEntry(
	name string,
	expiration time.Time) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.AddNegativeEntry(name, expiration)
	return
}

func (c *invariantsCache) Erase(name string) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	c.wrapped.Erase(name)
	return
}

func (c *invariantsCache) LookUp(
	name string,
	now time.Time) (hit bool, o *gcs.Object) {
	c.wrapped.CheckInvariants()
	defer c.wrapped.CheckInvariants()

	hit, o = c.wrapped.LookUp(name, now)
	return
}

func (c *invariantsCache) LookUpOrNil(
	name string,
	now time.Time) (o *gcs.Object) {
	_, o = c.LookUp(name, now)
	return
}

func (c *invariantsCache) Hit(
	name string,
	now time.Time) (hit bool) {
	hit, _ = c.LookUp(name, now)
	return
}

func (c *invariantsCache) NegativeEntry(
	name string,
	now time.Time) (negative bool) {
	hit, o := c.LookUp(name, now)
	negative = hit && o == nil
	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

const capacity = 3

var someTime = time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local)
var expiration = someTime.Add(time.Second)

type StatCacheTest struct {
	cache invariantsCache
}

func init() { RegisterTestSuite(&StatCacheTest{}) }

func (t *StatCacheTest) SetUp(ti *TestInfo) {
	t.cache.wrapped = gcscaching.NewStatCache(capacity)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *StatCacheTest) LookUpInEmptyCache() {
	ExpectFalse(t.cache.Hit("", someTime))
	ExpectFalse(t.cache.Hit("taco", someTime))
}

func (t *StatCacheTest) LookUpUnknownKey() {
	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}

	t.cache.Insert(o0, someTime.Add(time.Second))
	t.cache.Insert(o1, someTime.Add(time.Second))

	ExpectFalse(t.cache.Hit("", someTime))
	ExpectFalse(t.cache.Hit("enchilada", someTime))
}

func (t *StatCacheTest) KeysPresentButEverythingIsExpired() {
	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}

	t.cache.Insert(o0, someTime.Add(-time.Second))
	t.cache.Insert(o1, someTime.Add(-time.Second))

	ExpectFalse(t.cache.Hit("burrito", someTime))
	ExpectFalse(t.cache.Hit("taco", someTime))
}

func (t *StatCacheTest) FillUpToCapacity() {
	AssertEq(3, capacity)

	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)
	t.cache.AddNegativeEntry("enchilada", expiration)

	// Before expiration
	justBefore := expiration.Add(-time.Nanosecond)
	ExpectEq(o0, t.cache.LookUpOrNil("burrito", justBefore))
	ExpectEq(o1, t.cache.LookUpOrNil("taco", justBefore))
	ExpectTrue(t.cache.NegativeEntry("enchilada", justBefore))

	// At expiration
	ExpectEq(o0, t.cache.LookUpOrNil("burrito", expiration))
	ExpectEq(o1, t.cache.LookUpOrNil("taco", expiration))
	ExpectTrue(t.cache.NegativeEntry("enchilada", justBefore))

	// After expiration
	justAfter := expiration.Add(time.Nanosecond)
	ExpectFalse(t.cache.Hit("burrito", justAfter))
	ExpectFalse(t.cache.Hit("taco", justAfter))
	ExpectFalse(t.cache.Hit("enchilada", justAfter))
}

func (t *StatCacheTest) ExpiresLeastRecentlyUsed() {
	AssertEq(3, capacity)

	o0 := &gcs.Object{Name: "burrito"}
	o1 := &gcs.Object{Name: "taco"}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)                         // Least recent
	t.cache.AddNegativeEntry("enchilada", expiration)      // Second most recent
	AssertEq(o0, t.cache.LookUpOrNil("burrito", someTime)) // Most recent

	// Insert another.
	o3 := &gcs.Object{Name: "queso"}
	t.cache.Insert(o3, expiration)

	// See what's left.
	ExpectFalse(t.cache.Hit("taco", someTime))
	ExpectEq(o0, t.cache.LookUpOrNil("burrito", someTime))
	ExpectTrue(t.cache.NegativeEntry("enchilada", someTime))
	ExpectEq(o3, t.cache.LookUpOrNil("queso", someTime))
}

func (t *StatCacheTest) Overwrite_NewerGeneration() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 19, MetaGeneration: 1}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o1, t.cache.LookUpOrNil("taco", someTime))

	// The overwritten entry shouldn't count toward capacity.
	AssertEq(3, capacity)

	t.cache.Insert(&gcs.Object{Name: "burrito"}, expiration)
	t.cache.Insert(&gcs.Object{Name: "enchilada"}, expiration)

	ExpectNe(nil, t.cache.LookUpOrNil("taco", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("burrito", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("enchilada", someTime))
}

func (t *StatCacheTest) Overwrite_SameGeneration_NewerMetadataGen() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 7}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o1, t.cache.LookUpOrNil("taco", someTime))

	// The overwritten entry shouldn't count toward capacity.
	AssertEq(3, capacity)

	t.cache.Insert(&gcs.Object{Name: "burrito"}, expiration)
	t.cache.Insert(&gcs.Object{Name: "enchilada"}, expiration)

	ExpectNe(nil, t.cache.LookUpOrNil("taco", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("burrito", someTime))
	ExpectNe(nil, t.cache.LookUpOrNil("enchilada", someTime))
}

func (t *StatCacheTest) Overwrite_SameGeneration_SameMetadataGen() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o1, t.cache.LookUpOrNil("taco", someTime))
}

func (t *StatCacheTest) Overwrite_SameGeneration_OlderMetadataGen() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 3}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o0, t.cache.LookUpOrNil("taco", someTime))
}

func (t *StatCacheTest) Overwrite_OlderGeneration() {
	o0 := &gcs.Object{Name: "taco", Generation: 17, MetaGeneration: 5}
	o1 := &gcs.Object{Name: "taco", Generation: 13, MetaGeneration: 7}

	t.cache.Insert(o0, expiration)
	t.cache.Insert(o1, expiration)

	ExpectEq(o0, t.cache.LookUpOrNil("taco", someTime))
}

func (t *StatCacheTest) Overwrite_NegativeWithPositive() {
	AssertTrue(false, "TODO")
}

func (t *StatCacheTest) Overwrite_PositiveWithNegative() {
	AssertTrue(false, "TODO")
}

func (t *StatCacheTest) Overwrite_NegativeWithNegative() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package apns

import (
	"errors"
	"github.com/sideshow/apns2"
	"github.com/sideshow/apns2/payload"
	"github.com/smancke/guble/server/connector"
	"strings"
)

const (
	// deviceIDKey is the key name set on the route params to identify the application
	deviceIDKey = "device_token"
	userIDKey   = "user_id"
)

var (
	errPusherInvalidParams = errors.New("Invalid parameters of APNS Pusher")
)

type sender struct {
	client   Pusher
	appTopic string
}

func NewSender(config Config) (connector.Sender, error) {
	pusher, err := newPusher(config)
	if err != nil {
		logger.WithError(err).Error("APNS Pusher creation error")
		return nil, err
	}
	return NewSenderUsingPusher(pusher, *config.AppTopic)
}

func NewSenderUsingPusher(pusher Pusher, appTopic string) (connector.Sender, error) {
	if pusher == nil || appTopic == "" {
		return nil, errPusherInvalidParams
	}
	return &sender{
		client:   pusher,
		appTopic: appTopic,
	}, nil
}

func (s sender) Send(request connector.Request) (interface{}, error) {
	route := request.Subscriber().Route()

	//TODO Cosmin: Samsa should generate the Payload or the whole Notification, and JSON-serialize it into the guble-message Body.

	//m := request.Message()
	//n := &apns2.Notification{
	//	Priority:    apns2.PriorityHigh,
	//	Topic:       strings.TrimPrefix(string(s.route.Path), "/"),
	//	DeviceToken: s.route.Get(applicationIDKey),
	//	Payload:     m.Body,
	//}

	topic := strings.TrimPrefix(string(route.Path), "/")
	n := &apns2.Notification{
		Priority:    apns2.PriorityHigh,
		Topic:       s.appTopic,
		DeviceToken: route.Get(deviceIDKey),
		Payload: payload.NewPayload().
			AlertTitle("Title").
			AlertBody("Body").
			Custom("topic", topic).
			Badge(1).
			ContentAvailable(),
	}
	logger.Debug("Trying to push a message to APNS")
	return s.client.Push(n)
}
<commit_msg>apns: using the message body as the payload (should be precomputed)<commit_after>package apns

import (
	"errors"
	"github.com/sideshow/apns2"
	"github.com/smancke/guble/server/connector"
)

const (
	// deviceIDKey is the key name set on the route params to identify the application
	deviceIDKey = "device_token"
	userIDKey   = "user_id"
)

var (
	errPusherInvalidParams = errors.New("Invalid parameters of APNS Pusher")
)

type sender struct {
	client   Pusher
	appTopic string
}

func NewSender(config Config) (connector.Sender, error) {
	pusher, err := newPusher(config)
	if err != nil {
		logger.WithError(err).Error("APNS Pusher creation error")
		return nil, err
	}
	return NewSenderUsingPusher(pusher, *config.AppTopic)
}

func NewSenderUsingPusher(pusher Pusher, appTopic string) (connector.Sender, error) {
	if pusher == nil || appTopic == "" {
		return nil, errPusherInvalidParams
	}
	return &sender{
		client:   pusher,
		appTopic: appTopic,
	}, nil
}

func (s sender) Send(request connector.Request) (interface{}, error) {
	route := request.Subscriber().Route()

	n := &apns2.Notification{
		Priority:    apns2.PriorityHigh,
		Topic:       s.appTopic,
		DeviceToken: route.Get(deviceIDKey),
		Payload:     request.Message().Body,
	}

	//TODO Cosmin: remove old code below

	//topic := strings.TrimPrefix(string(route.Path), "/")
	//n := &apns2.Notification{
	//	Priority:    apns2.PriorityHigh,
	//	Topic:       s.appTopic,
	//	DeviceToken: route.Get(deviceIDKey),
	//	Payload: payload.NewPayload().
	//		AlertTitle("Title").
	//		AlertBody("Body").
	//		Custom("topic", topic).
	//		Badge(1).
	//		ContentAvailable(),
	//}

	logger.Debug("Trying to push a message to APNS")
	return s.client.Push(n)
}
<|endoftext|>